Static instance support.
[sod] / src / module-parse.lisp
index 95196f8..eff4af7 100644 (file)
@@ -52,6 +52,7 @@
 
 (define-pluggable-parser module code (scanner pset)
   ;; `code' id `:' item-name [constraints] `{' c-fragment `}'
+  ;; `code' id `:' constraints `;'
   ;;
   ;; constraints ::= `[' list[constraint] `]'
   ;; constraint ::= item-name+
             (item ()
               (parse (or (kw)
                          (seq (#\( (names (list (:min 1) (kw))) #\))
-                           names)))))
+                           names))))
+            (constraints ()
+              (parse (seq (#\[
+                           (constraints
+                            (list ()
+                              (list (:min 1)
+                                (error (:ignore-unconsumed t) (item)
+                                  (skip-until () :id #\( #\, #\])))
+                              #\,))
+                           #\])
+                       constraints)))
+            (fragment ()
+              (parse-delimited-fragment scanner #\{ #\})))
       (parse (seq ("code"
                   (reason (must (kw)))
                   (nil (must #\:))
-                  (name (must (item)))
-                  (constraints (? (seq (#\[
-                                        (constraints
-                                         (list ()
-                                           (list (:min 1)
-                                             (error (:ignore-unconsumed t)
-                                                 (item)
-                                               (skip-until ()
-                                                 :id #\( #\, #\])))
-                                           #\,))
-                                        #\])
-                                    constraints)))
-                  (fragment (parse-delimited-fragment scanner #\{ #\})))
-              (when name
-                (add-to-module *module*
-                               (make-instance 'code-fragment-item
-                                              :fragment fragment
-                                              :constraints constraints
-                                              :reason reason
-                                              :name name))))))))
+                  (item (or (seq ((constraints (constraints))
+                                  (nil (must #\;)))
+                              (make-instance 'code-fragment-item
+                                             :reason reason
+                                             :constraints constraints))
+                            (seq ((name (must (item)))
+                                  (constraints (? (constraints)))
+                                  (fragment (fragment)))
+                              (and name
+                                   (make-instance 'code-fragment-item
+                                                  :reason reason
+                                                  :constraints constraints
+                                                  :name name
+                                                  :fragment fragment))))))
+              (when item (add-to-module *module* item)))))))
 
 ;;; External files.
 
   (unless truep (setf truename (truename pathname)))
   (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
+      (let* ((char-scanner (make-instance 'charbuf-scanner
                                          :stream f-stream
                                          :filename (namestring pathname)))
             (scanner (make-instance 'sod-token-scanner
              (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 (must :string)) (nil (must #\;)))
-            (format t ";; DEMO ~S~%" string)))))
-
 (define-pluggable-parser module file (scanner pset)
   ;; `import' string `;'
   ;; `load' string `;'
   (declare (ignore pset))
   (flet ((common (name type what thunk)
           (when name
-            (find-file scanner
+            (find-file (pathname (scanner-filename scanner))
                        (merge-pathnames name
                                         (make-pathname :type type
                                                        :case :common))
                                                            :truename true)))
                                   (when module
                                     (module-import module)
+                                    (pushnew path (module-files *module*))
                                     (pushnew module
                                              (module-dependencies
                                               *module*))))
                   (common name "LISP" "Lisp file"
                           (lambda (path true)
                             (handler-case
-                                (load true :verbose nil :print nil)
+                                (progn
+                                  (pushnew path (module-files *module*))
+                                  (load true :verbose nil :print nil))
                               (error (error)
                                 (cerror* "Error loading Lisp file ~S: ~A"
                                          path error)))))))))))
                             (scanner-step scanner)
                             (values sexp t t))
                           (values '((:id "lisp")) nil nil)))
-                #\;)
+                (nil (must #\;)))
             (eval sexp)))))
 
 ;;;--------------------------------------------------------------------------
+;;; Static instances.
+
+(define-pluggable-parser module instance (scanner pset)
+  ;; `instance' id id list[slot-initializer] `;'
+  (with-parser-context (token-scanner-context :scanner scanner)
+    (let ((duff nil)
+         (floc nil)
+         (empty-pset (make-property-set)))
+      (parse (seq ("instance"
+                  (class (seq ((class-name (must :id)))
+                           (setf floc (file-location scanner))
+                           (restart-case (find-sod-class class-name)
+                             (continue ()
+                               (setf duff t)
+                               nil))))
+                  (name (must :id))
+                  (inits (? (seq (#\:
+                                  (inits (list (:min 0)
+                                           (seq ((nick (must :id))
+                                                 #\.
+                                                 (name (must :id))
+                                                 (value
+                                                  (parse-delimited-fragment
+                                                   scanner #\= '(#\, #\;)
+                                                   :keep-end t)))
+                                             (make-sod-instance-initializer
+                                              class nick name value
+                                              empty-pset
+                                              :add-to-class nil
+                                              :location scanner))
+                                           #\,)))
+                              inits)))
+                  #\;)
+              (unless duff
+                (acond ((find-if (lambda (item)
+                                   (and (typep item 'static-instance)
+                                        (string= (static-instance-name item)
+                                                 name)))
+                                 (module-items *module*))
+                        (cerror*-with-location floc
+                                               "Instance with name `~A' ~
+                                                already defined."
+                                               name)
+                        (info-with-location (file-location it)
+                                            "Previous definition was ~
+                                             here."))
+                       (t
+                        (add-to-module *module*
+                                       (make-static-instance class name
+                                                             inits
+                                                             pset
+                                                             floc))))))))))
+
+;;;--------------------------------------------------------------------------
 ;;; Class declarations.
 
 (export 'class-item)
     (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)))))
+            (funcall make class frag pset :location scanner)))))
 
 (define-pluggable-parser class-item initargs (scanner class pset)
   ;; initarg-item ::= `initarg' declspec+ list[init-declarator]
                          (make-sod-user-initarg class
                                                 (cdr declarator)
                                                 (car declarator)
-                                                pset init scanner))
+                                                pset
+                                                :default init
+                                                :location scanner))
                        #\,))
-                  #\;)))))
+                (nil (must #\;)))))))
+
+(defun synthetic-name ()
+  "Return an obviously bogus synthetic not-identifier."
+  (let ((ix *temporary-index*))
+    (incf *temporary-index*)
+    (make-instance 'temporary-variable :tag (format nil "%%#~A" ix))))
 
 (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* ((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))
+    (when name (make-class-type name))
+    (let* ((duff (null name))
+          (superclasses
+           (let ((superclasses (restart-case
+                                   (mapcar #'find-sod-class
+                                           (or supers (list "SodObject")))
+                                 (continue ()
+                                   (setf duff t)
+                                   (list (find-sod-class "SodObject"))))))
+             (find-duplicates (lambda (first second)
+                                (declare (ignore second))
+                                (setf duff t)
+                                (cerror* "Class `~A' has duplicate ~
+                                          direct superclass `~A'"
+                                         name first))
+                              superclasses)
+             (delete-duplicates superclasses)))
+          (synthetic-name (or name
+                              (let ((var (synthetic-name)))
+                                (unless pset
+                                  (setf pset (make-property-set)))
+                                (unless (pset-get pset "nick")
+                                  (add-property pset "nick" var :type :id))
+                                var)))
+          (class (make-sod-class synthetic-name superclasses pset
+                                 :location scanner))
           (nick (sod-class-nickname class)))
 
-      (labels ((parse-maybe-dotted-declarator (base-type)
-                ;; Parse a declarator or dotted-declarator, i.e., one whose
-                ;; centre is
-                ;;
-                ;; maybe-dotted-identifier ::= [id `.'] id
+      (labels ((must-id ()
+                (parse (must :id (progn (setf duff t) (synthetic-name)))))
+
+              (parse-maybe-dotted-name ()
+                ;; maybe-dotted-name ::= [id `.'] id
                 ;;
                 ;; A plain identifier is returned as a string, as usual; a
                 ;; dotted identifier is returned as a cons cell of the two
                 ;; names.
-                (parse-declarator
-                 scanner base-type
-                 :keywordp t
-                 :kernel (parser ()
-                           (seq ((name-a :id)
-                                 (name-b (? (seq (#\. (id :id)) id))))
-                             (if name-b (cons name-a name-b)
-                                 name-a)))))
+                (parse (seq ((name-a (must-id))
+                             (name-b (? (seq (#\. (id (must-id))) id))))
+                         (if name-b (cons name-a name-b)
+                             name-a))))
+
+              (parse-maybe-dotted-declarator (base-type)
+                ;; Parse a declarator or dotted-declarator, i.e., one whose
+                ;; centre is maybe-dotted-name above.
+                (parse-declarator scanner base-type
+                                  :keywordp t
+                                  :kernel #'parse-maybe-dotted-name))
 
               (parse-message-item (sub-pset type name)
                 ;; message-item ::=
                 ;; Don't allow a method-body here if the message takes a
                 ;; varargs list, because we don't have a name for the
                 ;; `va_list' parameter.
-                (let ((message (make-sod-message class name type
-                                                 sub-pset scanner)))
+                (let ((message (make-sod-message class name type sub-pset
+                                                 :location scanner)))
                   (if (varargs-message-p message)
                       (parse #\;)
                       (parse (or #\; (parse-method-item sub-pset
                 (parse (seq ((body (or (seq ("extern" #\;) nil)
                                        (parse-delimited-fragment
                                         scanner #\{ #\}))))
-                         (make-sod-method class sub-nick name type
-                                          body sub-pset scanner))))
+                         (restart-case
+                             (make-sod-method class sub-nick name type
+                                              body sub-pset
+                                              :location scanner)
+                           (continue () :report "Continue")))))
 
               (parse-initializer ()
                 ;; initializer ::= `=' c-fragment
                 ;;
                 ;; Return a VALUE, ready for passing to a `sod-initializer'
                 ;; constructor.
-                (parse-delimited-fragment scanner #\= (list #\, #\;)
+                (parse-delimited-fragment scanner #\= '(#\, #\;)
                                           :keep-end t))
 
               (parse-slot-item (sub-pset base-type type name)
                 ;;             [`,' list[init-declarator]] `;'
                 ;;
                 ;; 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 init sub-pset scanner)))
-                            (skip-many ()
-                              (seq (#\,
-                                    (ds (parse-declarator scanner
-                                                          base-type))
-                                    (init (? (parse-initializer))))
-                                (make-sod-slot class (cdr ds) (car ds)
-                                               sub-pset scanner)
-                                (when init
-                                  (make-sod-instance-initializer
-                                   class nick (cdr ds) init
-                                   sub-pset scanner))))
-                            #\;)))
+                (flet ((make-it (name type init)
+                         (restart-case
+                             (progn
+                               (make-sod-slot class name type sub-pset
+                                              :location scanner)
+                               (when init
+                                 (make-sod-instance-initializer
+                                  class nick name init sub-pset
+                                  :location scanner)))
+                           (continue () :report "Continue"))))
+                  (parse (and (error ()
+                                  (seq ((init (? (parse-initializer))))
+                                    (make-it name type init))
+                                (skip-until () #\, #\;))
+                              (skip-many ()
+                                (error (:ignore-unconsumed t)
+                                    (seq (#\,
+                                          (ds (parse-declarator scanner
+                                                                base-type))
+                                          (init (? (parse-initializer))))
+                                      (make-it (cdr ds) (car ds) init))
+                                  (skip-until () #\, #\;)))
+                              (must #\;)))))
 
               (parse-initializer-item (sub-pset must-init-p constructor)
                 ;; initializer-item ::=
                 (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))
+                                (error (:ignore-unconsumed t)
+                                    (seq ((name-a :id) #\.
+                                          (name-b (must-id))
+                                          (init (funcall parse-init)))
+                                      (restart-case
+                                          (funcall constructor class
+                                                   name-a name-b init
+                                                   sub-pset
+                                                   :location scanner)
+                                        (continue () :report "Continue")))
+                                  (skip-until () #\, #\;))
                                 #\,)
-                              #\;))))
+                              (must #\;)))))
 
               (class-item-dispatch (sub-pset base-type type name)
                 ;; Logically part of `parse-raw-class-item', but the
                            (parse-initializer-item sub-pset nil
                             #'make-sod-instance-initializer)))))
 
-       (parse (seq (#\{
+       (parse (seq ((nil (must #\{))
                     (nil (skip-many ()
                            (seq ((sub-pset (parse-property-set scanner))
                                  (nil (parse-raw-class-item sub-pset)))
                              (check-unused-properties sub-pset))))
-                    (nil (error () #\})))
+                    (nil (must #\})))
                 (unless (finalize-sod-class class)
                   (setf duff t))
                 (unless duff
   ;; `class' id `;'
   (with-parser-context (token-scanner-context :scanner scanner)
     (parse (seq ("class"
-                (name :id)
+                (name (must :id))
                 (nil (or (seq (#\;)
-                           (make-class-type name))
-                         (seq ((supers (seq (#\: (ids (list () :id #\,)))
-                                         ids))
+                           (when name (make-class-type name)))
+                         (seq ((supers (must (seq (#\:
+                                                   (ids (list () :id #\,)))
+                                               ids)))
                                (nil (parse-class-body
                                      scanner
                                      pset name supers)))))))))))