Massive reorganization in progress.
[sod] / src / parser / proto-parser-expr.lisp
CommitLineData
dea4d055
MW
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 Sensble 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-vlaue)
40(defgeneric push-value (value state)
41 (:documentation
42 "Push VALUE onto the STATE's value stack.
43
44 The default message 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 (flet ((wrap (parser)
81 `(parser (,nestedp)
82 (declare (ignorable ,nestedp))
83 ,parser)))
84 `(parse-expression ,(wrap operand)
85 ,(wrap binop)
86 ,(wrap preop)
87 ,(wrap postop))))
88
89;;;--------------------------------------------------------------------------
90;;; Numerical precedence.
91
92(export '(operator-left-precedence operator-right-precedence))
93(defgeneric operator-left-precedence (operator)
94 (:documentation
95 "Return the OPERATOR's left-precedence.
96
97 Higher precedence numbers indicate tighter leftward binding. Under the
98 default method for `operator-push-action', the OPERATOR's left precedence
99 is compared to the existing operators' right precedences to determine the
100 parser's behaviour: if it is higher, then the OPERATOR is pushed;
101 otherwise the existing operator is applied. Thus, equal precedences cause
102 left-associative parsing."))
103(defgeneric operator-right-precedence (operator)
104 (:documentation
105 "Return the OPERATOR's right-precedence.
106
107 Higher precedence numbers indicate tighter rightward binding. Under the
108 default method for `operator-push-action', a new operator's left
109 precedence may be compared to the existing OPERATOR'S right precedences to
110 determine the parser's behaviour: if it is higher, then the new operator
111 is pushed; otherwise the existing OPERATOR is applied. Thus, equal
112 precedences cause left-associative parsing."))
113
114(defgeneric operator-associativity (operator)
115 (:documentation
116 "Returns an OPERATOR's associativity, as a symbol.
117
118 The return value is one of `:left', `:right' or `nil'. If two adjacent
119 operators have the same precedence, their associativities are compared.
120 If both associativities are `:left' then the left-hand operator is
121 considered to have higher precedence; if both are `:right' then the
122 right-hand operator is considered to have higher precedence. If they're
123 inconsistent or `nil', then an error is reported and the behaviour is as
124 if both were `:left'.")
125 (:method (operator) :left))
126
127;;;--------------------------------------------------------------------------
128;;; Basic operator protocol.
129
130(export 'prefix-operator)
131(defclass prefix-operator ()
132 ()
133 (:documentation
134 "Prefix operator base class.
135
136 Prefix operators are special because they are pushed at a time when the
137 existing topmost operator on the stack may not have its operand
138 available. It is therefore incorrect to attempt to apply any existing
139 operators without careful checking. This class provides a method on
140 `push-operator' which immediately pushes the new operator without
141 inspecting the existing stack."))
142
143(export 'simple-operator)
144(defclass simple-operator ()
145 ((function :initarg :function :reader operator-function)
146 (name :initarg :name :initform "<unnamed operator>"
147 :reader operator-name))
148 (:documentation
149 "A simple operator applies a FUNCTION to arguments when it is applied.
150
151 The precise details of the function are left to subclasses to sort out."))
152
153(export 'simple-unary-operator)
154(defclass simple-unary-operator (simple-operator)
155 ()
156 (:documentation
157 "A unary operator works on the topmost value on the value stack.
158
159 The topmost item is popped, the FUNCTION is applied to it, and the result
160 is pushed back on."))
161
162(export 'simple-binary-operator)
163(defclass simple-binary-operator (simple-operator)
164 ((lprec :initarg :left-precedence :initarg :precedence
165 :reader operator-left-precedence)
166 (rprec :initarg :right-precedence :reader operator-right-precedence)
167 (associativity :initarg :associative :initform :left
168 :reader operator-associativity))
169 (:documentation
170 "A binary operator works on the two topmost values on the value stack.
171
172 The function's arguments are the two topmost items in /reverse/ order --
173 so the topmost item is second. This is usually what you want.
174
175 The left and right precedences are settable independently. Usually (and
176 this is the default) you will set them equal, and use the `:associativity'
177 initarg to determine associativity; however, right-associativity can also
178 be obtained by setting the right-precedence lower than the left. Special
179 effects can be obtained by setting them in other ways. Use your
180 imagination."))
181
182(export 'simple-postfix-operator)
183(defclass simple-postfix-operator (simple-unary-operator)
184 ((lprec :initarg :left-precedence :initarg :precedence
185 :reader operator-left-precedence)
186 (rprec :initarg :right-precedence :reader operator-right-precedence))
187 (:documentation
188 "A postfix operator is applied to a single operand.
189
190 The left and right precedences are settable independently. Usually you
191 will want to set them equal (this is the default) and quite high. Special
192 effects can be obtained by doing other things instead; but note that you
193 will get an incorrect parse if the right precedence is lower than the left
194 precedence of a binary operator because the postfix operator will be
195 applied to the result of the binary operator."))
196
197(export 'simple-prefix-operator)
198(defclass simple-prefix-operator (prefix-operator simple-unary-operator)
199 ((rprec :initarg :precedence :reader operator-right-precedence))
200 (:documentation
201 "A prefix operator is applied to a single operand.
202
203 There is only one precedence value for a prefix operator: the
204 `prefix-operator' superclass arranges that the left precedence is
205 effectively minus infinity."))
206
207(export 'preop)
208(defmacro preop (name (x prec) &body body)
209 `(make-instance 'simple-prefix-operator
210 :name ,name
211 :precedence ,prec
212 :function (lambda (,x) ,@body)))
213
214(export 'postop)
215(defmacro postop (name (x prec &key rprec) &body body)
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 (once-only (name prec rprec assoc)
226 `(make-instance 'simple-binary-operator
227 :name ,name
228 :left-precedence ,prec
229 :right-precedence ,(or rprec prec)
230 :associative ,assoc
231 :function (lambda (,x ,y) ,@body))))
232
233;;;--------------------------------------------------------------------------
234;;; Parentheses.
235
236(defclass parenthesis ()
237 ((tag :initarg :tag :initform nil))
238 (:documentation
239 "Base class for parenthesis operators."))
240
241(export 'open-parenthesis)
242(defclass open-parenthesis (parenthesis prefix-operator) ())
243
244(export 'close-parenthesis)
245(defclass close-parenthesis (parenthesis) ())
246
247(export '(lparen rparen))
248(defun lparen (tag)
249 (make-instance 'open-parenthesis :tag tag))
250(defun rparen (tag)
251 (make-instance 'close-parenthesis :tag tag))
252
253;;;----- That's all, folks --------------------------------------------------