diff options
author | Boutade <thegoofist@protonmail.com> | 2019-05-01 11:03:17 -0500 |
---|---|---|
committer | Boutade <thegoofist@protonmail.com> | 2019-05-01 11:03:17 -0500 |
commit | c591dafbe1d3378784a0dc3d06358183250b9314 (patch) | |
tree | dc8f1c071d847ead69a3f414a7d6ad7d1ab96180 | |
parent | 97fd7b67522abd6370f92ff0b401b75e5dbec322 (diff) |
temporarily refactored to use static-text-replay-streams
-rw-r--r-- | parzival.lisp | 75 | ||||
-rw-r--r-- | tests.lisp | 4 |
2 files changed, 47 insertions, 32 deletions
diff --git a/parzival.lisp b/parzival.lisp index 28fb2f9..e869006 100644 --- a/parzival.lisp +++ b/parzival.lisp @@ -32,9 +32,10 @@ (defun parse (stream parser &optional string-as-stream-p) "Parse STREAM with PARSER. If STRING-AS-STREAM-P then STREAM can be a string." - (if string-as-stream-p - (funcall parser (make-string-input-stream stream)) - (funcall parser stream))) + (funcall parser stream)) + ;; (if string-as-stream-p + ;; (funcall parser (make-instance 'static-text-replay-stream :text stream)) + ;; (funcall parser stream))) ;;; A private utility macro for defining a defvar and a defun at the same time, @@ -113,25 +114,34 @@ in then. If the parse fails the combinator else is run instead." ;;; "cleanup stream" function that would search for the smallest safe stream ;;; abstraction to return in case of a success. Call it instead of ;;; recover-source. +;; (defun <<plus (parser1 parser2) +;; "Introduces a choice between two parsers. If PARSER1 succeeds then its result +;; is used. If PARSER1 fails then the stream is rewound and tried again with +;; PARSER2." +;; (lambda (stream2) +;; ;; we need stream2 to rewind from in case of failure +;; (let ((stream2 (replay-on stream1))) +;; (<<if (result parser1 stream2) +;; ;; stream3 is whatever parser1 has created, it might be anything +;; (lambda (stream3) +;; (funcall (<<result result) +;; ;; to save memory when possible, we recover the +;; ;; underlying stream. Only safe to do when stream3 IS stream2 +;; (if (eq stream3 stream2) +;; (recover-source stream2) ;; a.k.a. stream1 +;; stream3))) +;; (lambda (stream3) +;; ;; in case of failure, we rewind from stream2 and try anew. +;; (funcall parser2 (rewind stream2))))))) + (defun <<plus (parser1 parser2) - "Introduces a choice between two parsers. If PARSER1 succeeds then its result - is used. If PARSER1 fails then the stream is rewound and tried again with - PARSER2." - (lambda (stream1) - ;; we need stream2 to rewind from in case of failure - (let ((stream2 (replay-on stream1))) - (<<if (result parser1 stream2) - ;; stream3 is whatever parser1 has created, it might be anything - (lambda (stream3) - (funcall (<<result result) - ;; to save memory when possible, we recover the - ;; underlying stream. Only safe to do when stream3 IS stream2 - (if (eq stream3 stream2) - (recover-source stream2) ;; a.k.a. stream1 - stream3))) - (lambda (stream3) - ;; in case of failure, we rewind from stream2 and try anew. - (funcall parser2 (rewind stream2))))))) + (lambda (stream) + (let ((chkpt (checkpoint stream))) + (<<if (result parser1 stream) + (<<result result) + (progn + (rewind-to stream chkpt) + parser2))))) (defun <<or (parser1 parser2 &rest parsers) @@ -334,9 +344,18 @@ the character C." (defun <<* (parser) "Runs the parser PARSER zero or more times, resulting in of list of parsed values." (lambda (stream) - (<<if (result (<<~ parser) stream) - (<<map-cons result (<<* parser)) - (<<result nil)))) + (let ((result nil) + (parser (<<~ parser))) + (labels ((rec (stream) + (multiple-value-bind + (res ok? stream2) (funcall parser stream) + (if ok? + (progn + (push res result) + (rec stream2)) + (values (nreverse result) t stream2))))) + (rec stream))))) + (defun <<+ (parser) "Like <<* but fails if P does not succeed at least once." @@ -372,11 +391,9 @@ the character C." "Parses a sequence of values with VALUE-PARSER ignoring a separator that is parsed with SEPARATOR-PARSER. E.g. (<<SEP-BY <NAT< (<<CHAR #\,)) would parse a string like '1,2,3,4' and result in a list (1 2 3 4)" - (<<bind value-parser - (lambda (val) - (<<or (<<and separator-parser - (<<map-cons val (<<sep-by value-parser separator-parser))) - (<<result (list val)))))) + (<<cons value-parser (<<* (<<and separator-parser value-parser)))) + + (defun <<brackets (left center right) (<<and left (<<bind center @@ -8,7 +8,7 @@ (defmacro test-with ((var input-string) &rest tests) `(subtest (format nil "With the input ~s ..." ,input-string) - (let ((,var (make-string-input-stream ,input-string))) + (let ((,var (make-instance 'replay-streams:static-text-replay-stream :text ,input-string))) ,@tests))) (defmacro results (stream expr val) @@ -16,7 +16,6 @@ (ok? (gensym)) (stream2 (gensym))) `(multiple-value-bind (,res ,ok? ,stream2) (parse ,stream ,expr) - (setf ,stream ,stream2) ; is this doing what I want? (is (and ,ok? ,res) ,val (format nil "Parsing with ~s results in ~s" ',expr ',val))))) (defmacro fails (stream expr) @@ -24,7 +23,6 @@ (ok? (gensym)) (stream2 (gensym))) `(multiple-value-bind (,res ,ok? ,stream2) (parse ,stream ,expr) - (setf ,stream ,stream2) ; Not sure about this... (unless ,res ; doing this to get rid of warning about unused variable (is ,ok? nil (format nil "Parsing with ~s should fail." ',expr)))))) |