chiark / gitweb /
It lives!
authorMark Wooding <mdw@distorted.org.uk>
Tue, 20 Oct 2009 00:42:17 +0000 (01:42 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 20 Oct 2009 00:42:17 +0000 (01:42 +0100)
The module parser is more-or-less done.  Output is more-or-less done.
Outstanding.  Now to remove the bugs...

14 files changed:
builtin.lisp
chimaera.sod [new file with mode: 0644]
class-builder.lisp
class-layout.lisp
class-output.lisp
codegen.lisp
examples.lisp
methods.lisp
module-output.lisp
module.lisp
parse-c-types.lisp
pset.lisp
sod.h
sod.tex

index 67c04c1d87d6670f64dd321524cc09aff0833c01..9309581f30b51b2f3afcab7a6f89dd21eb2fd87b 100644 (file)
@@ -65,8 +65,7 @@ (defun output-init-function (class stream)
        (dolist (item (ichain-body ichain))
          (etypecase item
            (vtable-pointer
-            (format stream "  ~A._vt = &~A;~%"
-                    ich (vtable-name class (ichain-head ichain))))
+            nil)
            (islots
             (let ((isl (format nil "~A.~A"
                                ich
@@ -75,14 +74,18 @@ (defun output-init-function (class stream)
                 (let ((dslot (effective-slot-direct-slot slot))
                       (init (effective-slot-initializer slot)))
                   (when init
+                    (format stream "  ~A =" isl)
                     (ecase (sod-initializer-value-kind init)
-                      (:single
-                       (format stream "  ~A = ~A;~%"
-                               isl (sod-initializer-value-form init)))
-                      (:compound
-                       (format stream "  ~A = (~A)~A;~%"
-                               isl (sod-slot-type dslot)
-                               (sod-initializer-value-form init)))))))))))))
+                      (:simple (write (sod-initializer-value-form init)
+                                      :stream stream
+                                      :pretty nil :escape nil)
+                               (format stream ";~%"))
+                      (:compound (format stream " (~A) {"
+                                         (sod-slot-type dslot))
+                                 (write (sod-initializer-value-form init)
+                                        :stream stream
+                                        :pretty nil :escape nil)
+                                 (format stream "};~%"))))))))))))
     (format stream "~&~:
   return (p);
 }~2%")))
