2 ;;;; Here is the dreaded infix parser.
3 ;;;; Heavily commented so I remember what happens here.
6 ; An ordered stack of operations is build up as closures,
7 ; each hold an accumulated value. When an operator of lesser
8 ; "rank" (see rank constants below) is encountered, the stack unwinds
9 ; recursively, passing the encountered rank and the accumulated
10 ; value for the operation.
12 ; Obviously there are different scopes where these operations apply,
13 ; namely "paren" and "function" scopes. Both scopes have their own
14 ; "read-eval" loop which terminate by the usual stack unwind and
15 ; a thrown symbol corresponding to the scope:
16 ; 'break-paren scope and 'break-fn-scope
18 ; The order of events and the way the termination is handled
19 ; is DIFFERENT between the two. Function scope is notably more
20 ; complex because I don't require ONE function parameter to be
21 ; enclosed in parentheses, multiple parameters require them.
23 ; TODO: overhaul parser to read 2a as 2*a
24 ; This will be a big change, optionally giving functions
25 ; control of the reader to parse their arguments.
29 ;V Set precedence ranks.V
30 (defconstant +base-rank
+ 0)
31 (defconstant +paren-rank
+ 1)
32 (defconstant +comma-rank
+ 2)
33 (defconstant +fn-rank
+ 3)
34 (defconstant +addn-rank
+ 4)
35 (defconstant +subtrn-rank
+ 5)
36 (defconstant +multn-rank
+ 6)
37 (defconstant +divisn-rank
+ 7)
38 (defconstant +exptn-rank
+ 8)
39 (defconstant +vip-rank
+ 9);< Virtually infinite precedence.
42 (defun multn-if-last-read ()
43 (declare (special *last-read
*))
45 ;; Implicit multiplication, two
46 ;; values were seperated by a space.
48 (parsed-opern +multn-rank
+ #'multn
))))
50 ;;; Everything in the file calls this
51 ;;; read function on the infix stream.
52 (defun read-next-infix (strm)
53 (declare (special *unwind-rank-fn
* *last-read
*))
56 (let ((*package
* (find-package
57 :lineal.client-vars
)))
58 ;; Actually read from the stream,
59 ;; sometimes an exception is thrown and
60 ;; control breaks to the current "read loop"
61 (setq this-read
(read strm
)))
63 ;; Did not just read an operator.
64 (multn-if-last-read);< Check for implied multiplication.
65 (if (symbolp this-read
)
66 (let ((val (symbol-value this-read
)))
67 ;; Don't bother checking boundp,
68 ;; there's a condition catch in
69 ;; process-infix-from-stream
71 ;; A symbol representing a function
72 ;; was read, change scope.
74 strm
(symbol-function val
))
75 (setq *last-read
* val
)))
76 (setq *last-read
* this-read
))))
79 (declare (ignore condit
))
80 ;; Don't check for unclosed parentheses,
82 (funcall *unwind-rank-fn
*
83 +base-rank
+ *last-read
*))))
85 (defmacro opern-climber-fn
86 ((this-rank op-rank val
) . body
)
87 `(lambda (,op-rank
,val
)
89 ;; A prefixed operator (see function below)
90 ;; was parsed last, unary-pre-opern.
92 ,this-rank
(the function
,op-rank
)))
94 (declare (type (integer 0 ,+vip-rank
+) ,op-rank
))
97 ;;; When something like 2 + -3 is encountered,
98 ;;; the negative is what this function deals with.
99 (defun unary-pre-opern (prev-rank this-op-fn
)
100 (declare (special *unwind-rank-fn
*))
101 (let ((prev-unwind-rank-fn *unwind-rank-fn
*)
106 (prev-rank op-rank val
)
107 (if (and (< prev-rank op-rank
)
108 (< +multn-rank
+ op-rank
))
109 (progn (setq *unwind-rank-fn
*
112 (funcall prev-unwind-rank-fn op-rank
113 (funcall this-op-fn val
))))
114 *unwind-rank-fn
* this-unwind-rank-fn
)
115 (throw 'fn-scope
(values))))
117 ;;; An operator is parsed.
118 (defun parsed-opern (this-rank this-op-fn
)
119 (declare (special *unwind-rank-fn
* *last-read
*))
121 (funcall *unwind-rank-fn
* this-op-fn nil
))
122 (let* ((args (cons (funcall *unwind-rank-fn
*
123 this-rank
*last-read
*)
126 (prev-unwind-rank-fn *unwind-rank-fn
*)
132 (this-rank op-rank val
)
134 ((= this-rank op-rank
)
136 *unwind-rank-fn
* this-unwind-rank-fn
138 tail
(cdr (rplacd tail
(cons val nil
))))
139 (throw 'fn-scope
(values)))
140 ((< this-rank op-rank
)
141 (setq *unwind-rank-fn
* this-unwind-rank-fn
)
144 (rplacd tail
(cons val nil
))
145 (funcall prev-unwind-rank-fn op-rank
146 (apply this-op-fn args
)))))
147 *unwind-rank-fn
* this-unwind-rank-fn
)
151 (defun parse-function-scope (strm this-fn
)
152 (declare (special *unwind-rank-fn
* *last-read
*))
153 (do () ((not (char= #\Space
(peek-char nil strm
))))
155 (when (char= #\
( (peek-char nil strm
))
156 ;; User chose to enclose the
157 ;; argument(s) in parentheses.
158 (read-next-infix strm
)
160 (if (consp *last-read
*)
161 (apply this-fn
*last-read
*)
162 (funcall this-fn
*last-read
*)))
163 (return-from parse-function-scope
(values)))
164 (when (char= #\
) (peek-char nil strm
))
165 ;; Using notation like (f*g)(x)
167 (setq *last-read
* this-fn
)
168 (return-from parse-function-scope
(values)))
169 (let ((prev-unwind-rank-fn *unwind-rank-fn
*)
175 (+fn-rank
+ op-rank val
)
177 ((< op-rank
+fn-rank
+)
178 ;; Breaking from parens or program.
179 (funcall prev-unwind-rank-fn
180 op-rank
(if (consp val
)
182 (funcall this-fn val
))))
183 ((= op-rank
+fn-rank
+)
184 ;; Return from the function scope.
185 (setq *unwind-rank-fn
* prev-unwind-rank-fn
186 *last-read
* (if (consp val
)
188 (funcall this-fn val
)))
189 (throw 'break-fn-scope
(values)))
190 (t ;V Stay in the function scope.V
191 (setq *unwind-rank-fn
* this-unwind-rank-fn
)
193 *unwind-rank-fn
* this-unwind-rank-fn
)
198 (read-next-infix strm
))))
199 (funcall *unwind-rank-fn
*
200 +fn-rank
+ *last-read
*)))
202 ;;; "read-eval" loop used by process-infix-from-stream
203 ;;; and open-paren-reader
204 (defun parse-infix (strm)
205 (declare (special *unwind-rank-fn
* *last-read
*))
206 (let ((prev-unwind-rank-fn *unwind-rank-fn
*)
212 (+paren-rank
+ op-rank val
)
214 ((< op-rank
+paren-rank
+)
215 (funcall prev-unwind-rank-fn
217 ((= op-rank
+paren-rank
+)
218 ;; Closing paren encountered.
219 (setq *unwind-rank-fn
* prev-unwind-rank-fn
221 (throw 'break-paren-scope
(values)))
223 (setq *unwind-rank-fn
* this-unwind-rank-fn
)
225 *unwind-rank-fn
* this-unwind-rank-fn
)
231 (read-next-infix strm
))))))
233 (defun open-paren-reader (strm ch
)
234 (declare (ignore ch
))
236 (catch 'break-paren-scope
240 ;;; Unwind the operation stack.
241 ;;; When the paren closure is reached,
242 ;;; *last-read* is set to the value calculated within
243 ;;; the parenthesis and 'break-paren-scope is thrown.
244 (defun close-paren-reader (strm ch
)
245 (declare (ignore strm ch
)
246 (special *unwind-rank-fn
* *last-read
*))
247 (funcall *unwind-rank-fn
*
248 +paren-rank
+ *last-read
*))
250 ;;; If *last-read* is nil, an operator
251 ;;; was read last, we can't logically
252 ;;; break from a function in that case.
253 (defun space-reader (strm ch
)
254 (declare (ignore strm ch
)
255 (special *last-read
*))
257 (throw 'break-fn-scope
(values))))
259 (defun comma-reader (strm ch
)
260 (declare (ignore strm ch
)
261 (special *unwind-rank-fn
* *last-read
*))
262 (let* ((lis (cons (funcall *unwind-rank-fn
*
263 +comma-rank
+ *last-read
*)
266 (prev-unwind-rank-fn *unwind-rank-fn
*)
271 (lambda (op-rank val
)
273 ((= +comma-rank
+ op-rank
)
279 (cdr (rplacd tail
(cons val nil
))))
280 (throw 'break-fn-scope
(values)))
281 ((< +comma-rank
+ op-rank
)
282 ;todo: figure out what this does
283 (setq *unwind-rank-fn
*
286 (t (rplacd tail
(cons val nil
))
287 (funcall prev-unwind-rank-fn op-rank
289 *unwind-rank-fn
* this-unwind-rank-fn
)
292 (defun set-opern-reader (ch this-rank this-op-fn
)
296 (declare (ignore strm ch
))
297 (parsed-opern this-rank this-op-fn
))))
299 (defun process-infix-from-stream (strm)
300 (let ((*readtable
* (copy-readtable))
301 (*read-default-float-format
* 'double-float
)
304 (lambda (op-rank val
)
305 (declare (ignore op-rank
))
306 (throw 'end-result val
)))
308 (declare (special *unwind-rank-fn
* *last-read
*))
309 (setf (readtable-case *readtable
*) :preserve
)
310 (set-macro-character #\
( #'open-paren-reader
)
311 (set-macro-character #\
) #'close-paren-reader
)
312 (set-macro-character #\Space
#'space-reader
)
313 (set-macro-character #\
, #'comma-reader
)
314 (set-opern-reader #\
+ +addn-rank
+ #'addn
)
315 (set-opern-reader #\-
+subtrn-rank
+ #'subtrn
)
316 (set-opern-reader #\
* +multn-rank
+ #'multn
)
317 (set-opern-reader #\
/ +divisn-rank
+ #'divisn
)
318 (set-opern-reader #\^
+exptn-rank
+ #'exptn
)
325 (declare (ignore condit
))
327 nil
"Likely too many parens! ~
328 (as if there were such a thing)~%"))
332 nil
"Evaluation flopped, perhaps bad input?~%~
333 Debug info: ~A~%" condit
)))))
335 (defun process-infix-from-string (str)
336 (with-input-from-string (strm str
)
337 (process-infix-from-stream strm
)))