chiark / gitweb /
src/pset-parse.lisp: Replace `dispatch' by some more elementary functions.
[sod] / src / pset-parse.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Parsing property sets
4 ;;;
5 ;;; (c) 2012 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 (in-package #:sod)
27
28 ;;;--------------------------------------------------------------------------
29 ;;; The expression parser.
30
31 (defun parse-expression (scanner)
32   "Parse and evaluate a simple expression.
33
34    The result is a pair (TYPE . VALUE).  Currently, type types are `:id',
35    `:int', `:string', `:char', `:fragment', `:type'.  If an error prevented a
36    sane value from being produced, the type `:invalid' is returned.
37
38    The syntax of expressions is rather limited at the moment, but more may be
39    added later.
40
41    expression: term | expression `+' term | expression `-' term
42    term: factor | term `*' factor | term `/' factor
43    factor: primary | `+' factor | `-' factor
44    primary: int | id | string | `(' expression `)' | `{' fragment `}'
45      | `<' declspec+ declarator[empty] `>' | `?' lisp-expression
46
47    Only operators for dealing with integers are provided."
48
49   ;; The expression parser works in two stages.  First, the parser proper
50   ;; builds a thunk as its `value'.  If this is successful, then the thunk is
51   ;; invoked to return a property type and value.  Primitive expressions
52   ;; produce thunks which just return their values; operators combine their
53   ;; argument thunks together, evaluating them (or not) on demand.
54
55   (macrolet ((oplambda (&body body)
56                ;; Like `lambda', but (a) always produces a function with no
57                ;; arguments, and (b) captures the current location so that
58                ;; errors are attributed correctly.
59
60                (with-gensyms (floc)
61                  `(let ((,floc (file-location scanner)))
62                     (lambda ()
63                       (with-default-error-location (,floc)
64                         ,@body))))))
65
66     (labels ((want (type thunk)
67                ;; Evaluate THUNK and convert its result to the given TYPE.
68                (multiple-value-bind (ty val) (funcall thunk)
69                  (coerce-property-value val ty type)))
70
71              (int-unop (intop x)
72                ;; Evaluate X to an integer, and apply INTOP to the result,
73                ;; giving another integer.
74                (oplambda (values :int (funcall intop (want :int x)))))
75
76              (int-binop (intop x y)
77                ;; Evaluate X and Y to integers, and apply INTOP to the
78                ;; results, giving another integer.
79                (oplambda
80                  (values :int (funcall intop (want :int x) (want :int y))))))
81
82       (with-parser-context (token-scanner-context :scanner scanner)
83         (when-parse ()
84
85             ;; Parse the expression, producing a thunk.
86             (expr (:nestedp nestedp)
87
88               (lisp (case (token-type scanner)
89
90                       ((:int :id :char :string)
91                        ;; A simple literal.
92                        (let ((type (token-type scanner))
93                              (value (token-value scanner)))
94                          (scanner-step scanner)
95                          (values (lambda () (values type value)) t t)))
96
97                       (#\?
98                        ;; A Lisp s-expression.  Catch and report reader-
99                        ;; errors (though the main parser will probably
100                        ;; end up /very/ confused); delay evaluation for
101                        ;; later.
102                        (handler-case
103                            (let* ((stream (make-scanner-stream scanner))
104                                   (sexp (read stream t)))
105                              (scanner-step scanner)
106                              (values (oplambda (decode-property (eval sexp)))
107                                      t t))
108                          (error (cond)
109                            (scanner-step scanner)
110                            (cerror*-with-location scanner
111                                                   "Lisp `read' error: ~A"
112                                                   cond)
113                            (values #'continue t t))))
114
115                       (#\{
116                        ;; A code fragment.
117                        (let ((fragment (parse-delimited-fragment scanner
118                                                                  #\{ #\})))
119                          (values (lambda () (values :fragment fragment))
120                                  t t)))
121
122                       (#\<
123                        ;; A C type.
124                        (parse (seq (#\<
125                                     (ds (parse-c-type scanner))
126                                     (dc (parse-declarator
127                                          scanner ds
128                                          :kernel (lambda ()
129                                                    (values nil t nil))
130                                          :abstractp t))
131                                     #\>)
132                                 (values (lambda () (values :type (car dc)))
133                                         t t))))
134
135                       (t
136                        ;; Anything else is an error.
137                        (values (list :int :id :char :string #\? #\{ #\<)
138                                nil nil))))
139
140               ;; Binary operators.
141               ((:op #\* binop "*" (x y 70) (int-binop #'* x y))
142                (:op #\/ binop "/" (x y 70)
143                     (oplambda
144                       (let ((x (want :int x)) (y (want :int y)))
145                         (when (zerop y) (error "Division by zero"))
146                         (values :int (floor x y)))))
147                (:op #\+ binop "+" (x y 60) (int-binop #'+ x y))
148                (:op #\- binop "-" (x y 60) (int-binop #'- x y)))
149
150               ;; Prefix operators.
151               ((:op #\+ preop "+" (x 90) (int-unop #'identity x))
152                (:op #\- preop "-" (x 90) (int-unop #'- x))
153                (:op #\( lparen #\)))
154
155               ;; Postfix operators.
156               ((:op (when nestedp #\)) rparen #\))))
157
158           ;; Do the delayed evaluation.  Establish a restart so that we can
159           ;; continue if evaluation fails for some reason.  (The value thunk
160           ;; is expected to report the correct error locations, if it signals
161           ;; conditions.)
162           (restart-case (multiple-value-bind (type value) (funcall it)
163                           (values (cons type value) t t))
164             (continue () (values (cons :invalid nil) t t))))))))
165
166 ;;;--------------------------------------------------------------------------
167 ;;; Parsing property sets.
168
169 (export 'parse-property)
170 (defun parse-property (scanner pset)
171   "Parse a single property using the SCANNER; add it to the PSET."
172   ;; property ::= id `=' expression
173   (with-parser-context (token-scanner-context :scanner scanner)
174     (parse (seq ((name :id) #\= (result (parse-expression scanner)))
175              (let ((type (car result))
176                    (value (cdr result)))
177                (unless (eq type :invalid)
178                  (add-property pset name value
179                                :type type
180                                :location scanner)))))))
181
182 (export 'parse-property-set)
183 (defun parse-property-set (scanner)
184   "Parse an optional property set from the SCANNER and return it."
185   ;; property-set ::= [`[' property-list `]']
186   (with-parser-context (token-scanner-context :scanner scanner)
187     (parse (? (seq (#\[
188                     (pset (many (pset (make-property-set) pset)
189                             (error ()
190                               (parse-property scanner pset)
191                               (skip-until () #\, #\]))
192                             #\,))
193                     #\])
194                 pset)))))
195
196 ;;;----- That's all, folks --------------------------------------------------