diff --git a/chimaera.sod b/chimaera.sod
new file mode 100644 (file)
index 0000000..d5507f8
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*-sod-*-
+ *
+ * A simple SOD module for testing.
+ */
+
+code c : includes {
+#include <stdio.h>
+#include "chimaera.h"
+}
+
+code h : includes {
+#include "sod.h"
+}
+
+lisp (write-line "Hello, world!") ;
+
+[nick = nml, link = SodObject]
+class Animal : SodObject {
+  int tickles = 0;
+
+  void tickle(void) { }
+
+  [role = before]
+  void nml.tickle(void) { me->nml.tickles++; }
+}
+
+class Lion : Animal {
+  void bite(void) { puts("Munch!"); }
+  void nml.tickle(void) { me->_vt.bite(me); }
+}
+
+class Goat : Animal {
+  void butt(void) { puts("Bonk!"); }
+  void nml.tickle(void) { me->_vt.butt(me); }
+}
+
+class Serpent : Animal {
+  void hiss(void) { puts("Sssss!"); }
+  void bite(void) { puts("Nom!"); }
+  void nml.tickle(void) {
+    if (SERPENT__CONV_NML(me)->nml.tickles > 2)
+      me->_vt.bite();
+    else
+      me->_vt.hiss();
+  }
+}
+
+[nick = sir, link = Animal]
+class Chimaera : Lion, Goat, Serpent {
+}
+
index 7acbeaeed22351a3caa6fe9b69b55f56f141112d..59dd4eec8c757ec3e14e18aa947adb38f98a3fad 100644 (file)
@@ -129,7 +129,7 @@ (defmethod shared-initialize :after ((class sod-class) slot-names &key pset)
 
   ;; If no nickname, copy the class name.  It won't be pretty, though.
   (default-slot (class 'nickname)
-    (get-property pset :nick :id (slot-value class 'name)))
+    (get-property pset :nick :id (string-downcase (slot-value class 'name))))
 
   ;; If no metaclass, guess one in a (Lisp) class-specific way.
   (default-slot (class 'metaclass)
index a37852ed365a955a9bef5ee4fddbdce62264adc3..877073992c31175a344f14b1ebc782f64d846063 100644 (file)
@@ -232,12 +232,11 @@ (defgeneric compute-sod-effective-method (message class)
 
 (defmethod compute-sod-effective-method
     ((message sod-message) (class sod-class))
-  (let ((direct-methods (mapcan (lambda (super)
-                                 (let ((method
-                                        (find message
-                                              (sod-class-methods super)
-                                              :key #'sod-method-message)))
-                                   (and method (list method))))
+  (let ((direct-methods (mappend (lambda (super)
+                                  (remove message
+                                          (sod-class-methods super)
+                                          :key #'sod-method-message
+                                          :test-not #'eql))
                                (sod-class-precedence-list class))))
     (make-instance (message-effective-method-class message)
                   :message message
@@ -549,10 +548,14 @@ (defun find-root-superclass (class)
   ;; is harmless.
   (let* ((supers (sod-class-direct-superclasses class))
         (roots (if supers
-                   (remove-if #'sod-class-direct-superclasses
-                              (mapcar (lambda (super)
-                                        (sod-class-chain-head super))
-                                      supers))
+                   (remove-duplicates
+                    (remove-if #'sod-class-direct-superclasses
+                               (mappend (lambda (super)
+                                          (mapcar (lambda (chain)
+                                                    (sod-class-chain-head
+                                                     (car chain)))
+                                                  (sod-class-chains super)))
+                                        supers)))
                    (list class))))
     (cond ((null roots) (error "Class ~A has no root class!" class))
          ((cdr roots) (error "Class ~A has multiple root classes ~
index ee2daf3f9ddab23b933e8270f160615e9f43b011..da6531b12e4f7dde38b052ea9ae14a829b0d1a6c 100644 (file)
@@ -93,8 +93,8 @@ (defmethod add-output-hooks progn
     ((class :object)
      (let ((metaclass (sod-class-metaclass class))
           (metaroot (find-root-metaclass class)))
-       (format stream "/* The class object. */~%~
-                      extern const struct ~A ~A__classobj;~%~
+       (format stream "/* The class object. */~@
+                      extern const struct ~A ~A__classobj;~@
                       #define ~:*~A__class (&~:*~A__classobj.~A.~A)~2%"
               (ilayout-struct-tag metaclass) class
               (sod-class-nickname (sod-class-chain-head metaroot))
@@ -106,7 +106,7 @@ (defmethod add-output-hooks progn
       (add-output-hooks slot 'populate-islots sequencer))
     (sequence-output (stream sequencer)
       ((class :islots :start)
-       (format stream "/* Instance slots. */~%~
+       (format stream "/* Instance slots. */~@
                       struct ~A {~%"
               (islots-struct-tag class)))
       ((class :islots :end)
@@ -141,9 +141,11 @@ (defmethod add-output-hooks progn
                    sequencer))
 
 (defmethod add-output-hooks progn ((class sod-class) reason sequencer)
-  (with-slots (ilayout vtables methods) class
+  (with-slots (ilayout vtables methods effective-methods) class
     (add-output-hooks ilayout reason sequencer)
     (dolist (method methods) (add-output-hooks method reason sequencer))
+    (dolist (method effective-methods)
+      (add-output-hooks method reason sequencer))
     (dolist (vtable vtables) (add-output-hooks vtable reason sequencer))))
 
 ;;;--------------------------------------------------------------------------
@@ -166,7 +168,7 @@ (defmethod add-output-hooks progn
   (with-slots (class ichains) ilayout
     (sequence-output (stream sequencer)
       ((class :ilayout :start)
-       (format stream "/* Instance layout. */~%~
+       (format stream "/* Instance layout. */~@
                       struct ~A {~%"
               (ilayout-struct-tag class)))
       ((class :ilayout :end)
@@ -185,13 +187,13 @@ (defmethod add-output-hooks progn
                     (class :ichain chain-head :end)
                     (class :ichains :end))
        ((class :ichain chain-head :start)
-        (format stream "/* Instance chain structure. */~%~
+        (format stream "/* Instance chain structure. */~@
                         struct ~A {~%"
                 (ichain-struct-tag chain-tail chain-head)))
        ((class :ichain chain-head :end)
         (format stream "};~2%")
-        (format stream "/* Union of equivalent superclass chains. */~%~
-                        union ~A {~%~
+        (format stream "/* Union of equivalent superclass chains. */~@
+                        union ~A {~@
                         ~:{  struct ~A ~A;~%~}~
                         };~2%"
                 (ichain-union-tag chain-tail chain-head)
@@ -259,7 +261,7 @@ (defmethod add-output-hooks progn
                     (class :vtable chain-head :end)
                     (class :vtables :end))
        ((class :vtable chain-head :start)
-        (format stream "/* Vtable structure. */~%~
+        (format stream "/* Vtable structure. */~@
                         struct ~A {~%"
                 (vtable-struct-tag chain-tail chain-head)))
        ((class :vtable chain-head :end)
@@ -290,7 +292,7 @@ (defmethod add-output-hooks progn
                     (subclass :vtmsgs class :end)
                     (subclass :vtmsgs :end))
        ((subclass :vtmsgs class :start)
-        (format stream "/* Messages protocol from class ~A */~%~
+        (format stream "/* Messages protocol from class ~A */~@
                         struct ~A {~%"
                 class
                 (vtmsgs-struct-tag subclass class)))
@@ -357,7 +359,7 @@ (defmethod add-output-hooks progn
     ((:classes :start)
      (class :banner)
      (class :direct-methods :start) (class :direct-methods :end)
-     (class :effective-methods :start) (class :effective-methods :end)
+     (class :effective-methods)
      (class :vtables :start) (class :vtables :end)
      (class :object :prepare) (class :object :start) (class :object :end)
      (:classes :end))
@@ -382,9 +384,6 @@ (defmethod add-output-hooks progn
 ;;;--------------------------------------------------------------------------
 ;;; Direct methods.
 
-;; This could well want splitting out into some more elaborate protocol.  We
-;; need a bunch of refactoring anyway.
-
 (defmethod add-output-hooks progn
     ((method delegating-direct-method) (reason (eql :c)) sequencer)
   (with-slots (class body) method
@@ -420,6 +419,90 @@ (defmethod add-output-hooks progn
       ((class :direct-method method :end)
        (terpri stream)))))
 
+;;;--------------------------------------------------------------------------
+;;; Vtables.
+
+(defmethod add-output-hooks progn
+    ((vtable vtable) (reason (eql :c)) sequencer)
+  (with-slots (class chain-head chain-tail) vtable
+    (sequence-output (stream sequencer)
+      :constraint ((class :vtables :start)
+                  (class :vtable chain-head :start)
+                  (class :vtable chain-head :end)
+                  (class :vtables :end))
+      ((class :vtable chain-head :start)
+       (format stream "/* Vtable for ~A chain. */~@
+                      static const struct ~A ~A = {~%"
+              chain-head
+              (vtable-struct-tag chain-tail chain-head)
+              (vtable-name chain-tail chain-head)))
+      ((class :vtable chain-head :end)
+       (format stream "};~2%")))))
+
+(defmethod add-output-hooks progn
+    ((cptr class-pointer) (reason (eql :c)) sequencer)
+  (with-slots (class chain-head metaclass meta-chain-head) cptr
+    (sequence-output (stream sequencer)
+      :constraint ((class :vtable chain-head :start)
+                  (class :vtable chain-head :class-pointer metaclass)
+                  (class :vtable chain-head :end))
+      ((class :vtable chain-head :class-pointer metaclass)
+       (format stream "  &~A__classobj.~A.~A,~%"
+              (sod-class-metaclass class)
+              (sod-class-nickname meta-chain-head)
+              (sod-class-nickname metaclass))))))
+
+(defmethod add-output-hooks progn
+    ((boff base-offset) (reason (eql :c)) sequencer)
+  (with-slots (class chain-head) boff
+    (sequence-output (stream sequencer)
+      :constraint ((class :vtable chain-head :start)
+                  (class :vtable chain-head :base-offset)
+                  (class :vtable chain-head :end))
+      ((class :vtable chain-head :base-offset)
+       (format stream "  offsetof(struct ~A, ~A),~%"
+              (ilayout-struct-tag class)
+              (sod-class-nickname chain-head))))))
+
+(defmethod add-output-hooks progn
+    ((choff chain-offset) (reason (eql :c)) sequencer)
+  (with-slots (class chain-head target-head) choff
+    (sequence-output (stream sequencer)
+      :constraint ((class :vtable chain-head :start)
+                  (class :vtable chain-head :chain-offset target-head)
+                  (class :vtable chain-head :end))
+      ((class :vtable chain-head :chain-offset target-head)
+       (format stream "  SOD_OFFSETDIFF(struct ~A, ~A, ~A),~%"
+              (ilayout-struct-tag class)
+              (sod-class-nickname chain-head)
+              (sod-class-nickname target-head))))))
+
+(defmethod add-output-hooks progn
+    ((vtmsgs vtmsgs) (reason (eql :c)) sequencer)
+  (with-slots (class subclass chain-head) vtmsgs
+    (sequence-output (stream sequencer)
+      :constraint ((subclass :vtable chain-head :start)
+                  (subclass :vtable chain-head :vtmsgs class :start)
+                  (subclass :vtable chain-head :vtmsgs class :slots)
+                  (subclass :vtable chain-head :vtmsgs class :end)
+                  (subclass :vtable chain-head :end))
+      ((subclass :vtable chain-head :vtmsgs class :start)
+       (format stream "  { /* Method entries for ~A messages. */~%"
+              class))
+      ((subclass :vtable chain-head :vtmsgs class :end)
+       (format stream "  },~%")))))
+
+(defmethod add-output-hooks progn
+    ((entry method-entry) (reason (eql :c)) sequencer)
+  (with-slots (method chain-head chain-tail) entry
+    (let* ((message (effective-method-message method))
+          (class (effective-method-class method))
+          (super (sod-message-class message)))
+      (sequence-output (stream sequencer)
+       ((class :vtable chain-head :vtmsgs super :slots)
+        (format stream "    ~A,~%"
+                (method-entry-function-name method chain-head)))))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Filling in the class object.
 
index e9415690d4672fcba1e3ef668ff2a5bba21b9b9a..fc6a4088e37147de18c38374dc500d6a6ab32de4 100644 (file)
@@ -248,6 +248,7 @@ (definst call (stream) (func args)
 
 (definst function (stream) (name type body)
   (pprint-logical-block (stream nil)
+    (princ "static " stream)
     (pprint-c-type type stream name)
     (format stream "~:@_~A~:@_~:@_" body)))
 
index f91f5521ce60d7ed28667d534c240b535c083aee..82702a6c2946162907fefbb6d4cf2acc75c07d7e 100644 (file)
@@ -3,6 +3,14 @@ (set-dispatch-macro-character #\# #\{ 'c-fragment-reader)
 (defparameter *chimaera-module*
   (define-module ("chimaera.sod")
 
+    (define-fragment (:c :includes) #{
+       #include "chimaera.h"
+     })
+
+    (define-fragment (:h :includes) #{
+       #include "sod.h"
+     })
+
     (define-sod-class "Animal" ("SodObject")
       :nick 'nml
       :link '|SodObject|
index 67033daf159ce65baee02c85f6809fb121f447f8..b54887a311b9faf222e0f7fca66f0d2a72634958 100644 (file)
@@ -382,9 +382,10 @@ (defun invoke-method (codegen target arguments-tail direct-method)
   (let* ((message (sod-method-message direct-method))
         (class (sod-method-class direct-method))
         (function (sod-method-function-name direct-method))
-        (arguments (cons (format nil "(~A *)&sod__obj.~A" class
+        (arguments (cons (format nil "&sod__obj.~A.~A"
                                  (sod-class-nickname
-                                  (sod-class-chain-head class)))
+                                  (sod-class-chain-head class))
+                                 (sod-class-nickname class))
                          arguments-tail)))
     (if (varargs-message-p message)
        (convert-stmts codegen target
@@ -533,7 +534,7 @@ (defgeneric compute-method-entry-functions (method)
    method entries.  Returns a list of functions (i.e., FUNCTION-INST objects)
    which need to be defined in the generated source code."))
 
-(defparameter *method-entry-inline-threshold* 20
+(defparameter *method-entry-inline-threshold* 200
   "Threshold below which effective method bodies are inlined into entries.
 
    After the effective method body has been computed, we calculate its
@@ -568,7 +569,7 @@ (defmethod method-entry-function-name
   (let* ((class (effective-method-class method))
         (message (effective-method-message method))
         (message-class (sod-message-class message)))
-    (format nil "~A__mentry_~A__~A__~A"
+    (format nil "~A__mentry_~A__~A__chain_~A"
            class
            (sod-class-nickname message-class)
            (sod-message-name message)
@@ -724,4 +725,15 @@ (defmethod make-method-entry ((method basic-effective-method)
                 :chain-head chain-head
                 :chain-tail chain-tail))
 
+;;;--------------------------------------------------------------------------
+;;; Output.
+
+(defmethod add-output-hooks progn
+    ((method basic-effective-method) (reason (eql :c)) sequencer)
+  (with-slots (class functions) method
+    (sequence-output (stream sequencer)
+      ((class :effective-methods)
+       (dolist (func functions)
+        (write func :stream stream :escape nil :circle nil))))))
+
 ;;;----- That's all, folks --------------------------------------------------
index dedbe97c02599e4274f4f207385beb23327771d0..891ff54fc379d9fbab7d1f422963bce3637e077b 100644 (file)
@@ -60,7 +60,13 @@ (defun guess-output-file (module type)
                   (module-name module)))
 
 (defun output-module (module reason stream)
-  (let ((sequencer (make-instance 'sequencer)))
+  (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)))
 
@@ -71,6 +77,18 @@ (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.
 
@@ -81,7 +99,7 @@ (defmethod add-output-hooks progn
                 (:guard :start)
                 (:typedefs :start) :typedefs (:typedefs :end)
                 (:includes :start) :includes (:includes :end)
-                (:classes :start) (:classes :end)
+                (:classes :start) :classes (:classes :end)
                 (:guard :end)
                 :epilogue)
 
index 5d05365f2ccdbabb194646edc7854ddf0e4dbe8e..6f8aeecc48548f5088c0f326cd6ac63e545c81cf 100644 (file)
@@ -166,9 +166,10 @@ (defun read-module (pathname &key (truename (truename pathname)) location)
    PROBE-FILE or similar, which drops the truename into your lap."
 
   ;; Deal with a module which is already in the map.  If its state is a
-  ;; file-location then it's in progress and we have a cyclic dependency.
+  ;; FILE-LOCATION then it's in progress and we have a cyclic dependency.
   (let ((module (gethash truename *module-map*)))
-    (cond ((typep (module-state module) 'file-location)
+    (cond ((null module))
+         ((typep (module-state module) 'file-location)
           (error "Module ~A already being imported at ~A"
                  pathname (module-state module)))
          (module
@@ -186,46 +187,50 @@   (define-module (pathname :location location :truename truename)
          (with-default-error-location (lexer)
            (next-char lexer)
            (next-token lexer)
-           (parse-module lexer *module*)))))))
+           (parse-module lexer)))))))
 
 ;;;--------------------------------------------------------------------------
 ;;; Module parsing protocol.
 
 (defgeneric parse-module-declaration (tag lexer pset)
   (:method (tag lexer pset)
-    (error "Unexpected module declaration ~(~A~)" tag)))
+    (error "Unexpected module declaration ~(~A~)" tag))
+  (:method :before (tag lexer pset)
+    (next-token lexer)))
 
 (defun parse-module (lexer)
   "Main dispatching for module parser.
 
    Calls PARSE-MODULE-DECLARATION for the identifiable declarations."
 
-  ;; A little fancy footwork is required because `class' is a reserved word.
   (loop
-    (flet ((dispatch (tag pset)
-            (next-token lexer)
-            (parse-module-declaration tag lexer pset)
-            (check-unused-properties pset)))
-      (restart-case
-         (case (token-type lexer)
-           (:eof (return))
-           (#\; (next-token lexer))
-           (t (let ((pset (parse-property-set lexer)))
-                (case (token-type lexer)
-                  (:id (dispatch (string-to-symbol (token-value lexer)
-                                                   :keyword)
-                                 pset))
-                  (t (error "Unexpected token ~A: ignoring"
-                            (format-token lexer)))))))
-       (continue ()
-         :report "Ignore the error and continue parsing."
-         nil)))))
+    (restart-case
+       (case (token-type lexer)
+         (:eof (return))
+         (#\; (next-token lexer))
+         (t (let ((pset (parse-property-set lexer)))
+              (case (token-type lexer)
+                (:id (let ((tag (intern (frob-case (token-value lexer))
+                                        :keyword)))
+                       (parse-module-declaration tag lexer pset)
+                       (check-unused-properties pset)))
+                (t (error "Unexpected token ~A: ignoring"
+                          (format-token lexer)))))))
+      (continue ()
+       :report "Ignore the error and continue parsing."
+       nil))))
 
 ;;;--------------------------------------------------------------------------
 ;;; Type definitions.
 
 (defclass type-item ()
-  ((name :initarg :name :type string :reader type-name)))
+  ((name :initarg :name :type string :reader type-name))
+  (:documentation
+   "A note that a module exports a type.
+
+   We can only export simple types, so we only need to remember the name.
+   The magic simple-type cache will ensure that we get the same type object
+   when we do the import."))
 
 (defmethod module-import ((item type-item))
   (let* ((name (type-name item))
@@ -239,6 +244,82 @@     (def (gethash name *type-map*))
 (defmethod module-import ((class sod-class))
   (record-sod-class class))
 
+(defmethod parse-module-declaration ((tag (eql :typename)) lexer pset)
+  "module-decl ::= `typename' id-list `;'"
+  (loop (let ((name (require-token lexer :id)))
+         (unless name (return))
+         (if (gethash name *type-map*)
+             (cerror* "Type `~A' already defined" name)
+             (add-to-module *module* (make-instance 'type-item :name name)))
+         (unless (require-token lexer #\, :errorp nil) (return))))
+  (require-token lexer #\;))
+
+;;;--------------------------------------------------------------------------
+;;; Fragments.
+
+(defclass code-fragment-item ()
+  ((fragment :initarg :fragment :type c-fragment :reader code-fragment)
+   (reason :initarg :reason :type keyword :reader code-fragment-reason)
+   (name :initarg :name :type t :reader code-fragment-name)
+   (constraints :initarg :constraints :type list
+               :reader code-fragment-constraints))
+  (:documentation
+   "A plain fragment of C to be dropped in at top-level."))
+
+(defmacro define-fragment ((reason name) &body things)
+  (categorize (thing things)
+      ((constraints (listp thing))
+       (frags (typep thing '(or string c-fragment))))
+    (when (null frags)
+      (error "Missing code fragment"))
+    (when (cdr frags)
+      (error "Multiple code fragments"))
+    `(add-to-module
+      *module*
+      (make-instance 'code-fragment-item
+                    :fragment ',(car frags)
+                    :name ,name
+                    :reason ,reason
+                    :constraints (list ,@(mapcar (lambda (constraint)
+                                                   (cons 'list constraint))
+                                                 constraints))))))
+
+(defmethod parse-module-declaration ((tag (eql :code)) lexer pset)
+  "module-decl ::= `code' id `:' id [constraint-list] `{' c-fragment `}'
+   constraint ::= id*"
+  (labels ((parse-constraint ()
+            (let ((list nil))
+              (loop (let ((id (require-token lexer :id
+                                             :errorp (null list))))
+                      (unless id (return))
+                      (push id list)))
+              (nreverse list)))
+          (parse-constraints ()
+            (let ((list nil))
+              (when (require-token lexer #\[ :errorp nil)
+                (loop (let ((constraint (parse-constraint)))
+                        (push constraint list)
+                        (unless (require-token lexer #\, :errorp nil)
+                          (return))))
+                (require-token lexer #\]))
+              (nreverse list)))
+          (keywordify (id)
+            (and id (intern (substitute #\- #\_ (frob-case id)) :keyword))))
+    (let* ((reason (prog1 (keywordify (require-token lexer :id))
+                  (require-token lexer #\:)))
+          (name (keywordify (require-token lexer :id)))
+          (constraints (parse-constraints)))
+      (when (require-token lexer #\{ :consumep nil)
+       (let ((frag (scan-c-fragment lexer '(#\}))))
+         (next-token lexer)
+         (require-token lexer #\})
+         (add-to-module *module*
+                        (make-instance 'code-fragment-item
+                                       :name name
+                                       :reason reason
+                                       :constraints constraints
+                                       :fragment frag)))))))
+
 ;;;--------------------------------------------------------------------------
 ;;; File searching.
 
@@ -281,11 +362,12 @@ (defun find-file (lexer name what thunk)
       (error "Error searching for ~A ~S: ~A" what (namestring name) error))
     (:no-error (path probe)
       (cond ((null path)
-            (error "Failed to find ~A ~S" what name))
+            (error "Failed to find ~A ~S" what (namestring name)))
            (t
             (funcall thunk path probe))))))
 
 (defmethod parse-module-declaration ((tag (eql :import)) lexer pset)
+  "module-decl ::= `import' string `;'"
   (let ((name (require-token lexer :string)))
     (when name
       (find-file lexer
@@ -304,6 +386,7 @@ (defmethod parse-module-declaration ((tag (eql :import)) lexer pset)
       (require-token lexer #\;))))
 
 (defmethod parse-module-declaration ((tag (eql :load)) lexer pset)
+  "module-decl ::= `load' string `;'"
   (let ((name (require-token lexer :string)))
     (when name
       (find-file lexer
@@ -317,6 +400,163 @@ (defmethod parse-module-declaration ((tag (eql :load)) lexer pset)
                                path error)))))
       (require-token lexer #\;))))
 
+;;;--------------------------------------------------------------------------
+;;; Lisp escapes.
+
+(defmethod parse-module-declaration :around ((tag (eql :lisp)) lexer pset)
+  "module-decl ::= `lisp' s-expression `;'"
+  (let ((form (with-lexer-stream (stream lexer) (read stream t))))
+    (eval form))
+  (next-token lexer)
+  (require-token lexer #\;))
+
+;;;--------------------------------------------------------------------------
+;;; Class declarations.
+
+(defmethod parse-module-declaration ((tag (eql :class)) lexer pset)
+  "module-decl ::= `class' id [`:' id-list] `{' class-item* `}'"
+  (let* ((location (file-location lexer))
+        (name (let ((name (require-token lexer :id)))
+                (make-class-type name location)
+                (when (require-token lexer #\; :errorp nil)
+                  (return-from parse-module-declaration))
+                name))
+        (supers (when (require-token lexer #\: :errorp nil)
+                  (let ((list nil))
+                    (loop (let ((id (require-token lexer :id)))
+                            (unless id (return))
+                            (push id list)
+                            (unless (require-token lexer #\, :errorp nil)
+                              (return))))
+                    (nreverse list))))
+        (class (make-sod-class name (mapcar #'find-sod-class supers)
+                               pset location))
+        (nick (sod-class-nickname class)))
+    (require-token lexer #\{)
+
+    (labels ((parse-item ()
+              "Try to work out what kind of item this is.  Messy."
+              (let* ((pset (parse-property-set lexer))
+                     (location (file-location lexer)))
+                (cond ((declaration-specifier-p lexer)
+                       (let ((declspec (parse-c-type lexer)))
+                         (multiple-value-bind (type name)
+                             (parse-c-declarator lexer declspec :dottedp t)
+                           (cond ((null type)
+                                  nil)
+                                 ((consp name)
+                                  (parse-method type (car name) (cdr name)
+                                                pset location))
+                                 ((typep type 'c-function-type)
+                                  (parse-message type name pset location))
+                                 (t
+                                  (parse-slots declspec type name
+                                               pset location))))))
+                      ((not (eq (token-type lexer) :id))
+                       (cerror* "Expected <class-item>; found ~A (skipped)"
+                                (format-token lexer))
+                       (next-token lexer))
+                      ((string= (token-value lexer) "class")
+                       (next-token lexer)
+                       (parse-initializers #'make-sod-class-initializer
+                                           pset location))
+                      (t
+                       (parse-initializers #'make-sod-instance-initializer
+                                           pset location)))))
+
+            (parse-method (type nick name pset location)
+              "class-item ::= declspec+ dotted-declarator -!- method-body
+
+               method-body ::= `{' c-fragment `}' | `extern' `;'
+
+               The dotted-declarator must describe a function type."
+              (let ((body (cond ((eq (token-type lexer) #\{)
+                                 (prog1 (scan-c-fragment lexer '(#\}))
+                                   (next-token lexer)
+                                   (require-token lexer #\})))
+                                ((and (eq (token-type lexer) :id)
+                                      (string= (token-value lexer)
+                                               "extern"))
+                                 (next-token lexer)
+                                 (require-token lexer #\;)
+                                 nil)
+                                (t
+                                 (cerror* "Expected <method-body>; ~
+                                           found ~A"
+                                          (format-token lexer))))))
+                (make-sod-method class nick name type body pset location)))
+
+            (parse-message (type name pset location)
+              "class-item ::= declspec+ declarator -!- (method-body | `;')
+
+               The declarator must describe a function type."
+              (make-sod-message class name type pset location)
+              (unless (require-token lexer #\; :errorp nil)
+                (parse-method type nick name nil location)))
+
+            (parse-initializer-body ()
+              "initializer ::= `=' `{' c-fragment `}' | `=' c-fragment"
+              (let ((char (lexer-char lexer)))
+                (loop
+                  (when (or (null char) (not (whitespace-char-p char)))
+                    (return))
+                  (setf char (next-char lexer)))
+                (cond ((eql char #\{)
+                       (next-char lexer)
+                       (let ((frag (scan-c-fragment lexer '(#\}))))
+                         (next-token lexer)
+                         (require-token lexer #\})
+                         (values :compound frag)))
+                      (t
+                       (let ((frag (scan-c-fragment lexer '(#\, #\;))))
+                         (next-token lexer)
+                         (values :simple frag))))))
+
+            (parse-slots (declspec type name pset location)
+              "class-item ::=
+                 declspec+ init-declarator [`,' init-declarator-list] `;'
+
+               init-declarator ::= declarator -!- [initializer]"
+              (loop
+                (make-sod-slot class name type pset location)
+                (when (eql (token-type lexer) #\=)
+                  (multiple-value-bind (kind form) (parse-initializer-body)
+                    (make-sod-instance-initializer class nick name
+                                                   kind form nil
+                                                   location)))
+                (unless (require-token lexer #\, :errorp nil)
+                  (return))
+                (setf (values type name)
+                      (parse-c-declarator lexer declspec)
+                      location (file-location lexer)))
+              (require-token lexer #\;))
+
+            (parse-initializers (constructor pset location)
+              "class-item ::= [`class'] -!- slot-initializer-list `;'
+
+               slot-initializer ::= id `.' id initializer"
+              (loop
+                (let ((nick (prog1 (require-token lexer :id)
+                              (require-token lexer #\.)))
+                      (name (require-token lexer :id)))
+                  (require-token lexer #\=)
+                  (multiple-value-bind (kind form)
+                      (parse-initializer-body)
+                    (funcall constructor class nick name kind form
+                             pset location)))
+                (unless (require-token lexer #\, :errorp nil)
+                  (return))
+                (setf location (file-location lexer)))
+              (require-token lexer #\;)))
+
+      (loop
+       (when (require-token lexer #\} :errorp nil)
+         (return))
+       (parse-item)))
+
+    (finalize-sod-class class)
+    (add-to-module *module* class)))
+
 ;;;--------------------------------------------------------------------------
 ;;; Modules.
 
@@ -359,12 +599,7 @@ (defun parse-module (lexer)
           ;;
           ;; Process an in-line Lisp form immediately.
           (:lisp
-           (let ((form (with-lexer-stream (stream lexer)
-                         (read stream t))))
-             (handler-case
-                 (eval form)
-               (error (error)
-                 (cerror* "Error in Lisp form: ~A" error))))
+           
            (next-token lexer)
            (go top))
 
index 361396500165bbe3272448a9e34de3461fb3a12d..63e8b9b277a04c7486dab45af6ed4883148bbd69 100644 (file)
@@ -398,9 +398,8 @@ (defun parse-c-declarator (lexer type &key abstractp dottedp)
               (eq (token-type lexer) :id))
       (let ((name (token-value lexer)))
        (next-token lexer)
-       (cond ((and dottedp
-                   (eq (token-type lexer) #\.))
-              (let ((sub (require-token :id :default (gensym))))
+       (cond ((and dottedp (require-token lexer #\. :errorp nil))
+              (let ((sub (require-token lexer :id :default (gensym))))
                 (setf item (cons name sub))))
              (t
               (setf item name)))))
index 9db8c4ffee40f0899b18a8f45dabc5e801ce3cb3..a9bbde90dc83551b138a30894be42712894dbce6 100644 (file)
--- a/pset.lisp
+++ b/pset.lisp
@@ -269,7 +269,8 @@ (defun check-unused-properties (pset)
                (unless (p-seenp prop)
                  (cerror*-with-location (p-location prop)
                                         "Unknown property `~A'"
-                                        (p-name prop))))
+                                        (p-name prop))
+                 (setf (p-seenp prop) t)))
              pset)))
 
 ;;;--------------------------------------------------------------------------
diff --git a/sod.h b/sod.h
index cb562444a3f79c964bcb1576c46e0967221aeada..999c30ed34b19428414f7dc0742c561d3f8a7d6d 100644 (file)
--- a/sod.h
+++ b/sod.h
@@ -84,6 +84,19 @@ struct sod_chain {
 
 #define SOD_XCHAIN(chead, p) ((char *)(p) + (p)->_vt->_off_##chead)
 
+/* --- @SOD_OFFSETDIFF@ --- *
+ *
+ * Arguments:  @type@ = a simple (i.e., declaratorless) type name
+ *             @mema, memb@ = members of @type@
+ *
+ * Returns:    The relative offset from @mema@ to @memb@, as a @ptrdiff_t@.
+ *
+ * Use:                Computes a signed offset between structure members.
+ */
+
+#define SOD_OFFSETDIFF(type, mema, memb)                               \
+  ((ptrdiff_t)offsetof(type, memb) - (ptrdiff_t)offsetof(type, mema))
+
 /* --- @SOD_ILAYOUT@ --- *
  *
  * Arguments:  @cls@ = name of a class
diff --git a/sod.tex b/sod.tex
index 7af3c0f0ba1e8bd915d1ac2492325adb20a1243a..dfc4a10260a0f45cd11ce75cd9ca6ff0a141e53f 100644 (file)
--- a/sod.tex
+++ b/sod.tex
@@ -933,6 +933,7 @@ the applicable methods are invoked are described fully in
 \subsection{Chains and instance layout}
 
 \include{sod-backg}
+\include{sod-protocol}
 
 \end{document}
 \f