summaryrefslogtreecommitdiff
path: root/parzival.lisp
diff options
context:
space:
mode:
authorBoutade <thegoofist@protonmail.com>2019-04-24 14:07:46 -0500
committerBoutade <thegoofist@protonmail.com>2019-04-24 14:07:46 -0500
commitf8e4f8979d815c82ea0dfe524e5ba2ef8ed89bef (patch)
tree6f4ae4a9c8833a022640cc8513c482bf96aa0e1d /parzival.lisp
parent074eaea57b5afffdc00c0d6bd8ff05acb88be881 (diff)
Added documentation to a number of parsers and combinators
Diffstat (limited to 'parzival.lisp')
-rw-r--r--parzival.lisp32
1 files changed, 27 insertions, 5 deletions
diff --git a/parzival.lisp b/parzival.lisp
index 00e3890..9ad09a7 100644
--- a/parzival.lisp
+++ b/parzival.lisp
@@ -7,23 +7,30 @@
;;; e.g. >>foo returns a parser while >goo> is a parser
-(defmacro >def> (name parser)
+(defmacro >def> (name parser &optional docstring)
`(progn
(defvar ,name ,parser)
- (defun ,name (stream) (funcall ,name stream))))
+ (defun ,name (stream) ,docstring (funcall ,name stream))))
(defun >>result (x)
(lambda (stream) (values x t stream)))
-(>def> >fail> (lambda (stream) (values nil nil stream)))
+(>def> >fail>
+ (lambda (stream) (values nil nil stream))
+ "Consumes nothing from input and fails the parse.")
-(>def> >item> (lambda (stream) (values (read-char stream) t stream)))
+(>def> >item>
+ (lambda (stream) (values (read-char stream) t stream))
+ "Consumes exactly one item from input and results in that item.")
-(>def> >peek> (lambda (stream) (values (peek-char stream) t stream)))
+(>def> >peek>
+ (lambda (stream) (values (peek-char stream) t stream))
+ "Results in next item from the input without consuming it.")
(defmacro >>if ((var parser stream) then else)
+ "Binds the result of the parser on the stream to var and runs the combinator in then. If the parse fials the combinator else is run instead."
(let ((ok? (gensym))
(stream2 (gensym)))
`(multiple-value-bind (,var ,ok? ,stream2) (funcall ,parser ,stream)
@@ -32,9 +39,11 @@
(funcall ,else ,stream2)))))
(defmacro >>when ((var parser stream) form)
+ "Binds the result of parser on stream to var and runs the form. Fails otherwise."
`(>>if (,var ,parser ,stream) ,form >fail>))
(defun >>rewinding (parser)
+ "Turns a parser into a rewinding parser. I.e. If the parse fails, the stream is rewound to its state from before the parse so that other parsers can continue from there."
(lambda (s)
(let ((s (replay-on s)))
(>>if (res parsers)
@@ -44,60 +53,73 @@
(funcall >fail> (rewind s)))))))
(defun >>bind (p f)
+ "Performs a parse p and returns a new parser that results from applying f to the result of p. If p fails, then so does (>>bind p f)."
(lambda (stream)
(>>when (result p stream)
(funcall f result))))
(defun >>= (p f &rest fs)
+ "Just like bind but with a chain of functions. Each function accepts the result of the parse from the previous step and returns a new parser. If any intermediate parser fails, the whole chain fails."
(if fs
(apply #'>>= (cons (>>bind p f) fs))
(>>bind p f)))
(defun >>and (p1 p2 &rest ps)
+ "Just like >>= but where parse results are ignored. I.e. Applies each parser in sequence, ignoring any intermediate results. The result (>>and p1 p2 ... pn) is the result of pn."
(if ps
(apply #'>>and (cons (>>bind p1 (lambda (ignore) p2)) ps))
(>>bind p1 (lambda (ignore) p2))))
(defun >>map (f p)
+ "Turns a parser that results in x into a parser that results in (funcall f x)."
(lambda (s)
(>>when (val p s) (>>result (funcall f val)))))
;; fails if p fails
(defun >>cons (x p)
+ "If the parser p results in y then the parser (>>cons x p) results in (cons x y). If p fails, then so does (>>cons x p)"
(>>map (lambda (xs) (cons x xs)) p))
;; succeeds with (x) even if p fails, otherwise (cons x result-of-p)
(defun >>cons? (x p)
+ "Just like >>cons except that (>>cons? x p) always succeeds. In the case of p failing, (>>cons? x p) results in (cons x nil)."
(lambda (s)
(>>if (val p s)
(>>result (cons x val))
(>>result (cons x nil)))))
(defun >>many (p)
+ "Runs the parser p zero or more times, resulting in of list of parsed values."
(>>bind p (lambda (x) (>>cons? x (>>many p)))))
(defun >>many1 (p)
+ "Like >>many but fails if p does not succeed at least once."
(>>bind p (lambda (x) (>>cons x (>>many p)))))
(defun >>sat (pred)
+ "(>>sat pred) is parser that reads one item from input and succeeds if (pred item) is true."
(>>bind >item>
(lambda (c) (if (funcall pred c)
(>>result c)
>fail>))))
(defun >>peek-sat (pred)
+ "(>>peek-sat pred) is like (>>sat pred) but doesn't consume the item if (pred item) is false."
(>>bind >peek>
(lambda (c) (if (funcall pred c)
>item>
>fail>))))
(defun >>char (c)
+ "(>>char c) consumes an item from input and succeds if that item is exactly the character c."
(>>sat (lambda (x) (char-equal x c))))
(defun >>peek-char (c)
+ "Like >>char but wont consume the input if the input is not equal to c."
(>>peek-sat (lambda (x) (char-equal x c))))
(defun >>string (str)
+ "Parses exactly the string str, resulting in that str on success."
(>>map (lambda (ignore) str)
(apply #'>>and (loop for c across str collect (>>peek-char c)))))