9052e54a65277cb58eec25e3e4fa7264ef9776f5
[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 'operator-push-action)
32 (defgeneric operator-push-action (left right)
33 (:documentation
34 "Determine relative precedence between LEFT and RIGHT operators.
35
36 Returns one of three possible values:
37
38 * `:push' means to push the RIGHT operator onto the stack, above the
39 LEFT operator -- i.e., RIGHT has higher precedence than LEFT.
40
41 * `:apply' means to apply the LEFT operator to arguments immediately
42 and try again, comparing RIGHT to the new topmost operator -- i.e.,
43 LEFT has higher precedence than RIGHT.
44
45 * `:error' means that the situation is erroneous: a continuable error is
46 signalled and the situation resolved by applying the LEFT operator and
47 then pushing the RIGHT one -- i.e., treating them as having similar
48 precedence and left associativity).
49
50 There is a default method which decides between `:push' and `:apply' by
51 comparing numerical precedence values."))
52
53 (export 'expr)
54 (defparse expr ((&key (nestedp (gensym "NESTEDP-")))
55 operand binop preop postop)
56 "Parse an expression involving unary and binary operators.
57
58 Within the parsers for operands and operators, the variable NESTEDP is
59 bound to a generalized boolean which is true if an unmatched open-
60 parenthesis has been seen.
61
62 The OPERAND parser should produce a value; the various operator parsers
63 (BINOP, PREOP, and POSTOP) should produce objects obeying the `operator'
64 protocol. The final output of the `expr' parse is the result of
65 evaluating the parsed expression. (Of course, the definition of
66 `evaluation' here is determined entirely by the methods on
67 `apply-operator', so the final value may be a parse tree, for example.)"
68
69 (flet ((wrap (parser)
70 `(parser (,nestedp)
71 (declare (ignorable ,nestedp))
72 ,parser)))
73 `(parse-expression ,(wrap operand)
74 ,(wrap binop)
75 ,(wrap preop)
76 ,(wrap postop))))
77
78 ;;;--------------------------------------------------------------------------
79 ;;; Numerical precedence.
80
81 (export '(operator-left-precedence operator-right-precedence
82 operator-associativity))
83 (defgeneric operator-left-precedence (operator)
84 (:documentation
85 "Return the OPERATOR's left-precedence.
86
87 Higher precedence numbers indicate tighter leftward binding. Under the
88 default method for `operator-push-action', the OPERATOR's left precedence
89 is compared to the existing operators' right precedences to determine the
90 parser's behaviour: if it is higher, then the OPERATOR is pushed;
91 otherwise the existing operator is applied. Thus, equal precedences cause
92 left-associative parsing."))
93 (defgeneric operator-right-precedence (operator)
94 (:documentation
95 "Return the OPERATOR's right-precedence.
96
97 Higher precedence numbers indicate tighter rightward binding. Under the
98 default method for `operator-push-action', a new operator's left
99 precedence may be compared to the existing OPERATOR's right precedences to
100 determine the parser's behaviour: if it is higher, then the new operator
101 is pushed; otherwise the existing OPERATOR is applied. Thus, equal
102 precedences cause left-associative parsing."))
103
104 (defgeneric operator-associativity (operator)
105 (:documentation
106 "Returns an OPERATOR's associativity, as a symbol.
107
108 The return value is one of `:left', `:right' or `nil'. If two adjacent
109 operators have the same precedence, their associativities are compared.
110 If both associativities are `:left' then the left-hand operator is
111 considered to have higher precedence; if both are `:right' then the
112 right-hand operator is considered to have higher precedence. If they're
113 inconsistent or `nil', then an error is reported and the behaviour is as
114 if both were `:left'.")
115 (:method (operator) :left))
116
117 ;;;--------------------------------------------------------------------------
118 ;;; Basic operator protocol.
119
120 (export 'prefix-operator)
121 (defclass prefix-operator ()
122 ()
123 (:documentation
124 "Prefix operator base class.
125
126 Prefix operators are special because they are pushed at a time when the
127 existing topmost operator on the stack may not have its operand
128 available. It is therefore incorrect to attempt to apply any existing
129 operators without careful checking."))
130
131 (export 'simple-operator)
132 (defclass simple-operator ()
133 ((%function :initarg :function :reader operator-function)
134 (name :initarg :name :initform "<unnamed operator>"
135 :reader operator-name))
136 (:documentation
137 "A simple operator applies a FUNCTION to arguments when it is applied.
138
139 The precise details of the function are left to subclasses to sort out."))
140
141 (export 'simple-unary-operator)
142 (defclass simple-unary-operator (simple-operator)
143 ()
144 (:documentation
145 "A unary operator works on the topmost value on the value stack.
146
147 The topmost item is popped, the FUNCTION is applied to it, and the result
148 is pushed back on."))
149
150 (export 'simple-binary-operator)
151 (defclass simple-binary-operator (simple-operator)
152 ((lprec :initarg :left-precedence :initarg :precedence
153 :reader operator-left-precedence)
154 (rprec :initarg :right-precedence :reader operator-right-precedence)
155 (associativity :initarg :associative :initform :left
156 :reader operator-associativity))
157 (:documentation
158 "A binary operator works on the two topmost values on the value stack.
159
160 The function's arguments are the two topmost items in /reverse/ order --
161 so the topmost item is second. This is usually what you want.
162
163 The left and right precedences are settable independently. Usually (and
164 this is the default) you will set them equal, and use the `:associativity'
165 initarg to determine associativity; however, right-associativity can also
166 be obtained by setting the right-precedence lower than the left. Special
167 effects can be obtained by setting them in other ways. Use your
168 imagination."))
169
170 (export 'simple-postfix-operator)
171 (defclass simple-postfix-operator (simple-unary-operator)
172 ((lprec :initarg :left-precedence :initarg :precedence
173 :reader operator-left-precedence)
174 (rprec :initarg :right-precedence :reader operator-right-precedence))
175 (:documentation
176 "A postfix operator is applied to a single operand.
177
178 The left and right precedences are settable independently. Usually you
179 will want to set them equal (this is the default) and quite high. Special
180 effects can be obtained by doing other things instead; but note that you
181 will get an incorrect parse if the right precedence is lower than the left
182 precedence of a binary operator because the postfix operator will be
183 applied to the result of the binary operator."))
184
185 (export 'simple-prefix-operator)
186 (defclass simple-prefix-operator (prefix-operator simple-unary-operator)
187 ((rprec :initarg :precedence :reader operator-right-precedence))
188 (:documentation
189 "A prefix operator is applied to a single operand.
190
191 There is only one precedence value for a prefix operator: the
192 `prefix-operator' superclass arranges that the left precedence is
193 effectively minus infinity."))
194
195 (export 'preop)
196 (defmacro preop (name (x prec) &body body)
197 "Define a prefix operator.
198
199 The operator will be called NAME in error messages, and have right
200 precedence PREC. To apply the operator, BODY is evaluated with X bound to
201 the operand."
202
203 `(make-instance 'simple-prefix-operator
204 :name ,name
205 :precedence ,prec
206 :function (lambda (,x) ,@body)))
207
208 (export 'postop)
209 (defmacro postop (name (x prec &key rprec) &body body)
210 "Define a postfix operator.
211
212 The operator will be called NAME in error messages, and have left
213 precedence PREC and right precendence RPREC (defaulting to PREC). To
214 apply the operator, BODY is evaluated with X bound to the operand."
215
216 (once-only (name prec rprec)
217 `(make-instance 'simple-postfix-operator
218 :name ,name
219 :left-precedence ,prec
220 :right-precedence ,(or rprec prec)
221 :function (lambda (,x) ,@body))))
222
223 (export 'binop)
224 (defmacro binop (name (x y prec &key rprec (assoc :left)) &body body)
225 "Define a binary operator.
226
227 The operator will be called NAME in error messages, and have left
228 precedence PREC and right precedence RPREC (defaulting to PREC, implying
229 left associativity under the default `operator-push-action'
230 implementation. To apply the operator, BODY is evaluated with X and Y
231 bound to the operands in the order they were parsed"
232
233 (once-only (name prec rprec assoc)
234 `(make-instance 'simple-binary-operator
235 :name ,name
236 :left-precedence ,prec
237 :right-precedence ,(or rprec prec)
238 :associative ,assoc
239 :function (lambda (,x ,y) ,@body))))
240
241 ;;;--------------------------------------------------------------------------
242 ;;; Parentheses.
243
244 (defclass parenthesis ()
245 ((tag :initarg :tag :initform nil))
246 (:documentation
247 "Base class for parenthesis operators."))
248
249 (export 'open-parenthesis)
250 (defclass open-parenthesis (parenthesis prefix-operator) ())
251
252 (export 'close-parenthesis)
253 (defclass close-parenthesis (parenthesis) ())
254
255 (export '(lparen rparen))
256 (defun lparen (tag)
257 (make-instance 'open-parenthesis :tag tag))
258 (defun rparen (tag)
259 (make-instance 'close-parenthesis :tag tag))
260
261 ;;;----- That's all, folks --------------------------------------------------