chiark / gitweb /
src/module-parse.lisp: Improve error recovery for `initarg' class-items.
[sod] / src / module-parse.lisp
index a42c31fc804ce6ecba9d9bc55cdbe0a7edd81153..2d81de17037157e703cf9b8f6a6d7e421e5fca0a 100644 (file)
@@ -31,48 +31,63 @@ (in-package #:sod)
 ;;; Type names.
 
 (define-pluggable-parser module typename (scanner pset)
-  ;; `typename' id ( `,' id )* `;'
+  ;; `typename' list[id] `;'
   (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (and "typename"
-               (skip-many (:min 1)
-                 (seq ((id :id))
-                   (if (gethash id *module-type-map*)
-                       (cerror* "Type `~A' already defined" id)
-                       (add-to-module *module*
-                                      (make-instance 'type-item
-                                                     :name id))))
+               (skip-many ()
+                 (error ()
+                     (seq ((id :id))
+                       (if (or (gethash id *module-type-map*)
+                               (find-simple-c-type id))
+                           (cerror* "Type `~A' already defined" id)
+                           (add-to-module *module*
+                                          (make-instance 'type-item
+                                                         :name id))))
+                   (skip-until () #\, #\;))
                  #\,)
-               #\;))))
+               (must #\;)))))
 
 ;;; Fragments.
 
 (define-pluggable-parser module code (scanner pset)
-  ;; `code' id `:' id [constraints] `{' c-fragment `}'
+  ;; `code' id `:' item-name [constraints] `{' c-fragment `}'
   ;;
-  ;; constrains ::= `[' constraint-list `]'
-  ;; constraint ::= id+
+  ;; constraints ::= `[' list[constraint] `]'
+  ;; constraint ::= item-name+
+  ;; item-name ::= id | `(' id+ `)'
   (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
-    (flet ((kw ()
-            (parse (seq ((kw :id)) (intern (string-upcase kw) 'keyword)))))
+    (labels ((kw ()
+              (parse (seq ((kw :id))
+                       (intern (frob-identifier kw) 'keyword))))
+            (item ()
+              (parse (or (kw)
+                         (seq (#\( (names (list (:min 1) (kw))) #\))
+                           names)))))
       (parse (seq ("code"
-                  (reason (kw))
-                  #\:
-                  (name (kw))
+                  (reason (must (kw)))
+                  (nil (must #\:))
+                  (name (must (item)))
                   (constraints (? (seq (#\[
-                                        (constraints (list (:min 1)
-                                                       (list (:min 1) (kw))
-                                                       #\,))
+                                        (constraints
+                                         (list ()
+                                           (list (:min 1)
+                                             (error (:ignore-unconsumed t)
+                                                 (item)
+                                               (skip-until ()
+                                                 :id #\( #\, #\])))
+                                           #\,))
                                         #\])
                                     constraints)))
                   (fragment (parse-delimited-fragment scanner #\{ #\})))
-              (add-to-module *module*
-                             (make-instance 'code-fragment-item
-                                            :fragment fragment
-                                            :constraints constraints
-                                            :reason reason
-                                            :name name)))))))
+              (when name
+                (add-to-module *module*
+                               (make-instance 'code-fragment-item
+                                              :fragment fragment
+                                              :constraints constraints
+                                              :reason reason
+                                              :name name))))))))
 
 ;;; External files.
 
@@ -94,23 +109,30 @@ (defun read-module (pathname &key (truename nil truep) location)
   (define-module (pathname :location location :truename truename)
     (with-open-file (f-stream pathname :direction :input)
       (let* ((*readtable* (copy-readtable))
+            (*package* (find-package '#:sod-user))
             (char-scanner (make-instance 'charbuf-scanner
-                                         :stream f-stream))
+                                         :stream f-stream
+                                         :filename (namestring pathname)))
             (scanner (make-instance 'sod-token-scanner
                                     :char-scanner char-scanner)))
        (with-default-error-location (scanner)
          (with-parser-context (token-scanner-context :scanner scanner)
-           (parse (skip-many ()
-                    (seq ((pset (parse-property-set scanner))
-                          (nil (error ()
-                                 (plug module scanner pset))))
-                      (check-unused-properties pset))))))))))
+           (multiple-value-bind (result winp consumedp)
+               (parse (skip-many ()
+                         (seq ((pset (parse-property-set scanner))
+                               (nil (error ()
+                                        (plug module scanner pset)
+                                      (skip-until (:keep-end nil)
+                                        #\; #\}))))
+                           (check-unused-properties pset))))
+             (declare (ignore consumedp))
+             (unless winp (syntax-error scanner result)))))))))
 
 (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) #\;)
+    (parse (seq ("demo" (string (must :string)) (nil (must #\;)))
             (format t ";; DEMO ~S~%" string)))))
 
 (define-pluggable-parser module file (scanner pset)
@@ -118,14 +140,15 @@ (define-pluggable-parser module file (scanner pset)
   ;; `load' string `;'
   (declare (ignore pset))
   (flet ((common (name type what thunk)
-          (find-file scanner
-                     (merge-pathnames name
-                                      (make-pathname :type type
-                                                     :case :common))
-                     what
-                     thunk)))
+          (when name
+            (find-file scanner
+                       (merge-pathnames name
+                                        (make-pathname :type type
+                                                       :case :common))
+                       what
+                       thunk))))
     (with-parser-context (token-scanner-context :scanner scanner)
-      (parse (or (seq ("import" (name :string) #\;)
+      (parse (or (seq ("import" (name (must :string)) (nil (must #\;)))
                   (common name "SOD" "module"
                           (lambda (path true)
                             (handler-case
@@ -138,8 +161,12 @@ (define-pluggable-parser module file (scanner pset)
                                               *module*))))
                               (file-error (error)
                                 (cerror* "Error reading module ~S: ~A"
+                                         path error))
+                              (error (error)
+                                (cerror* "Unexpected error reading ~
+                                          module ~S: ~A"
                                          path error))))))
-                (seq ("load" (name :string) #\;)
+                (seq ("load" (name (must :string)) (nil (must #\;)))
                   (common name "LISP" "Lisp file"
                           (lambda (path true)
                             (handler-case
@@ -151,23 +178,23 @@ (define-pluggable-parser module file (scanner pset)
 ;;; Setting properties.
 
 (define-pluggable-parser module set (scanner pset)
-  ;; `set' property-list `;'
+  ;; `set' list[property] `;'
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (and "set"
                (lisp (let ((module-pset (module-pset *module*)))
                        (when pset
                          (pset-map (lambda (prop)
-                                     (add-property module-pset
-                                                   (p-name prop)
-                                                   (p-value prop)
-                                                   :type (p-type prop)
-                                                   :location (p-location prop))
+                                     (add-property
+                                      module-pset
+                                      (p-name prop) (p-value prop)
+                                      :type (p-type prop)
+                                      :location (p-location prop))
                                      (setf (p-seenp prop) t))
                                    pset))
-                       (parse (skip-many (:min 0)
+                       (parse (skip-many (:min (if pset 0 1))
                                 (error (:ignore-unconsumed t)
-                                  (parse-property scanner module-pset)
-                                  (skip-until (:keep-end t) #\, #\;))
+                                    (parse-property scanner module-pset)
+                                  (skip-until () #\, #\;))
                                 #\,))))
                #\;))))
 
@@ -184,7 +211,7 @@ (define-pluggable-parser module lisp (scanner pset)
                             (scanner-step scanner)
                             (values sexp t t))
                           (values '((:id "lisp")) nil nil)))
-                #\;)
+                (nil (must #\;)))
             (eval sexp)))))
 
 ;;;--------------------------------------------------------------------------
@@ -192,13 +219,47 @@ (define-pluggable-parser module lisp (scanner pset)
 
 (export 'class-item)
 
+(define-pluggable-parser class-item initfrags (scanner class pset)
+  ;; raw-class-item ::= frag-keyword `{' c-fragment `}'
+  ;; frag-keyword ::= `init' | `teardown'
+  (with-parser-context (token-scanner-context :scanner scanner)
+    (parse (seq ((make (or (seq ("init") #'make-sod-class-initfrag)
+                          (seq ("teardown") #'make-sod-class-tearfrag)))
+                (frag (parse-delimited-fragment scanner #\{ #\})))
+            (funcall make class frag pset scanner)))))
+
+(define-pluggable-parser class-item initargs (scanner class pset)
+  ;; initarg-item ::= `initarg' declspec+ list[init-declarator]
+  ;; init-declarator ::= declarator [`=' initializer]
+  (with-parser-context (token-scanner-context :scanner scanner)
+    (parse (seq ("initarg"
+                (base-type (parse-c-type scanner))
+                (nil (skip-many (:min 1)
+                       (seq ((declarator (parse-declarator scanner
+                                                           base-type))
+                             (init (? (parse-delimited-fragment
+                                       scanner #\= (list #\; #\,)
+                                       :keep-end t))))
+                         (make-sod-user-initarg class
+                                                (cdr declarator)
+                                                (car declarator)
+                                                pset init scanner))
+                       #\,))
+                (nil (must #\;)))))))
+
 (defun parse-class-body (scanner pset name supers)
   ;; class-body ::= `{' class-item* `}'
   ;;
   ;; class-item ::= property-set raw-class-item
   (with-parser-context (token-scanner-context :scanner scanner)
     (make-class-type name)
-    (let* ((class (make-sod-class name (mapcar #'find-sod-class supers)
+    (let* ((duff nil)
+          (class (make-sod-class name
+                                 (restart-case
+                                     (mapcar #'find-sod-class supers)
+                                   (continue ()
+                                     (setf duff t)
+                                     (list (find-sod-class "SodObject"))))
                                  pset scanner))
           (nick (sod-class-nickname class)))
 
@@ -213,6 +274,7 @@ (defun parse-class-body (scanner pset name supers)
                 ;; names.
                 (parse-declarator
                  scanner base-type
+                 :keywordp t
                  :kernel (parser ()
                            (seq ((name-a :id)
                                  (name-b (? (seq (#\. (id :id)) id))))
@@ -245,65 +307,25 @@ (defun parse-class-body (scanner pset name supers)
                                           body sub-pset scanner))))
 
               (parse-initializer ()
-                ;; initializer ::= `=' c-fragment | `=' `{' c-fragment `}'
+                ;; initializer ::= `=' c-fragment
                 ;;
-                ;; Return (VALUE-KIND . VALUE-FORM), ready for passing to a
-                ;; `sod-initializer' constructor.
-
-                ;; This is kind of tricky because we have to juggle both
-                ;; layers of the parsing machinery.  The character scanner
-                ;; will already have consumed the lookahead token (which, if
-                ;; we're going to do anything, is `=').
-                (let ((char-scanner (token-scanner-char-scanner scanner)))
-
-                  ;; First, skip the character-scanner past any whitespace.
-                  ;; We don't record this consumption, which is a bit
-                  ;; naughty, but nobody will actually mind.
-                  (loop
-                    (when (or (scanner-at-eof-p char-scanner)
-                              (not (whitespace-char-p
-                                    (scanner-current-char char-scanner))))
-                      (return))
-                    (scanner-step char-scanner))
-
-                  ;; Now maybe read an initializer.
-                  (cond ((not (eql (token-type scanner) #\=))
-                         ;; It's not an `=' after all.  There's no
-                         ;; initializer.
-                         (values '(#\=) nil nil))
-
-                        ((and (not (scanner-at-eof-p char-scanner))
-                              (char= (scanner-current-char char-scanner)
-                                     #\{))
-                         ;; There's a brace after the `=', so we should
-                         ;; consume the `=' here, and read a compound
-                         ;; initializer enclosed in braces.
-                         (parse (seq (#\= (frag (parse-delimited-fragment
-                                                 scanner #\{ #\})))
-                                  (cons :compound frag))))
-
-                        (t
-                         ;; No brace, so read from the `=' up to, but not
-                         ;; including, the trailing `,' or `;' delimiter.
-                         (parse (seq ((frag (parse-delimited-fragment
-                                             scanner #\= '(#\; #\,)
-                                             :keep-end t)))
-                                  (cons :simple frag)))))))
+                ;; Return a VALUE, ready for passing to a `sod-initializer'
+                ;; constructor.
+                (parse-delimited-fragment scanner #\= (list #\, #\;)
+                                          :keep-end t))
 
               (parse-slot-item (sub-pset base-type type name)
                 ;; slot-item ::=
                 ;;     declspec+ declarator -!- [initializer]
-                ;;             [`,' init-declarator-list] `;'
+                ;;             [`,' list[init-declarator]] `;'
                 ;;
-                ;; init-declarator-list ::=
-                ;;     declarator [initializer] [`,' init-declarator-list]
+                ;; init-declarator ::= declarator [initializer]
                 (parse (and (seq ((init (? (parse-initializer))))
                               (make-sod-slot class name type
                                              sub-pset scanner)
                               (when init
                                 (make-sod-instance-initializer
-                                 class nick name (car init) (cdr init)
-                                 sub-pset scanner)))
+                                 class nick name init sub-pset scanner)))
                             (skip-many ()
                               (seq (#\,
                                     (ds (parse-declarator scanner
@@ -313,25 +335,25 @@ (defun parse-class-body (scanner pset name supers)
                                                sub-pset scanner)
                                 (when init
                                   (make-sod-instance-initializer
-                                   class nick (cdr ds)
-                                   (car init) (cdr init)
+                                   class nick (cdr ds) init
                                    sub-pset scanner))))
                             #\;)))
 
-              (parse-initializer-item (sub-pset constructor)
+              (parse-initializer-item (sub-pset must-init-p constructor)
                 ;; initializer-item ::=
-                ;;     [`class'] -!- slot-initializer-list `;'
+                ;;     [`class'] -!- list[slot-initializer] `;'
                 ;;
-                ;; slot-initializer ::= id `.' id initializer
-                (parse (and (skip-many ()
-                              (seq ((name-a :id) #\. (name-b :id)
-                                    (init (parse-initializer)))
-                                (funcall constructor class
-                                         name-a name-b
-                                         (car init) (cdr init)
-                                         sub-pset scanner))
-                              #\,)
-                            #\;)))
+                ;; slot-initializer ::= id `.' id [initializer]
+                (let ((parse-init (if must-init-p #'parse-initializer
+                                      (parser () (? (parse-initializer))))))
+                  (parse (and (skip-many ()
+                                (seq ((name-a :id) #\. (name-b :id)
+                                      (init (funcall parse-init)))
+                                  (funcall constructor class
+                                           name-a name-b init
+                                           sub-pset scanner))
+                                #\,)
+                              #\;))))
 
               (class-item-dispatch (sub-pset base-type type name)
                 ;; Logically part of `parse-raw-class-item', but the
@@ -345,9 +367,8 @@ (defun parse-class-body (scanner pset name supers)
                 ;; definition; otherwise it might be a message or slot.
                 (cond ((not (typep type 'c-function-type))
                        (when (consp name)
-                         (cerror*-with-location
-                          scanner
-                          "Method declarations must have function type.")
+                         (cerror*
+                          "Method declarations must have function type")
                          (setf name (cdr name)))
                        (parse-slot-item sub-pset base-type type name))
                       ((consp name)
@@ -362,6 +383,7 @@ (defun parse-class-body (scanner pset name supers)
                 ;;   | method-item
                 ;;   | slot-item
                 ;;   | initializer-item
+                ;;   | initfrag-item
                 ;;
                 ;; Most of the above begin with declspecs and a declarator
                 ;; (which might be dotted).  So we parse that here and
@@ -370,16 +392,15 @@ (defun parse-class-body (scanner pset name supers)
                            (peek
                             (seq ((ds (parse-c-type scanner))
                                   (dc (parse-maybe-dotted-declarator ds))
+                                  (nil (commit))
                                   (nil (class-item-dispatch sub-pset
                                                             ds
                                                             (car dc)
                                                             (cdr dc))))))
                            (and "class"
-                                (parse-initializer-item
-                                 sub-pset
+                                (parse-initializer-item sub-pset t
                                  #'make-sod-class-initializer))
-                           (parse-initializer-item
-                            sub-pset
+                           (parse-initializer-item sub-pset nil
                             #'make-sod-instance-initializer)))))
 
        (parse (seq (#\{
@@ -388,19 +409,21 @@ (defun parse-class-body (scanner pset name supers)
                                  (nil (parse-raw-class-item sub-pset)))
                              (check-unused-properties sub-pset))))
                     (nil (error () #\})))
-                (finalize-sod-class class)
-                (add-to-module *module* class)))))))
+                (unless (finalize-sod-class class)
+                  (setf duff t))
+                (unless duff
+                  (add-to-module *module* class))))))))
 
 (define-pluggable-parser module class (scanner pset)
-  ;; `class' id [`:' id-list] class-body
+  ;; `class' id `:' list[id] class-body
   ;; `class' id `;'
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("class"
                 (name :id)
                 (nil (or (seq (#\;)
                            (make-class-type name))
-                         (seq ((supers (? (seq (#\: (ids (list () :id #\,)))
-                                            ids)))
+                         (seq ((supers (seq (#\: (ids (list () :id #\,)))
+                                         ids))
                                (nil (parse-class-body
                                      scanner
                                      pset name supers)))))))))))