chiark / gitweb /
Refactoring more or less complete. Maybe I should test it.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 17 Jul 2013 20:03:27 +0000 (21:03 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 17 Jul 2013 20:03:58 +0000 (21:03 +0100)
14 files changed:
pre-reorg/module-output.lisp
src/c-types-test.lisp
src/class-make-impl.lisp
src/class-make-proto.lisp
src/lexer-impl.lisp
src/lexer-proto.lisp
src/module-output.lisp [new file with mode: 0644]
src/module-parse.lisp
src/module-proto.lisp
src/parser/parser-proto.lisp
src/pset-parse.lisp
src/pset-proto.lisp
src/pset-test.lisp [new file with mode: 0644]
src/sod.asd

index 891ff54fc379d9fbab7d1f422963bce3637e077b..fd690ad6e7f7629d91606b9d7b8c887f6fc264f9 100644 (file)
@@ -28,144 +28,13 @@ (cl:in-package #:sod)
 ;;;--------------------------------------------------------------------------
 ;;; Utilities.
 
 ;;;--------------------------------------------------------------------------
 ;;; Utilities.
 
-(defun banner (title output &key (blank-line-p t))
-  (format output "~&/*----- ~A ~A*/~%"
-         title
-         (make-string (- 77 2 5 1 (length title) 1 2)
-                      :initial-element #\-))
-  (when blank-line-p
-    (terpri output)))
-
-(defun guard-name (filename)
-  "Return a sensible inclusion guard name for FILENAME."
-  (with-output-to-string (guard)
-    (let* ((pathname (make-pathname :name (pathname-name filename)
-                                   :type (pathname-type filename)))
-          (name (namestring pathname))
-          (uscore t))
-      (dotimes (i (length name))
-       (let ((ch (char name i)))
-         (cond ((alphanumericp ch)
-                (write-char (char-upcase ch) guard)
-                (setf uscore nil))
-               ((not uscore)
-                (write-char #\_ guard)
-                (setf uscore t))))))))
-
-;;;--------------------------------------------------------------------------
-;;; Driving output.
-
-(defun guess-output-file (module type)
-  (merge-pathnames (make-pathname :type type :case :common)
-                  (module-name module)))
-
-(defun output-module (module reason stream)
-  (let ((sequencer (make-instance 'sequencer))
-       (stream (if (typep stream 'position-aware-output-stream)
-                   stream
-                   (make-instance 'position-aware-output-stream
-                                  :stream stream
-                                  :file (or (stream-pathname stream)
-                                            #p"<unnamed>")))))
-    (add-output-hooks module reason sequencer)
-    (invoke-sequencer-items sequencer stream)))
-
 ;;;--------------------------------------------------------------------------
 ;;; Main output protocol implementation.
 
 ;;;--------------------------------------------------------------------------
 ;;; Main output protocol implementation.
 
-(defmethod add-output-hooks progn ((module module) reason sequencer)
-  (dolist (item (module-items module))
-    (add-output-hooks item reason sequencer)))
-
-(defmethod add-output-hooks progn
-    ((frag code-fragment-item) reason sequencer)
-  (when (eq reason (code-fragment-reason frag))
-    (dolist (constraint (code-fragment-constraints frag))
-      (add-sequencer-constraint sequencer constraint))
-    (add-sequencer-item-function sequencer (code-fragment-name frag)
-                                (lambda (stream)
-                                  (write (code-fragment frag)
-                                         :stream stream
-                                         :pretty nil
-                                         :escape nil)))))
-
 ;;;--------------------------------------------------------------------------
 ;;; Header output.
 
 ;;;--------------------------------------------------------------------------
 ;;; Header output.
 
-(defmethod add-output-hooks progn
-    ((module module) (reason (eql :h)) sequencer)
-  (sequence-output (stream sequencer)
-    :constraint (:prologue
-                (:guard :start)
-                (:typedefs :start) :typedefs (:typedefs :end)
-                (:includes :start) :includes (:includes :end)
-                (:classes :start) :classes (:classes :end)
-                (:guard :end)
-                :epilogue)
-
-    (:prologue
-     (format stream "~
-/* -*-c-*-
- *
- * Header file generated by SOD for ~A
- */~2%"
-            (namestring (module-name module))))
-
-    ((:guard :start)
-     (format stream "~
-#ifndef ~A
-#define ~:*~A
-
-#ifdef __cplusplus
-  extern \"C\" {
-#endif~2%"
-            (or (get-property (module-pset module) :guard :id)
-                (guard-name (or (stream-pathname stream)
-                                (guess-output-file module "H"))))))
-    ((:guard :end)
-     (banner "That's all, folks" stream)
-     (format stream "~
-#ifdef __cplusplus
-  }
-#endif
-
-#endif~%"))
-
-    ((:typedefs :start)
-     (banner "Forward type declarations" stream))
-    ((:typedefs :end)
-     (terpri stream))
-
-    ((:includes :start)
-     (banner "External header files" stream))
-    ((:includes :end)
-     (terpri stream))))
-
 ;;;--------------------------------------------------------------------------
 ;;; Source output.
 
 ;;;--------------------------------------------------------------------------
 ;;; Source output.
 
-(defmethod add-output-hooks progn
-    ((module module) (reason (eql :c)) sequencer)
-  (sequence-output (stream sequencer)
-    :constraint (:prologue
-                (:includes :start) :includes (:includes :end)
-                (:classes :start) (:classes :end)
-                :epilogue)
-
-    (:prologue
-     (format stream "~
-/* -*-c-*-
- *
- * Implementation file generated by SOD for ~A
- */~2%"
-            (namestring (module-name module))))
-
-    (:epilogue
-     (banner "That's all, folks" stream :blank-line-p nil))
-
-    ((:includes :start)
-     (banner "External header files" stream))
-    ((:includes :end)
-     (terpri stream))))
-
 ;;;----- That's all, folks --------------------------------------------------
 ;;;----- That's all, folks --------------------------------------------------
index 16e41ce6be6f6264ceafd774f9789d32fe910ab6..0eadfe6ccb75efb9245a1a52e0f75a09ca246ac3 100644 (file)
@@ -249,40 +249,51 @@ (def-test-method commentify-non-recursive ((test c-types-test) :run nil)
 ;;;--------------------------------------------------------------------------
 ;;; Parsing.
 
 ;;;--------------------------------------------------------------------------
 ;;; Parsing.
 
-(def-test-method parse-c-type ((test c-types-test) :run nil)
-  (flet ((check (string c-type name)
-          (let* ((char-scanner (make-string-scanner string))
-                 (scanner (make-instance 'sod-token-scanner
-                                         :char-scanner char-scanner
-                                         :filename "<none>")))
-            (with-parser-context (token-scanner-context :scanner scanner)
-              (define-module ("<temporary>" :truename nil :location scanner)
-                (multiple-value-bind (result winp consumedp)
-                    (parse (seq ((ds (parse-c-type scanner))
-                                 (dc (parse-declarator scanner ds))
-                                 :eof)
-                             dc))
-                  (declare (ignore consumedp))
-                  (cond ((null c-type)
-                         (assert-false winp))
-                        (t
-                         (assert-true winp)
-                         (unless (eq c-type t)
-                           (assert-cteqp (car result) c-type))
-                         (unless (eq name t)
-                           (assert-equal (cdr result) name))))))))))
-
-    (check "int x" (c-type int) "x")
-    (check "int long unsigned long y" (c-type unsigned-long-long) "y")
-    (check "int long int x" nil nil)
-    (check "float v[69][42]" (c-type ([] float "69" "42")) "v")
-    (check "const char *const tab[]"
-          (c-type ([] (* (char :const) :const) ""))
-          "tab")
-    (check "void (*signal(int, void (*)(int)))(int)"
-          (c-type (func (* (func void (nil int)))
-                        (nil int)
-                        (nil (* (func void (nil int))))))
-          "signal")))
+(defun check-c-type-parse (string c-type name)
+  (let* ((char-scanner (make-string-scanner string))
+        (scanner (make-instance 'sod-token-scanner
+                                :char-scanner char-scanner
+                                :filename "<none>")))
+    (with-parser-context (token-scanner-context :scanner scanner)
+      (define-module ("<temporary>" :truename nil :location scanner)
+       (multiple-value-bind (result winp consumedp)
+           (parse (seq ((ds (parse-c-type scanner))
+                        (dc (parse-declarator scanner ds))
+                        :eof)
+                    dc))
+         (declare (ignore consumedp))
+         (cond ((null c-type)
+                (assert-false winp))
+               (t
+                (assert-true winp)
+                (unless (eq c-type t)
+                  (assert-cteqp (car result) c-type))
+                (unless (eq name t)
+                  (assert-equal (cdr result) name)))))))))
+
+(def-test-method parse-simple ((test c-types-test) :run nil)
+  (check-c-type-parse "int x" (c-type int) "x"))
+
+(def-test-method parse-hairy-declspec ((test c-types-test) :run nil)
+  (check-c-type-parse "int long unsigned long y"
+                     (c-type unsigned-long-long) "y"))
+
+(def-test-method parse-bogus-declspec ((test c-types-test) :run nil)
+  (check-c-type-parse "int long int x" nil nil))
+
+(def-test-method parse-array ((test c-types-test) :run nil)
+  (check-c-type-parse "float v[69][42]" (c-type ([] float "69" "42")) "v"))
+
+(def-test-method parse-array-of-pointers ((test c-types-test) :run nil)
+  (check-c-type-parse "const char *const tab[]"
+                     (c-type ([] (* (char :const) :const) ""))
+                     "tab"))
+
+(def-test-method parse-hairy-function-pointer ((test c-types-test) :run nil)
+  (check-c-type-parse "void (*signal(int, void (*)(int)))(int)"
+                     (c-type (func (* (func void (nil int)))
+                                   (nil int)
+                                   (nil (* (func void (nil int))))))
+                     "signal")))
 
 ;;;----- That's all, folks --------------------------------------------------
 
 ;;;----- That's all, folks --------------------------------------------------
index 4470416e63306414a98654d929159edea90413ca..ae65392185aed89338ef05891dc1123f7e7bb68d 100644 (file)
@@ -87,8 +87,7 @@ (defmethod make-sod-slot
                               :location (file-location location)
                               :pset pset)))
       (with-slots (slots) class
                               :location (file-location location)
                               :pset pset)))
       (with-slots (slots) class
-       (setf slots (append slots (list slot))))
-      (check-unused-properties pset))))
+       (setf slots (append slots (list slot)))))))
 
 (defmethod shared-initialize :after ((slot sod-slot) slot-names &key pset)
   "This method does nothing.
 
 (defmethod shared-initialize :after ((slot sod-slot) slot-names &key pset)
   "This method does nothing.
@@ -112,8 +111,7 @@ (defmethod make-sod-instance-initializer
                         (file-location location))))
       (with-slots (instance-initializers) class
        (setf instance-initializers
                         (file-location location))))
       (with-slots (instance-initializers) class
        (setf instance-initializers
-             (append instance-initializers (list initializer))))
-      (check-unused-properties pset))))
+             (append instance-initializers (list initializer)))))))
 
 (defmethod make-sod-class-initializer
     ((class sod-class) nick name value-kind value-form pset
 
 (defmethod make-sod-class-initializer
     ((class sod-class) nick name value-kind value-form pset
@@ -126,8 +124,7 @@ (defmethod make-sod-class-initializer
                         (file-location location))))
       (with-slots (class-initializers) class
        (setf class-initializers
                         (file-location location))))
       (with-slots (class-initializers) class
        (setf class-initializers
-             (append class-initializers (list initializer))))
-      (check-unused-properties pset))))
+             (append class-initializers (list initializer)))))))
 
 (defmethod make-sod-initializer-using-slot
     ((class sod-class) (slot sod-slot)
 
 (defmethod make-sod-initializer-using-slot
     ((class sod-class) (slot sod-slot)
@@ -163,8 +160,7 @@ (defmethod make-sod-message
                                  :location (file-location location)
                                  :pset pset)))
       (with-slots (messages) class
                                  :location (file-location location)
                                  :pset pset)))
       (with-slots (messages) class
-       (setf messages (append messages (list message))))
-      (check-unused-properties pset))))
+       (setf messages (append messages (list message)))))))
 
 (defmethod shared-initialize :after
     ((message sod-message) slot-names &key pset)
 
 (defmethod shared-initialize :after
     ((message sod-message) slot-names &key pset)
@@ -189,8 +185,7 @@ (defmethod make-sod-method
                                                  type body pset
                                                  (file-location location))))
       (with-slots (methods) class
                                                  type body pset
                                                  (file-location location))))
       (with-slots (methods) class
-       (setf methods (append methods (list method)))))
-    (check-unused-properties pset)))
+       (setf methods (append methods (list method)))))))
 
 (defmethod make-sod-method-using-message
     ((message sod-message) (class sod-class) type body pset location)
 
 (defmethod make-sod-method-using-message
     ((message sod-message) (class sod-class) type body pset location)
index 0a633de41351014d0bc5d697a9a2238de6ac3d09..2b4463a3c04a5bbb88a82c6915c1b5e6f6dc0de8 100644 (file)
@@ -41,9 +41,7 @@ (defun make-sod-class (name superclasses pset &optional location)
    `shared-initialize'.
 
    Minimal sanity checking is done during class construction; most of it is
    `shared-initialize'.
 
    Minimal sanity checking is done during class construction; most of it is
-   left for `finalize-sod-class' to do (via `check-sod-class').
-
-   Unused properties in PSET are diagnosed as errors."
+   left for `finalize-sod-class' to do (via `check-sod-class')."
 
   (with-default-error-location (location)
     (let* ((pset (property-set pset))
 
   (with-default-error-location (location)
     (let* ((pset (property-set pset))
@@ -53,7 +51,6 @@ (defun make-sod-class (name superclasses pset &optional location)
                                 :superclasses superclasses
                                 :location (file-location location)
                                 :pset pset)))
                                 :superclasses superclasses
                                 :location (file-location location)
                                 :pset pset)))
-      (check-unused-properties pset)
       class)))
 
 (export 'guess-metaclass)
       class)))
 
 (export 'guess-metaclass)
@@ -78,9 +75,7 @@ (defgeneric make-sod-slot (class name type pset &optional location)
    to `sod-slot') to choose a (CLOS) class to instantiate.  The slot is then
    constructed by `make-instance' passing the arguments as initargs; further
    behaviour is left to the standard CLOS instance construction protocol; for
    to `sod-slot') to choose a (CLOS) class to instantiate.  The slot is then
    constructed by `make-instance' passing the arguments as initargs; further
    behaviour is left to the standard CLOS instance construction protocol; for
-   example, `sod-slot' defines an `:after'-method on `shared-initialize'.
-
-   Unused properties on PSET are diagnosed as errors."))
+   example, `sod-slot' defines an `:after'-method on `shared-initialize'."))
 
 (export 'make-sod-instance-initializer)
 (defgeneric make-sod-instance-initializer
 
 (export 'make-sod-instance-initializer)
 (defgeneric make-sod-instance-initializer
@@ -93,9 +88,7 @@ (defgeneric make-sod-instance-initializer
    construction process.  The default method looks up the slot using
    `find-instance-slot-by-name', calls `make-sod-initializer-using-slot' to
    actually make the initializer object, and adds it to the appropriate list
    construction process.  The default method looks up the slot using
    `find-instance-slot-by-name', calls `make-sod-initializer-using-slot' to
    actually make the initializer object, and adds it to the appropriate list
-   in CLASS.
-
-   Unused properties on PSET are diagnosed as errors."))
+   in CLASS."))
 
 (export 'make-sod-class-initializer)
 (defgeneric make-sod-class-initializer
 
 (export 'make-sod-class-initializer)
 (defgeneric make-sod-class-initializer
@@ -108,9 +101,7 @@ (defgeneric make-sod-class-initializer
    construction process.  The default method looks up the slot using
    `find-class-slot-by-name', calls `make-sod-initializer-using-slot' to
    actually make the initializer object, and adds it to the appropriate list
    construction process.  The default method looks up the slot using
    `find-class-slot-by-name', calls `make-sod-initializer-using-slot' to
    actually make the initializer object, and adds it to the appropriate list
-   in CLASS.
-
-   Unused properties on PSET are diagnosed as errors."))
+   in CLASS."))
 
 (export 'make-sod-initializer-using-slot)
 (defgeneric make-sod-initializer-using-slot
 
 (export 'make-sod-initializer-using-slot)
 (defgeneric make-sod-initializer-using-slot
@@ -150,9 +141,7 @@ (defgeneric make-sod-message (class name type pset &optional location)
    then constructed by `make-instance' passing the arguments as initargs;
    further behaviour is left to the standard CLOS instance construction
    protocol; for example, `sod-message' defines an `:after'-method on
    then constructed by `make-instance' passing the arguments as initargs;
    further behaviour is left to the standard CLOS instance construction
    protocol; for example, `sod-message' defines an `:after'-method on
-   `shared-initialize'.
-
-   Unused properties on PSET are diagnosed as errors."))
+   `shared-initialize'."))
 
 (export 'make-sod-method)
 (defgeneric make-sod-method
 
 (export 'make-sod-method)
 (defgeneric make-sod-method
@@ -168,9 +157,7 @@ (defgeneric make-sod-method
    invokes `make-sod-method-using-message' to make the method object, and
    then adds the method to the class's list of methods.  This split allows
    the message class to intervene in the class selection process, for
    invokes `make-sod-method-using-message' to make the method object, and
    then adds the method to the class's list of methods.  This split allows
    the message class to intervene in the class selection process, for
-   example.
-
-   Unused properties on PSET are diagnosed as errors."))
+   example."))
 
 (export 'make-sod-method-using-message)
 (defgeneric make-sod-method-using-message
 
 (export 'make-sod-method-using-message)
 (defgeneric make-sod-method-using-message
index f4745909a5233786e6568cc2b8266654edd28c42..6fc6fccd09bc0be756b719dfdd4ec3e96b4106bc 100644 (file)
@@ -52,6 +52,41 @@ (defun show-char (stream char &optional colonp atsignp)
         (format stream "`~C'" char))
        (t (format stream "<~(~:C~)>" char))))
 
         (format stream "`~C'" char))
        (t (format stream "<~(~:C~)>" char))))
 
+(defun skip-until (scanner token-types &key keep-end)
+  "This is the implementation of the `skip-until' parser."
+  (do ((consumedp nil t))
+      ((member (token-type scanner) token-types)
+       (unless keep-end (scanner-step scanner))
+       (values nil t (or keep-end consumedp)))
+    (when (scanner-at-eof-p scanner)
+      (return (values token-types nil consumedp)))
+    (scanner-step scanner)))
+
+(defun parse-error-recover (scanner parser recover)
+  "This is the implementation of the `error' parser."
+  (multiple-value-bind (result win consumedp) (funcall parser)
+    (cond ((or win (and (not consumedp) (scanner-at-eof-p scanner)))
+          ;; If we succeeded then there's nothing for us to do here.  On the
+          ;; other hand, if we failed, didn't consume any tokens, and we're
+          ;; at end-of-file, then there's not much hope of making onward
+          ;; progress, so in this case we propagate the failure rather than
+          ;; trying to recover.  And we assume that the continuation will
+          ;; somehow arrange to report the problem, and avoid inundating the
+          ;; user with error reports.
+          (values result win consumedp))
+         (t
+          ;; Now we have to do some kind of sensible error recovery.  The
+          ;; important thing to do here is to make sure that we make some
+          ;; progress.  If we consumed any tokens then we're fine, and we'll
+          ;; just try the provided recovery strategy.  Otherwise, if we're
+          ;; not at EOF, then we can ensure progress by discarding the
+          ;; current token.  Finally, if we are at EOF then our best bet is
+          ;; simply to propagate the current failure back to the caller, but
+          ;; we handled that case above.
+          (syntax-error scanner result :continuep t)
+          (unless consumedp (scanner-step scanner))
+          (funcall recover)))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Token scanning.
 
 ;;;--------------------------------------------------------------------------
 ;;; Token scanning.
 
index e72152e2c9d1afafd1576c3a7e8067670d198317..af2e535e207fa607ae6cb069aae489336fc44d8f 100644 (file)
@@ -56,6 +56,7 @@ (defun syntax-error (scanner expected &key (continuep t))
                 (format nil "~/sod::show-char/" type)
                 (case type
                   (:id (format nil "<identifier~@[ `~A'~]>" value))
                 (format nil "~/sod::show-char/" type)
                 (case type
                   (:id (format nil "<identifier~@[ `~A'~]>" value))
+                  (:int "<integer-literal>")
                   (:string "<string-literal>")
                   (:char "<character-literal>")
                   (:eof "<end-of-file>")
                   (:string "<string-literal>")
                   (:char "<character-literal>")
                   (:eof "<end-of-file>")
@@ -95,6 +96,36 @@ (defun lexer-error (char-scanner expected consumedp)
                (scanner-current-char char-scanner))
           (and consumedp (file-location char-scanner))))
 
                (scanner-current-char char-scanner))
           (and consumedp (file-location char-scanner))))
 
+(defparse skip-until (:context (context token-scanner-context)
+                     (&key (keep-end nil keep-end-p))
+                     &rest token-types)
+  "Discard tokens until we find one listed in TOKEN-TYPES.
+
+   If KEEP-END is true then retain the found token for later; otherwise
+   discard it.  KEEP-END defaults to true if multiple TOKEN-TYPES are given;
+   otherwise false.  If end-of-file is encountered then the indicator list is
+   simply the list of TOKEN-TYPES; otherwise the result is `nil'."
+  `(skip-until ,(parser-scanner context)
+              (list ,@token-types)
+              :keep-end ,(if keep-end-p keep-end
+                             (> (length token-types) 1))))
+
+(defparse error (:context (context token-scanner-context)
+                (&key) sub &optional (recover t))
+  "Try to parse SUB; if it fails then report an error, and parse RECOVER.
+
+   This is the main way to recover from errors and continue parsing.  Even
+   then, it's not especially brilliant.
+
+   If the SUB parser succeeds then just propagate its result: it's like we
+   were never here.  Otherwise, try to recover in a sensible way so we can
+   continue parsing.  The details of this recovery are subject to change, but
+   the final action is generally to invoke the RECOVER parser and return its
+   result."
+  `(parse-error-recover ,(parser-scanner context)
+                       (parser () ,sub)
+                       (parser () ,recover)))
+
 ;;;--------------------------------------------------------------------------
 ;;; Lexical analysis utilities.
 
 ;;;--------------------------------------------------------------------------
 ;;; Lexical analysis utilities.
 
diff --git a/src/module-output.lisp b/src/module-output.lisp
new file mode 100644 (file)
index 0000000..b093b82
--- /dev/null
@@ -0,0 +1,183 @@
+;;; -*-lisp-*-
+;;;
+;;; Output for modules
+;;;
+;;; (c) 2013 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Sensble Object Design, an object system for C.
+;;;
+;;; SOD is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; SOD is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with SOD; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(cl:in-package #:sod)
+
+;;;--------------------------------------------------------------------------
+;;; Utilities.
+
+(export 'banner)
+(defun banner (title output &key (blank-line-p t))
+  "Write a banner to the OUTPUT stream, starting a new section called TITLE.
+
+   If BLANK-LINE-P is false, then leave a blank line after the banner.  (This
+   is useful for a final banner at the end of a file.)"
+  (format output "~&/*----- ~A ~A*/~%"
+         title
+         (make-string (- 77 2 5 1 (length title) 1 2)
+                      :initial-element #\-))
+  (when blank-line-p
+    (terpri output)))
+
+(export 'guard-name)
+(defun guard-name (filename)
+  "Return a sensible inclusion guard name for FILENAME."
+  (with-output-to-string (guard)
+    (let* ((pathname (make-pathname :name (pathname-name filename)
+                                   :type (pathname-type filename)))
+          (name (namestring pathname))
+          (uscore t))
+      (dotimes (i (length name))
+       (let ((ch (char name i)))
+         (cond ((alphanumericp ch)
+                (write-char (char-upcase ch) guard)
+                (setf uscore nil))
+               ((not uscore)
+                (write-char #\_ guard)
+                (setf uscore t))))))))
+
+(defun guess-output-file (module type)
+  "Guess the filename to use for a file TYPE, generated from MODULE.
+
+   Here, TYPE is a filetype string.  The result is returned as a pathname."
+  (merge-pathnames (make-pathname :type type :case :common)
+                  (module-name module)))
+
+;;;--------------------------------------------------------------------------
+;;; Main output interface.
+
+(export 'output-module)
+(defun output-module (module reason stream)
+  "Write the MODULE to STREAM, giving the output machinery the REASON.
+
+   This is the top-level interface for producing output."
+  (let ((sequencer (make-instance 'sequencer))
+       (stream (if (typep stream 'position-aware-output-stream)
+                   stream
+                   (make-instance 'position-aware-output-stream
+                                  :stream stream
+                                  :file (or (stream-pathname stream)
+                                            #p"<unnamed>")))))
+    (hook-output module reason sequencer)
+    (invoke-sequencer-items sequencer stream)))
+
+;;;--------------------------------------------------------------------------
+;;; Output implementation.
+
+(defmethod hook-output progn ((module module) reason sequencer)
+
+  ;; Ask the module's items to sequence themselves.
+  (dolist (item (module-items module))
+    (hook-output item reason sequencer)))
+
+(defmethod hook-output progn ((frag code-fragment-item) reason sequencer)
+
+  ;; Output fragments when their reasons are called up.
+  (when (eq reason (code-fragment-reason frag))
+    (dolist (constraint (code-fragment-constraints frag))
+      (add-sequencer-constraint sequencer constraint))
+    (add-sequencer-item-function sequencer (code-fragment-name frag)
+                                (lambda (stream)
+                                  (write (code-fragment frag)
+                                         :stream stream
+                                         :pretty nil
+                                         :escape nil)))))
+
+(defmethod hook-output progn ((module module) (reason (eql :h)) sequencer)
+  (sequence-output (stream sequencer)
+
+    :constraint
+    (:prologue
+     (:guard :start)
+     (:typedefs :start) :typedefs (:typedefs :end)
+     (:includes :start) :includes (:includes :end)
+     (:classes :start) :classes (:classes :end)
+     (:guard :end)
+     :epilogue)
+
+    (:prologue
+     (format stream "~
+/* -*-c-*-
+ *
+ * Header file generated by SOD for ~A
+ */~2%"
+            (namestring (module-name module))))
+
+    ((:guard :start)
+     (format stream "~
+#ifndef ~A
+#define ~:*~A
+
+#ifdef __cplusplus
+  extern \"C\" {
+#endif~2%"
+            (or (get-property (module-pset module) :guard :id)
+                (guard-name (or (stream-pathname stream)
+                                (guess-output-file module "H"))))))
+    ((:guard :end)
+     (banner "That's all, folks" stream)
+     (format stream "~
+#ifdef __cplusplus
+  }
+#endif
+
+#endif~%"))
+
+    ((:typedefs :start)
+     (banner "Forward type declarations" stream))
+    ((:typedefs :end)
+     (terpri stream))
+
+    ((:includes :start)
+     (banner "External header files" stream))
+    ((:includes :end)
+     (terpri stream))))
+
+(defmethod hook-output progn ((module module) (reason (eql :c)) sequencer)
+  (sequence-output (stream sequencer)
+
+    :constraint
+    (:prologue
+     (:includes :start) :includes (:includes :end)
+     (:classes :start) (:classes :end)
+     :epilogue)
+
+    (:prologue
+     (format stream "~
+/* -*-c-*-
+ *
+ * Implementation file generated by SOD for ~A
+ */~2%"
+            (namestring (module-name module))))
+
+    (:epilogue
+     (banner "That's all, folks" stream :blank-line-p nil))
+
+    ((:includes :start)
+     (banner "External header files" stream))
+    ((:includes :end)
+     (terpri stream))))
+
+;;;----- That's all, folks --------------------------------------------------
index 6fb6be800b4a813e461446c8893dd69b41a2a75e..5d26760ebd883710ff4c813788959432d2bc84fe 100644 (file)
@@ -32,9 +32,9 @@ (export 'module)
 
 ;;; Type names.
 
 
 ;;; Type names.
 
-(define-pluggable-parser module typename (scanner)
-  ;; `typename' ID ( `,' ID )* `;'
-
+(define-pluggable-parser module typename (scanner pset)
+  ;; `typename' id ( `,' id )* `;'
+  (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (and "typename"
                (skip-many (:min 1)
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (and "typename"
                (skip-many (:min 1)
@@ -49,9 +49,12 @@ (define-pluggable-parser module typename (scanner)
 
 ;;; Fragments.
 
 
 ;;; Fragments.
 
-(define-pluggable-parser module code (scanner)
-  ;; `code' ID `:' ID [ CONSTRAINTS ] `{' C-FRAGMENT `}'
-
+(define-pluggable-parser module code (scanner pset)
+  ;; `code' id `:' id [constraints] `{' c-fragment `}'
+  ;;
+  ;; constrains ::= `[' constraint-list `]'
+  ;; constraint ::= id+
+  (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("code"
                 (reason :id)
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("code"
                 (reason :id)
@@ -92,19 +95,23 @@   (define-module (pathname :location location :truename truename)
                                     :char-scanner char-scanner)))
        (with-default-error-location (scanner)
          (with-parser-context (token-scanner-context :scanner scanner)
                                     :char-scanner char-scanner)))
        (with-default-error-location (scanner)
          (with-parser-context (token-scanner-context :scanner scanner)
-           (parse (skip-many () (plug module scanner)))))))))
-
-(define-pluggable-parser module test (scanner)
-  ;; `demo' STRING `;'
-
+           (parse (skip-many ()
+                    (seq ((pset (parse-property-set scanner))
+                          (nil (error ()
+                                 (plug module scanner pset))))
+                      (check-unused-properties pset))))))))))
+
+(define-pluggable-parser module test (scanner pset)
+  ;; `demo' string `;'
+  (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("demo" (string :string) #\;)
             (format t ";; DEMO ~S~%" string)))))
 
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("demo" (string :string) #\;)
             (format t ";; DEMO ~S~%" string)))))
 
-(define-pluggable-parser module file (scanner)
-  ;; `import' STRING `;'
-  ;; `load' STRING `;'
-
+(define-pluggable-parser module file (scanner pset)
+  ;; `import' string `;'
+  ;; `load' string `;'
+  (declare (ignore pset))
   (flet ((common (name type what thunk)
           (find-file scanner
                      (merge-pathnames name
   (flet ((common (name type what thunk)
           (find-file scanner
                      (merge-pathnames name
@@ -138,9 +145,9 @@ (define-pluggable-parser module file (scanner)
 
 ;;; Lisp escape.
 
 
 ;;; Lisp escape.
 
-(define-pluggable-parser module lisp (scanner)
+(define-pluggable-parser module lisp (scanner pset)
   ;; `lisp' s-expression `;'
   ;; `lisp' s-expression `;'
-
+  (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ((sexp (if (and (eql (token-type scanner) :id)
                                (string= (token-value scanner) "lisp"))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ((sexp (if (and (eql (token-type scanner) :id)
                                (string= (token-value scanner) "lisp"))
@@ -155,11 +162,13 @@ (define-pluggable-parser module lisp (scanner)
 ;;;--------------------------------------------------------------------------
 ;;; Class declarations.
 
 ;;;--------------------------------------------------------------------------
 ;;; Class declarations.
 
-(defun parse-class-body (scaner pset name supers)
+(defun parse-class-body (scanner pset name supers)
   ;; class-body ::= `{' class-item* `}'
   ;; class-body ::= `{' class-item* `}'
+  ;;
+  ;; class-item ::= property-set raw-class-item
   (with-parser-context (token-scanner-context :scanner scanner)
     (make-class-type name)
   (with-parser-context (token-scanner-context :scanner scanner)
     (make-class-type name)
-    (let* ((class (make-sod-class name (mapcat #'find-sod-class supers)
+    (let* ((class (make-sod-class name (mapcar #'find-sod-class supers)
                                  pset scanner))
           (nick (sod-class-nickname class)))
 
                                  pset scanner))
           (nick (sod-class-nickname class)))
 
@@ -180,14 +189,12 @@ (defun parse-class-body (scaner pset name supers)
                              (if name-b (cons name-a name-b)
                                  name-a)))))
 
                              (if name-b (cons name-a name-b)
                                  name-a)))))
 
-              ;; class-item ::= [property-set] raw-class-item
-              ;;
-
               (parse-message-item (sub-pset type name)
                 ;; message-item ::=
                 ;;     declspec+ declarator -!- (method-body | `;')
                 (make-sod-message class name type sub-pset scanner)
               (parse-message-item (sub-pset type name)
                 ;; message-item ::=
                 ;;     declspec+ declarator -!- (method-body | `;')
                 (make-sod-message class name type sub-pset scanner)
-                (parse (or #\; (parse-method-item nil type nick name))))
+                (parse (or #\; (parse-method-item sub-pset
+                                                  type nick name))))
 
               (parse-method-item (sub-pset type sub-nick name)
                 ;; method-item ::=
 
               (parse-method-item (sub-pset type sub-nick name)
                 ;; method-item ::=
@@ -226,7 +233,7 @@ (defun parse-class-body (scaner pset name supers)
                               (when init
                                 (make-sod-instance-initializer
                                  class nick name (car init) (cdr init)
                               (when init
                                 (make-sod-instance-initializer
                                  class nick name (car init) (cdr init)
-                                 nil scanner)))
+                                 sub-pset scanner)))
                             (skip-many ()
                               (seq (#\,
                                     (ds (parse-declarator scanner
                             (skip-many ()
                               (seq (#\,
                                     (ds (parse-declarator scanner
@@ -238,7 +245,7 @@ (defun parse-class-body (scaner pset name supers)
                                   (make-sod-instance-initializer
                                    class nick (cdr ds)
                                    (car init) (cdr init)
                                   (make-sod-instance-initializer
                                    class nick (cdr ds)
                                    (car init) (cdr init)
-                                   nil scanner))))
+                                   sub-pset scanner))))
                             #\;)))
 
               (parse-initializer-item (sub-pset constructor)
                             #\;)))
 
               (parse-initializer-item (sub-pset constructor)
@@ -289,14 +296,14 @@ (defun parse-class-body (scaner pset name supers)
                 ;; Most of the above begin with declspecs and a declarator
                 ;; (which might be dotted).  So we parse that here and
                 ;; dispatch based on what we find.
                 ;; Most of the above begin with declspecs and a declarator
                 ;; (which might be dotted).  So we parse that here and
                 ;; dispatch based on what we find.
-                (parse (or (peek
+                (parse (or (plug class-item scanner class sub-pset)
+                           (peek
                             (seq ((ds (parse-c-type scanner))
                                   (dc (parse-maybe-dotted-declarator ds))
                             (seq ((ds (parse-c-type scanner))
                                   (dc (parse-maybe-dotted-declarator ds))
-                                  (result (class-item-dispatch sub-pset
-                                                               ds
-                                                               (car dc)
-                                                               (cdr dc))))
-                              result))
+                                  (nil (class-item-dispatch sub-pset
+                                                            ds
+                                                            (car dc)
+                                                            (cdr dc))))))
                            (and "class"
                                 (parse-initializer-item
                                  sub-pset
                            (and "class"
                                 (parse-initializer-item
                                  sub-pset
@@ -305,16 +312,19 @@ (defun parse-class-body (scaner pset name supers)
                             sub-pset
                             #'make-sod-instance-initializer)))))
 
                             sub-pset
                             #'make-sod-instance-initializer)))))
 
-       (parse (and #\{
-                   (skip-many ()
-                     (seq ((sub-pset (? (parse-property-set)))
-                           (nil (parse-raw-class-item sub-pset)))))
-                   #\}))))))
-
-(define-pluggable-parser module class (scanner)
+       (parse (seq (#\{
+                    (nil (skip-many ()
+                           (seq ((sub-pset (parse-property-set scanner))
+                                 (nil (error ()
+                                             (parse-raw-class-item sub-pset))))
+                             (check-unused-properties sub-pset))))
+                    #\})
+                (finalize-sod-class class)
+                (add-to-module *module* class)))))))
+
+(define-pluggable-parser module class (scanner pset)
   ;; `class' id [`:' id-list] class-body
   ;; `class' id `;'
   ;; `class' id [`:' id-list] class-body
   ;; `class' id `;'
-
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("class"
                 (name :id)
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("class"
                 (name :id)
@@ -326,14 +336,4 @@ (define-pluggable-parser module class (scanner)
                                      scanner
                                      pset name supers)))))))))))
 
                                      scanner
                                      pset name supers)))))))))))
 
-
-
-
-    (parse (seq ("class"
-                (name :id)
-                (supers (? (seq (#\: (supers (list (:min 1) :id #\,)))
-                                supers)))
-                #\{
-                
-
 ;;;----- That's all, folks --------------------------------------------------
 ;;;----- That's all, folks --------------------------------------------------
index 93b4f689e311d1b75a84b9f8b490bd680f36504f..28af7bd055e528d22dcf3c7e14272e1c3c83519a 100644 (file)
@@ -125,7 +125,7 @@ (defgeneric add-to-module (module item)
   (:documentation
    "Add ITEM to the MODULE's list of accumulated items.
 
   (:documentation
    "Add ITEM to the MODULE's list of accumulated items.
 
-   The module items participate in the `module-import' and `add-output-hooks'
+   The module items participate in the `module-import' and `hook-output'
    protocols."))
 
 (export 'finalize-module)
    protocols."))
 
 (export 'finalize-module)
index f60e4254ecd3a80f4095b3f2bb7544249229790d..4242dfef0340cad9a94aef1a1770611c7677953c 100644 (file)
@@ -124,10 +124,6 @@ (defmacro defparse (name bvl &body body)
    body FORMs. The BVL is a destructuring lambda-list to be applied to the
    tail of the form.  The body forms are enclosed in a block called NAME.
 
    body FORMs. The BVL is a destructuring lambda-list to be applied to the
    tail of the form.  The body forms are enclosed in a block called NAME.
 
-   Within the FORMs, a function `expand' is available: it takes a parser
-   specifier as its argument and returns its expansion in the parser's
-   context.
-
    If the :context key is provided, then the parser form is specialized on a
    particular class of parser contexts SPEC; specialized expanders take
    priority over less specialized or unspecialized expanders -- so you can
    If the :context key is provided, then the parser form is specialized on a
    particular class of parser contexts SPEC; specialized expanders take
    priority over less specialized or unspecialized expanders -- so you can
@@ -457,8 +453,7 @@ (defparse many ((acc init update
        (,func (lambda (,new)
                (declare (ignorable ,new))
                (setf ,accvar ,update))
        (,func (lambda (,new)
                (declare (ignorable ,new))
                (setf ,accvar ,update))
-             (lambda ()
-               ,final)
+             (lambda () ,final)
              (parser () ,parser)
              ,@(and sepp (list `(parser () ,sep)))
              ,@(and minp `(:min ,min))
              (parser () ,parser)
              ,@(and sepp (list `(parser () ,sep)))
              ,@(and minp `(:min ,min))
index 0bc4680072ce2f5c607dd533b720ff30d8178f66..ff595516ced3b8c83de9776b9baea5425a1e9a70 100644 (file)
@@ -130,13 +130,15 @@ (defun parse-property (scanner pset)
 (export 'parse-property-set)
 (defun parse-property-set (scanner)
   "Parse an optional property set from the SCANNER and return it."
 (export 'parse-property-set)
 (defun parse-property-set (scanner)
   "Parse an optional property set from the SCANNER and return it."
-  ;; property-set ::= `[' property-list `]'
+  ;; property-set ::= [`[' property-list `]']
   (with-parser-context (token-scanner-context :scanner scanner)
   (with-parser-context (token-scanner-context :scanner scanner)
-    (parse (seq (#\[
-                (pset (many (pset (make-property-set) pset)
-                        (parse-property scanner pset)
-                        #\,))
-                #\])
-            pset))))
+    (parse (? (seq (#\[
+                   (pset (many (pset (make-property-set) pset)
+                           (error ()
+                             (parse-property scanner pset)
+                             (skip-until () #\, #\]))
+                           #\,))
+                   #\])
+               pset)))))
 
 ;;;----- That's all, folks --------------------------------------------------
 
 ;;;----- That's all, folks --------------------------------------------------
index aafa306729fc723392e9750dbb240e377d5c4d47..91668eddc75bf43b73380f72c919145f1dc9db10 100644 (file)
@@ -38,27 +38,14 @@ (defun property-key (name)
     (symbol name)
     (string (intern (frob-identifier name) :keyword))))
 
     (symbol name)
     (string (intern (frob-identifier name) :keyword))))
 
-(export 'property-type)
-(defgeneric property-type (value)
-  (:documentation "Guess a sensible property type to use for VALUE.")
-  (:method ((value symbol)) :symbol)
-  (:method ((value integer)) :int)
-  (:method ((value string)) :string)
-  (:method ((value character)) :char)
-  (:method (value) :other))
-
-(export '(property propertyp make-property
-         p-name p-value p-type p-key p-seenp))
+(export '(property propertyp p-name p-value p-type p-key p-seenp))
 (defstruct (property
             (:predicate propertyp)
             (:conc-name p-)
 (defstruct (property
             (:predicate propertyp)
             (:conc-name p-)
-            (:constructor make-property
-              (name value
-               &key (type (property-type value))
-                    ((:location %loc))
-                    seenp
-               &aux (key (property-key name))
-                    (location (file-location %loc)))))
+            (:constructor %make-property
+                          (name value
+                           &key type location seenp
+                           &aux (key (property-key name)))))
   "A simple structure for holding a property in a property set.
 
    The main useful feature is the ability to tick off properties which have
   "A simple structure for holding a property in a property set.
 
    The main useful feature is the ability to tick off properties which have
@@ -75,6 +62,27 @@ (defstruct (property
   (key nil :type symbol)
   (seenp nil :type boolean))
 
   (key nil :type symbol)
   (seenp nil :type boolean))
 
+(export 'decode-property)
+(defgeneric decode-property (raw)
+  (:documentation "Decode a RAW value into a TYPE, VALUE pair.")
+  (:method ((raw symbol)) (values :symbol raw))
+  (:method ((raw integer)) (values :int raw))
+  (:method ((raw string)) (values :string raw))
+  (:method ((raw character)) (values :char raw))
+  (:method ((raw property)) (values (p-type raw) (p-value raw)))
+  (:method ((raw cons)) (values (car raw) (cdr raw))))
+
+(export 'make-property)
+(defun make-property (name raw-value &key type location seenp)
+  (multiple-value-bind (type value)
+      (if type
+         (values type raw-value)
+         (decode-property raw-value))
+    (%make-property name value
+                   :type type
+                   :location (file-location location)
+                   :seenp seenp)))
+
 (defun string-to-symbol
     (string &key (package *package*) (swap-case t) (swap-hyphen t))
   "Convert STRING to a symbol in PACKAGE.
 (defun string-to-symbol
     (string &key (package *package*) (swap-case t) (swap-hyphen t))
   "Convert STRING to a symbol in PACKAGE.
@@ -186,9 +194,9 @@ (defmacro with-pset-iterator ((name pset) &body body)
   (with-gensyms (next win key value)
     `(with-hash-table-iterator (,next (%pset-hash ,pset))
        (macrolet ((,name ()
   (with-gensyms (next win key value)
     `(with-hash-table-iterator (,next (%pset-hash ,pset))
        (macrolet ((,name ()
-                   (multiple-value-bind (,win ,key ,value) (,next)
-                     (declare (ignore ,key))
-                     (and ,win ,value))))
+                   `(multiple-value-bind (,',win ,',key ,',value) (,',next)
+                     (declare (ignore ,',key))
+                     (and ,',win ,',value))))
         ,@body))))
 
 ;;;--------------------------------------------------------------------------
         ,@body))))
 
 ;;;--------------------------------------------------------------------------
@@ -196,7 +204,7 @@ (defmacro with-pset-iterator ((name pset) &body body)
 
 (export 'store-property)
 (defun store-property
 
 (export 'store-property)
 (defun store-property
-    (pset name value &key (type (property-type value)) location)
+    (pset name value &key type location)
   "Store a property in PSET."
   (pset-store pset
              (make-property name value :type type :location location)))
   "Store a property in PSET."
   (pset-store pset
              (make-property name value :type type :location location)))
@@ -233,8 +241,7 @@ (defun get-property (pset name type &optional default)
                     (p-location prop)))))))
 
 (export 'add-property)
                     (p-location prop)))))))
 
 (export 'add-property)
-(defun add-property
-    (pset name value &key (type (property-type value)) location)
+(defun add-property (pset name value &key type location)
   "Add a property to PSET.
 
    If a property with the same NAME already exists, report an error."
   "Add a property to PSET.
 
    If a property with the same NAME already exists, report an error."
@@ -257,7 +264,7 @@ (defun make-property-set (&rest plist)
    An attempt is made to guess property types from the Lisp types of the
    values.  This isn't always successful but it's not too bad.  The
    alternative is manufacturing a `property-value' object by hand and
    An attempt is made to guess property types from the Lisp types of the
    values.  This isn't always successful but it's not too bad.  The
    alternative is manufacturing a `property-value' object by hand and
-   stuffing into the set."
+   stuffing it into the set."
 
   (property-set plist))
 
 
   (property-set plist))
 
diff --git a/src/pset-test.lisp b/src/pset-test.lisp
new file mode 100644 (file)
index 0000000..e10f7ab
--- /dev/null
@@ -0,0 +1,106 @@
+;;; -*-lisp-*-
+;;;
+;;; Test the property set implementation
+;;;
+;;; (c) 2013 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Sensble Object Design, an object system for C.
+;;;
+;;; SOD is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; SOD is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with SOD; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(cl:in-package #:sod-test)
+
+;;;--------------------------------------------------------------------------
+;;; Here we go.
+
+(defclass pset-test (test-case) ())
+(add-test *sod-test-suite* (get-suite pset-test))
+
+;;;--------------------------------------------------------------------------
+;;; Utilities.
+
+(defun pset-equal-p (pset-a pset-b)
+  (do ((i 0 (1+ i))
+       (p (or pset-a (make-property-set)) q)
+       (q (or pset-b (make-property-set)) p))
+      ((>= i 2) t)
+    (with-pset-iterator (next p)
+      (loop (let ((prop (next)))
+             (when (null prop) (return))
+             (let ((other (pset-get q (p-key prop))))
+               (unless (and other
+                            (equal (p-name prop) (p-name other))
+                            (eq (p-type prop) (p-type other))
+                            (equal (p-value prop) (p-value other)))
+                 (return-from pset-equal-p nil))))))))
+
+(defun assert-pset-equal (pset-a pset-b)
+  (unless (pset-equal-p pset-a pset-b)
+    (failure "Assert equal property sets: ~A ~_and ~A" pset-a pset-b)))
+
+;;;--------------------------------------------------------------------------
+;;; Parser tests.
+
+(defun check-pset-parse (string pset)
+  (let* ((char-scanner (make-string-scanner string))
+        (scanner (make-instance 'sod-token-scanner
+                                :char-scanner char-scanner
+                                :filename "<none>"))
+        (errors nil))
+    (with-parser-context (token-scanner-context :scanner scanner)
+      (multiple-value-bind (result winp consumedp)
+         (handler-bind ((error (lambda (cond)
+                                 (declare (ignore cond))
+                                 (setf errors t)
+                                 (if (find-restart 'continue)
+                                     (invoke-restart 'continue)
+                                     :decline))))
+           (parse-property-set scanner))
+       (declare (ignore consumedp))
+       (when errors (setf winp nil))
+       (cond ((null pset)
+              (assert-false winp))
+             (t
+              (assert-true winp)
+              (unless (eq pset t)
+                (assert-pset-equal result pset))))))))
+
+(def-test-method parse-empty ((test pset-test) :run nil)
+  (check-pset-parse "anything" (make-property-set)))
+
+(def-test-method parse-simple ((test pset-test) :run nil)
+  (check-pset-parse "[ thing = 69 ]"
+                   (make-property-set "thing" 69)))
+
+(def-test-method parse-wrong ((test pset-test) :run nil)
+  (check-pset-parse "[ broken = (1 + ]" nil))
+
+(def-test-method parse-arith ((test pset-test) :run nil)
+  (check-pset-parse (concatenate 'string "[ "
+                                "one = 13*5 - 16*4, "
+                                "two = \"spong\", "
+                                "three = 'c', "
+                                "four = something_different"
+                                "]")
+                   (make-property-set "one" 1
+                                      "two" "spong"
+                                      "three" #\c
+                                      "four" (cons :id
+                                                   "something_different"))))
+
+;;;----- That's all, folks --------------------------------------------------
index 33b54c6449fcbfda23f4a2885c39a8b18776f17c..0b7f6a76543bba70a2adb29af48c11a712d299a4 100644 (file)
          ("module-proto" "pset-proto" "c-types-class-impl" "builtin"))
    (:file "builtin" :depends-on ("module-proto" "pset-proto" "classes"
                                 "c-types-impl" "c-types-class-impl"))
          ("module-proto" "pset-proto" "c-types-class-impl" "builtin"))
    (:file "builtin" :depends-on ("module-proto" "pset-proto" "classes"
                                 "c-types-impl" "c-types-class-impl"))
-   #+no
-   (:file "module-parse" :depends-on ("module-impl"
-                                     "lexer-proto" "fragment-parse"))
+   (:file "module-parse" :depends-on
+         ("module-impl" "lexer-proto" "fragment-parse"))
+   (:file "module-output" :depends-on ("module-impl" "output-proto"))
 
    ;; Output.
    (:file "output-proto" :depends-on ("package"))
 
    ;; Output.
    (:file "output-proto" :depends-on ("package"))