326f3e5714d5066ddd5ad452ed0ee736d02d3828
[sod] / src / parser / parser-expr-proto.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Parsers for expressions with binary operators
4 ;;;
5 ;;; (c) 2009 Straylight/Edgeware
6 ;;;
7
8 ;;;----- Licensing notice ---------------------------------------------------
9 ;;;
10 ;;; This file is part of the Sensible Object Design, an object system for C.
11 ;;;
12 ;;; SOD is free software; you can redistribute it and/or modify
13 ;;; it under the terms of the GNU General Public License as published by
14 ;;; the Free Software Foundation; either version 2 of the License, or
15 ;;; (at your option) any later version.
16 ;;;
17 ;;; SOD is distributed in the hope that it will be useful,
18 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;;; GNU General Public License for more details.
21 ;;;
22 ;;; You should have received a copy of the GNU General Public License
23 ;;; along with SOD; if not, write to the Free Software Foundation,
24 ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 (cl:in-package #:sod-parser)
27
28 ;;;--------------------------------------------------------------------------
29 ;;; Basic protocol.
30
31 (export 'push-operator)
32 (defgeneric push-operator (operator state)
33 (:documentation
34 "Push an OPERATOR onto the STATE's operator stack.
35
36 This should apply existing stacked operators as necessary to obey the
37 language's precedence rules."))
38
39 (export 'push-value)
40 (defgeneric push-value (value state)
41 (:documentation
42 "Push VALUE onto the STATE's value stack.
43
44 The default method just does that without any fuss. It's unlikely that
45 this will need changing unless you invent some really weird values."))
46
47 (export 'apply-operator)
48 (defgeneric apply-operator (operator state)
49 (:documentation
50 "Apply the OPERATOR to argument on the STATE's value stack.
51
52 This should pop any necessary arguments, and push the result."))
53
54 (export 'operator-push-action)
55 (defgeneric operator-push-action (left right)
56 (:documentation
57 "Determine relative precedence between LEFT and RIGHT operators.
58
59 Returns one of three possible values:
60
61 * `:push' means to push the RIGHT operator onto the stack, above the
62 LEFT operator -- i.e., RIGHT has higher precedence than LEFT.
63
64 * `:apply' means to apply the LEFT operator to arguments immediately
65 and try again, comparing RIGHT to the new topmost operator -- i.e.,
66 LEFT has higher precedence than RIGHT.
67
68 * `:error' means that the situation is erroneous: a continuable error is
69 signalled and the situation resolved by applying the LEFT operator and
70 then pushing the RIGHT one -- i.e., treating them as having similar
71 precedence and left associativity).
72
73 There is a default method which decides between `:push' and `:apply' by
74 comparing numerical precedence values."))
75
76 (export 'expr)
77 (defparse expr ((&key (nestedp (gensym "NESTEDP-")))
78 operand binop preop postop)
79 "Parse an expression involving unary and binary operators.
80
81 Within the parsers for operands and operators, the variable NESTEDP is
82 bound to a generalized boolean which is true if an unmatched open-
83 parenthesis has been seen.
84
85 The OPERAND parser should produce a value; the various operator parsers
86 (BINOP, PREOP, and POSTOP) should produce objects obeying the `operator'
87 protocol. The final output of the `expr' parse is the result of
88 evaluating the parsed expression. (Of course, the definition of
89 `evaluation' here is determined entirely by the methods on
90 `apply-operator', so the final value may be a parse tree, for example.)"
91
92 (flet ((wrap (parser)
93 `(parser (,nestedp)
94 (declare (ignorable ,nestedp))
95 ,parser)))
96 `(parse-expression ,(wrap operand)
97 ,(wrap binop)
98 ,(wrap preop)
99 ,(wrap postop))))
100
101 ;;;--------------------------------------------------------------------------
102 ;;; Numerical precedence.
103
104 (export '(operator-left-precedence operator-right-precedence
105 operator-associativity))
106 (defgeneric operator-left-precedence (operator)
107 (:documentation
108 "Return the OPERATOR's left-precedence.
109
110 Higher precedence numbers indicate tighter leftward binding. Under the
111 default method for `operator-push-action', the OPERATOR's left precedence
112 is compared to the existing operators' right precedences to determine the
113 parser's behaviour: if it is higher, then the OPERATOR is pushed;
114 otherwise the existing operator is applied. Thus, equal precedences cause
115 left-associative parsing."))
116 (defgeneric operator-right-precedence (operator)
117 (:documentation
118 "Return the OPERATOR's right-precedence.
119
120 Higher precedence numbers indicate tighter rightward binding. Under the
121 default method for `operator-push-action', a new operator's left
122 precedence may be compared to the existing OPERATOR's right precedences to
123 determine the parser's behaviour: if it is higher, then the new operator
124 is pushed; otherwise the existing OPERATOR is applied. Thus, equal
125 precedences cause left-associative parsing."))
126
127 (defgeneric operator-associativity (operator)
128 (:documentation
129 "Returns an OPERATOR's associativity, as a symbol.
130
131 The return value is one of `:left', `:right' or `nil'. If two adjacent
132 operators have the same precedence, their associativities are compared.
133 If both associativities are `:left' then the left-hand operator is
134 considered to have higher precedence; if both are `:right' then the
135 right-hand operator is considered to have higher precedence. If they're
136 inconsistent or `nil', then an error is reported and the behaviour is as
137 if both were `:left'.")
138 (:method (operator) :left))
139
140 ;;;--------------------------------------------------------------------------
141 ;;; Basic operator protocol.
142
143 (export 'prefix-operator)
144 (defclass prefix-operator ()
145 ()
146 (:documentation
147 "Prefix operator base class.
148
149 Prefix operators are special because they are pushed at a time when the
150 existing topmost operator on the stack may not have its operand
151 available. It is therefore incorrect to attempt to apply any existing
152 operators without careful checking. This class provides a method on
153 `push-operator' which immediately pushes the new operator without
154 inspecting the existing stack."))
155
156 (export 'simple-operator)
157 (defclass simple-operator ()
158 ((%function :initarg :function :reader operator-function)
159 (name :initarg :name :initform "<unnamed operator>"
160 :reader operator-name))
161 (:documentation
162 "A simple operator applies a FUNCTION to arguments when it is applied.
163
164 The precise details of the function are left to subclasses to sort out."))
165
166 (export 'simple-unary-operator)
167 (defclass simple-unary-operator (simple-operator)
168 ()
169 (:documentation
170 "A unary operator works on the topmost value on the value stack.
171
172 The topmost item is popped, the FUNCTION is applied to it, and the result
173 is pushed back on."))
174
175 (export 'simple-binary-operator)
176 (defclass simple-binary-operator (simple-operator)
177 ((lprec :initarg :left-precedence :initarg :precedence
178 :reader operator-left-precedence)
179 (rprec :initarg :right-precedence :reader operator-right-precedence)
180 (associativity :initarg :associative :initform :left
181 :reader operator-associativity))
182 (:documentation
183 "A binary operator works on the two topmost values on the value stack.
184
185 The function's arguments are the two topmost items in /reverse/ order --
186 so the topmost item is second. This is usually what you want.
187
188 The left and right precedences are settable independently. Usually (and
189 this is the default) you will set them equal, and use the `:associativity'
190 initarg to determine associativity; however, right-associativity can also
191 be obtained by setting the right-precedence lower than the left. Special
192 effects can be obtained by setting them in other ways. Use your
193 imagination."))
194
195 (export 'simple-postfix-operator)
196 (defclass simple-postfix-operator (simple-unary-operator)
197 ((lprec :initarg :left-precedence :initarg :precedence
198 :reader operator-left-precedence)
199 (rprec :initarg :right-precedence :reader operator-right-precedence))
200 (:documentation
201 "A postfix operator is applied to a single operand.
202
203 The left and right precedences are settable independently. Usually you
204 will want to set them equal (this is the default) and quite high. Special
205 effects can be obtained by doing other things instead; but note that you
206 will get an incorrect parse if the right precedence is lower than the left
207 precedence of a binary operator because the postfix operator will be
208 applied to the result of the binary operator."))
209
210 (export 'simple-prefix-operator)
211 (defclass simple-prefix-operator (prefix-operator simple-unary-operator)
212 ((rprec :initarg :precedence :reader operator-right-precedence))
213 (:documentation
214 "A prefix operator is applied to a single operand.
215
216 There is only one precedence value for a prefix operator: the
217 `prefix-operator' superclass arranges that the left precedence is
218 effectively minus infinity."))
219
220 (export 'preop)
221 (defmacro preop (name (x prec) &body body)
222 "Define a prefix operator.
223
224 The operator will be called NAME in error messages, and have right
225 precedence PREC. To apply the operator, BODY is evaluated with X bound to
226 the operand."
227
228 `(make-instance 'simple-prefix-operator
229 :name ,name
230 :precedence ,prec
231 :function (lambda (,x) ,@body)))
232
233 (export 'postop)
234 (defmacro postop (name (x prec &key rprec) &body body)
235 "Define a postfix operator.
236
237 The operator will be called NAME in error messages, and have left
238 precedence PREC and right precendence RPREC (defaulting to PREC). To
239 apply the operator, BODY is evaluated with X bound to the operand."
240
241 (once-only (name prec rprec)
242 `(make-instance 'simple-postfix-operator
243 :name ,name
244 :left-precedence ,prec
245 :right-precedence ,(or rprec prec)
246 :function (lambda (,x) ,@body))))
247
248 (export 'binop)
249 (defmacro binop (name (x y prec &key rprec (assoc :left)) &body body)
250 "Define a binary operator.
251
252 The operator will be called NAME in error messages, and have left
253 precedence PREC and right precedence RPREC (defaulting to PREC, implying
254 left associativity under the default `operator-push-action'
255 implementation. To apply the operator, BODY is evaluated with X and Y
256 bound to the operands in the order they were parsed"
257
258 (once-only (name prec rprec assoc)
259 `(make-instance 'simple-binary-operator
260 :name ,name
261 :left-precedence ,prec
262 :right-precedence ,(or rprec prec)
263 :associative ,assoc
264 :function (lambda (,x ,y) ,@body))))
265
266 ;;;--------------------------------------------------------------------------
267 ;;; Parentheses.
268
269 (defclass parenthesis ()
270 ((tag :initarg :tag :initform nil))
271 (:documentation
272 "Base class for parenthesis operators."))
273
274 (export 'open-parenthesis)
275 (defclass open-parenthesis (parenthesis prefix-operator) ())
276
277 (export 'close-parenthesis)
278 (defclass close-parenthesis (parenthesis) ())
279
280 (export '(lparen rparen))
281 (defun lparen (tag)
282 (make-instance 'open-parenthesis :tag tag))
283 (defun rparen (tag)
284 (make-instance 'close-parenthesis :tag tag))
285
286 ;;;----- That's all, folks --------------------------------------------------