Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 176 lines (151 sloc) 8.283 kb
f5e912f @Ramarren Plurarize combinators in system/package name
authored
1 (in-package :parser-combinators)
2b91b9c @Ramarren Implement basic operators and examples.
authored
2
3 ;;; operate on list of tokens
4
5 (defclass parser-possibility ()
6 ((tree :accessor tree-of :initarg :tree :initform nil)
7 (suffix :accessor suffix-of :initarg :suffix :initform nil)))
8
e737752 @Ramarren Add more sensible lazyness constructs
authored
9
f8f649b @Ramarren Add lazy parse-result class and extractor function
authored
10 ;;; lazy results
dfd88fc @Ramarren Change parse result representation.
authored
11
12 ;;; continuation is a thunk returning parser-possibility or nil
13
14 (defclass parse-result-store ()
15 ((storage :accessor storage-of :initarg :storage :initform (make-array 3 :initial-element nil))
16 (counter :accessor counter-of :initarg :counter :initform 0)
c513135 @deepfire basic.lisp: add some docstrings
deepfire authored
17 (continuation :accessor continuation-of :initarg :continuation :initform (constantly nil)))
18 (:documentation
19 "Track a set of parse results, of which COUNTER are already computed and placed in STORAGE,
20 and an unknown remainder is to be realised by repetitively calling CONTINUATION."))
dfd88fc @Ramarren Change parse result representation.
authored
21
2f9d8d3 @Ramarren Create class before degeneric using it, add dependencies.
authored
22 (defclass parse-result ()
23 ((store :accessor store-of :initarg :store :initform nil)
c513135 @deepfire basic.lisp: add some docstrings
deepfire authored
24 (current :accessor current-of :initarg :current :initform -1))
25 (:documentation
26 "Represent a set of parse results, as tracked by STORE, with one of them deemed CURRENT."))
2f9d8d3 @Ramarren Create class before degeneric using it, add dependencies.
authored
27
dfd88fc @Ramarren Change parse result representation.
authored
28 (defgeneric nth-result (n parse-result-store)
c513135 @deepfire basic.lisp: add some docstrings
deepfire authored
29 (:documentation
30 "Attempt to realise all results within PARSE-RESULT-STORE upto and including the N-th one,
31 and return that upon success. Otherwise return NIL.")
dfd88fc @Ramarren Change parse result representation.
authored
32 (:method (n (parse-result-store null))
33 (declare (ignore n parse-result-store))
34 nil)
654a9de @Ramarren Add some documentation.
authored
35 (:method (n (parse-result parse-result))
36 (nth-result n (store-of parse-result)))
dfd88fc @Ramarren Change parse result representation.
authored
37 (:method (n (parse-result-store parse-result-store))
38 (with-accessors ((storage storage-of)
39 (counter counter-of)
40 (continuation continuation-of))
41 parse-result-store
42 (if (< n counter)
43 (svref storage n)
44 (when continuation
45 (iter (for i from counter to n)
46 (for next-result = (funcall continuation))
47 (when (= i (length storage))
48 (let ((old-storage storage))
49 (setf storage (make-array (* 2 (length storage)) :initial-element nil))
50 (setf (subseq storage 0 i) old-storage)))
51 (setf (svref storage i) next-result)
52 (unless next-result
53 (setf continuation nil))
54 (while next-result)
02e1d53 @Ramarren Maybe fix loop counter bug.
authored
55 (finally (setf counter i)
dfd88fc @Ramarren Change parse result representation.
authored
56 (return next-result))))))))
57
58 (defun make-parse-result (continuation)
c513135 @deepfire basic.lisp: add some docstrings
deepfire authored
59 "Document all potential results of CONTINUATION as a PARSE-RESULT object."
dfd88fc @Ramarren Change parse result representation.
authored
60 (make-instance 'parse-result :store
61 (make-instance 'parse-result-store :continuation continuation)))
ad1cb46 @Ramarren Rewrite with continuation passing.
authored
62
290aeb1 @Ramarren Implement lazy results.
authored
63 (defun current-result (parse-result)
dfd88fc @Ramarren Change parse result representation.
authored
64 (when (= (current-of parse-result) -1)
65 (next-result parse-result))
66 (nth-result (current-of parse-result) (store-of parse-result)))
f8f649b @Ramarren Add lazy parse-result class and extractor function
authored
67
68 (defun next-result (parse-result)
dfd88fc @Ramarren Change parse result representation.
authored
69 (incf (current-of parse-result))
70 (current-result parse-result))
f8f649b @Ramarren Add lazy parse-result class and extractor function
authored
71
3426993 @Ramarren Add result lazyness.
authored
72 (defun gather-results (parse-result)
c513135 @deepfire basic.lisp: add some docstrings
deepfire authored
73 "Obtain all of the results within PARSE-RESULT, starting with the current one, potentially
74 realising the non-realised ones in the backing store."
290aeb1 @Ramarren Implement lazy results.
authored
75 (let ((current-result (current-result parse-result))
ae52aad @Ramarren Untabify and whitespace cleanup.
authored
76 (continuation-results
77 (iter (for result next (next-result parse-result))
78 (while result)
79 (collect result))))
290aeb1 @Ramarren Implement lazy results.
authored
80 (when current-result
81 (cons current-result continuation-results))))
3426993 @Ramarren Add result lazyness.
authored
82
dfd88fc @Ramarren Change parse result representation.
authored
83 (defun copy-parse-result (parse-result)
84 (make-instance 'parse-result :store (store-of parse-result)))
1abdf2e @Ramarren Add multiple choices.
authored
85
1a59bd8 @Ramarren Try and fail to write patterned parser generator
authored
86 ;;; here parser spec is list of (pattern optional-guard comprehension)
87 ;;; using do-like notation, <- is special
88
a69896a @Ramarren Implement pattern parser generator with simplified do notation
authored
89 ;;; list of either monads: (monad parameters), name bindings (<- name monad)
90 ;;; simple, no let
91
1828973 @Ramarren Eval-whenify do-notation
authored
92 (eval-when (:compile-toplevel :load-toplevel :execute)
bcce707 @Ramarren Make ignored lambda parameters a gensym
authored
93 (defun do-notation (monad-sequence bind ignore-gensym)
47df9ad @Ramarren Move BPM dependency to test suite.
authored
94 (destructuring-bind (monad . rest) monad-sequence
95 (cond ((endp rest)
96 monad)
97 ((and (listp monad)
98 (eql (car monad) '<-))
99 (destructuring-bind (<- name monad) monad
100 (declare (ignore <-))
101 `(,bind ,monad
102 #'(lambda (,name)
103 ,(do-notation rest bind ignore-gensym)))))
104 (t
105 `(,bind ,monad
106 #'(lambda (,ignore-gensym)
107 (declare (ignore ,ignore-gensym))
108 ,(do-notation rest bind ignore-gensym))))))))
a69896a @Ramarren Implement pattern parser generator with simplified do notation
authored
109
93f700a @Ramarren More parser combinators
authored
110 (defmacro mdo (&body spec)
56d45aa @Ramarren Fix docstring for mdo
authored
111 "Combinator: use do-like notation to sequentially link parsers. (<- name parser) allows capturing of return values."
bcce707 @Ramarren Make ignored lambda parameters a gensym
authored
112 (with-unique-names (ignore-gensym)
113 (do-notation spec 'bind ignore-gensym)))
e7c0594 @Ramarren Try to write psat and many?
authored
114
47dd3a1 @Ramarren Working left recursion curtailment
authored
115 (defparameter *memo-table* (make-hash-table))
116
df86968 @Ramarren Change parse-string and parse-string* to parse-sequence and parse-seq…
authored
117 (defun parse-sequence (parser sequence)
118 "Parse a sequence (where a sequence is any object which implementes CONTEXT interface), return a
119 PARSE-RESULT object. All returned values may share structure."
47dd3a1 @Ramarren Working left recursion curtailment
authored
120 (let ((*memo-table* (make-hash-table))
df86968 @Ramarren Change parse-string and parse-string* to parse-sequence and parse-seq…
authored
121 (context (make-context sequence)))
d0f2c84 @Ramarren Return context front from top level functions.
authored
122 (values (make-parse-result (funcall parser context))
9c7569a @Ramarren Bind seen-positions hashtable to a context
authored
123 (front-of context)
124 (seen-positions-of context))))
5f9f20e @Ramarren Add parse-string* utility function.
authored
125
df86968 @Ramarren Change parse-string and parse-string* to parse-sequence and parse-seq…
authored
126 (defun parse-string (parser string)
127 "Synonym for parse-sequence. Parse a string, return a PARSE-RESULT object. All returned values may share structure."
128 (parse-sequence parser string))
129
130 (defun parse-sequence* (parser sequence &key (complete nil))
a959cad @deepfire A simple profiling mechanism, based on per-position context creation …
deepfire authored
131 "Parse a sequence (where a sequence is any object which implementes CONTEXT interface) and
132 return as multiple values the first result, whether the parse was incomplete, whether it was
133 successful, the context front and the position frequency table. The context front is an object
134 containing the context which most advances the input sequence and a list of lists of parser
135 tags which were current at that point, which allows approximate error reporting. It will be
136 NIL if the parse is successful and complete. The position frequency table serves profiling
137 needs and maps sequence positions to the number of times a new context was created at that
138 position -- which should provide a rough hint at how problematic that particular spot is.
d0f2c84 @Ramarren Return context front from top level functions.
authored
139
140 If COMPLETE is T, return the first parse to consume the input
37a88a4 @Ramarren Add key argument to parse-string*
authored
141 completely. If COMPLETE is :FIRST return the first result only when it the whole input was consumed,
142 or immediately return nil."
9c7569a @Ramarren Bind seen-positions hashtable to a context
authored
143 (multiple-value-bind (parse-result front seen-positions) (parse-sequence (ensure-parser parser) sequence)
144 (ecase complete
145 ((nil :first)
146 (let ((result
147 (current-result parse-result)))
148 (cond ((or (null result)
149 (and (eql complete :first)
150 (not (end-context-p (suffix-of result)))))
151 (values nil nil nil front))
152 ((not (end-context-p (suffix-of result)))
153 (values (tree-of result) (suffix-of result) t front seen-positions))
154 (t (values (tree-of result) nil t nil seen-positions)))))
155 (t (iter (with results = parse-result)
156 (for result = (next-result results))
157 (while result)
158 (when (end-context-p (suffix-of result))
159 (return (values (tree-of result) nil t nil seen-positions)))
160 (finally (return (values nil nil nil front))))))))
df86968 @Ramarren Change parse-string and parse-string* to parse-sequence and parse-seq…
authored
161
162 (defun parse-string* (parser string &key (complete nil))
a959cad @deepfire A simple profiling mechanism, based on per-position context creation …
deepfire authored
163 "Synonym for parse-sequence*. Parse a string and return as multiple values the first result,
164 whether the parse was incomplete, whether it was successful, the context front, and the
165 position frequency table. The context front is an object containing the context which most
166 advances the input sequence and a list of lists of parser tags which were current at that
167 point, which allows approximate error reporting. It will be NIL if the parse is successful and
168 complete. The position frequency table serves profiling needs and maps sequence positions to
169 the number of times a new context was created at that position -- which should provide a rough
170 hint at how problematic that particular spot is.
df86968 @Ramarren Change parse-string and parse-string* to parse-sequence and parse-seq…
authored
171
172 If COMPLETE is T, return the first parse to consume the input
173 completely. If COMPLETE is :FIRST return the first result only when it the whole input was consumed,
174 or immediately return nil."
175 (parse-sequence* parser string :complete complete))
Something went wrong with that request. Please try again.