chiark / gitweb /
Major effort to plug slot-name leaks.
[sod] / src / parser / parser-impl.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Parser protocol implementation.
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 ;;; Hairy functions used by parser syntax expanders.
30
31 (declaim (inline %many))
32 (defun %many (update final parser &key (min 0) max)
33   "Helper function for the `many' parser syntax.
34
35    This deals with simple repetition, without separators.  See the parser
36    syntax documentation for details."
37
38   (let ((consumed-any-p nil))
39     (do ((i 0 (1+ i)))
40         ((and max (>= i max)))
41       (multiple-value-bind (value winp consumep) (funcall parser)
42         (when consumep (setf consumed-any-p t))
43         (cond (winp (funcall update value))
44               ((or consumep (< i min))
45                (return-from %many (values value nil consumed-any-p)))
46               (t (return)))))
47     (values (funcall final) t consumed-any-p)))
48
49 (defun %many-sep (update final parser sep &key (min 1) max (commitp t))
50   "Helper function for the `many' parser syntax.
51
52    This deals with the hairy separator and commit stuff.  See the parser
53    syntax documentation for details."
54
55   (let ((consumed-any-p nil)
56         (i 0))
57     (block nil
58       (flet ((sep ()
59                (multiple-value-bind (value winp consumep) (funcall sep)
60                  (when consumep (setf consumed-any-p t))
61                  (unless winp
62                    (if (and (>= i min) (not consumep)) (return)
63                        (return-from %many-sep
64                          (values value nil consumed-any-p))))))
65
66              (main (mustp)
67                (multiple-value-bind (value winp consumep) (funcall parser)
68                  (when consumep (setf consumed-any-p t))
69                  (cond (winp (funcall update value))
70                        ((or mustp consumep (< i min))
71                         (return-from %many-sep
72                           (values value nil consumed-any-p)))
73                        (t (return))))
74                (incf i)))
75
76         (when (eql max 0) (return))
77
78         (main nil)
79
80         (if commitp
81             (loop (when (and max (>= i max)) (return)) (sep) (main t))
82             (loop (sep) (when (and max (>= i max)) (return)) (main nil)))))
83
84     (values (funcall final) t consumed-any-p)))
85
86 ;;;--------------------------------------------------------------------------
87 ;;; Token parser implementation.
88
89 (defmethod parser-at-eof-p ((context token-parser-context))
90   `(eq ,(parser-token-type context) :eof))
91
92 ;;;--------------------------------------------------------------------------
93 ;;; Simple list-based parser; useful for testing.
94
95 (export 'list-parser)
96 (defclass list-parser ()
97   ((var :initarg :var :type symbol :reader parser-var)))
98
99 (defmethod parser-at-eof-p ((context list-parser))
100   `(not ,(parser-var context)))
101
102 (defmethod parser-capture-place ((context list-parser))
103   `,(parser-var context))
104
105 (defmethod parser-restore-place ((context list-parser) place)
106   `(setf ,(parser-var context) ,place))
107
108 (defmethod expand-parser-spec ((context list-parser) parser)
109   (if (atom parser)
110       (expand-parser-form context 'quote (list parser))
111       (call-next-method)))
112
113 (defparse quote (:context (context list-parser) object)
114   `(if (and ,(parser-var context)
115             (eql (car ,(parser-var context)) ',object))
116        (progn (pop ,(parser-var context)) (values ',object t t))
117        (values (list ',object) nil nil)))
118
119 (defparse type (:context (context list-parser) type)
120   `(if (and ,(parser-var context)
121             (typep (car ,(parser-var context)) ',type))
122        (values (pop ,(parser-var context)) t t)
123        (values (list ',type) nil nil)))
124
125 (defmethod parser-places-must-be-released-p ((context list-parser)) nil)
126
127 ;;;--------------------------------------------------------------------------
128 ;;; Simple string-based parser; useful for testing.
129
130 (export 'string-parser)
131 (defclass string-parser (character-parser-context)
132   ((%string :initarg :string :reader parser-string)
133    (index :initarg :index :initform 0 :reader parser-index)
134    (%length :initform (gensym "LEN-") :reader parser-length)))
135
136 (defmethod wrap-parser ((context string-parser) form)
137   (with-slots ((string %string) index (length %length)) context
138     `(let* (,@(unless (symbolp string)
139                 (let ((s string))
140                   (setf string (gensym "STRING-"))
141                   `((,string ,s))))
142             ,@(unless (symbolp index)
143                 (let ((i index))
144                   (setf index (gensym "INDEX-"))
145                   `((,index ,i))))
146               (,length (length ,string)))
147        ,form)))
148
149 (defmethod parser-at-eof-p ((context string-parser))
150   `(>= ,(parser-index context) ,(parser-length context)))
151
152 (defmethod parser-current-char ((context string-parser))
153   `(char ,(parser-string context) ,(parser-index context)))
154
155 (defmethod parser-step ((context string-parser))
156   `(incf ,(parser-index context)))
157
158 (defmethod parser-capture-place ((context string-parser))
159   `,(parser-index context))
160
161 (defmethod parser-restore-place ((context string-parser) place)
162   `(setf ,(parser-index context) ,place))
163
164 (defmethod parser-places-must-be-released-p ((context string-parser)) nil)
165
166 ;;;----- That's all, folks --------------------------------------------------