More work. Highlights:
authorMark Wooding <mdw@distorted.org.uk>
Thu, 15 Oct 2009 09:24:32 +0000 (10:24 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Thu, 15 Oct 2009 09:24:32 +0000 (10:24 +0100)
  * start using new module import protocol
  * move bootstrapping to separate file
  * eliminated most of the non-C reserved words

Maybe I'll eliminate the C reserved words too.  It'll make the grammar
easier to extend.

builtin.lisp [new file with mode: 0644]
class-builder.lisp
lex.lisp
module.lisp
pset.lisp
tables.lisp

diff --git a/builtin.lisp b/builtin.lisp
new file mode 100644 (file)
index 0000000..26d384b
--- /dev/null
@@ -0,0 +1,331 @@
+;;; -*-lisp-*-
+;;;
+;;; Builtin module provides basic definitions
+;;;
+;;; (c) 2009 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Simple Object Definition system.
+;;;
+;;; 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)
+
+;;;--------------------------------------------------------------------------
+;;; Output of class instances.
+
+(defun output-imprint-function (class stream)
+  (let ((ilayout (sod-class-ilayout class)))
+    (format stream "~&~:
+static void *~A__imprint(void *p)
+{
+  struct ~A *sod__obj = p;
+
+  ~:{sod__obj.~A._vt = &~A;~:^~%  ~}
+  return (p);
+}~2%"
+           class
+           (ilayout-struct-tag class)
+           (mapcar (lambda (ichain)
+                     (list (sod-class-nickname (ichain-head ichain))
+                           (vtable-name class (ichain-head ichain))))
+                   (ilayout-ichains ilayout)))))
+
+(defun output-init-function (class stream)
+  ;; FIXME this needs a metaobject protocol
+  (let ((ilayout (sod-class-ilayout class)))
+    (format stream "~&~:
+static void *~A__init(void *p)
+{
+  struct ~A *sod__obj = ~0@*~A__imprint(p);~2%"
+           class
+           (ilayout-struct-tag class))
+    (dolist (ichain (ilayout-ichains ilayout))
+      (let ((ich (format nil "sod__obj.~A"
+                        (sod-class-nickname (ichain-head ichain)))))
+       (dolist (item (ichain-body ichain))
+         (etypecase item
+           (vtable-pointer
+            (format stream "  ~A._vt = &~A;~%"
+                    ich (vtable-name class (ichain-head ichain))))
+           (islots
+            (let ((isl (format nil "~A.~A"
+                               ich
+                               (sod-class-nickname (islots-class item)))))
+              (dolist (slot (islots-slots item))
+                (let ((dslot (effective-slot-direct-slot slot))
+                      (init (effective-slot-initializer slot)))
+                  (when init
+                    (ecase (sod-initializer-value-kind init)
+                      (:single
+                       (format stream "  ~A = ~A;~%"
+                               isl (sod-initializer-value-form slot)))
+                      (:compound
+                       (format stream "  ~A = (~A)~A;~%"
+                               isl (sod-slot-type dslot)
+                               (sod-initializer-value-form slot)))))))))))))
+    (format stream "~&~:
+  return (p);
+}~2%")))
+
+(defun output-supers-vector (class stream)
+  (let ((supers (sod-class-direct-superclasses class)))
+    (when supers
+      (format stream "~&~:
+static const SodClass *const ~A__supers[] = {
+  ~{~A__class~^,~%  ~}
+};~2%"
+             class supers))))
+
+(defun output-cpl-vector (class stream)
+  (format stream "~&~:
+static const SodClass *const ~A__cpl[] = {
+  ~{~A__class~^,~%  ~}
+};~2%"
+         class (sod-class-precedence-list class)))
+
+(defun output-chains-vector (class stream)
+  (let ((chains (sod-class-chains class)))
+    (format stream "~&~:
+~1@*~:{static const SodClass *const ~A__chain_~A[] = {
+~{  ~A__class~^,~%~}
+};~:^~2%~}
+
+~0@*static const struct sod_chain ~A__chains[] = {
+~:{  { ~3@*~A,
+    ~0@*&~A__chain_~A,
+    ~4@*offsetof(struct ~A, ~A),
+    (const struct sod_vtable *)&~A,
+    sizeof(struct ~A) }~:^,~%~}
+};~2%"
+           class                       ;0
+           (mapcar (lambda (chain)     ;1
+                     (let* ((head (sod-class-chain-head (car chain)))
+                            (chain-nick (sod-class-nickname head)))
+                       (list class chain-nick                      ;0 1
+                             (reverse chain)                       ;2
+                             (length chain)                        ;3
+                             (ilayout-struct-tag class) chain-nick ;4 5
+                             (vtable-name class head)              ;6
+                             (ichain-struct-tag class head))))     ;7
+                   chains))))
+
+(defclass sod-class-slot (sod-slot)
+  ((initializer-function :initarg :initializer-function
+                        :type (or symbol function)
+                        :reader sod-slot-initializer-function)
+   (prepare-function :initarg :prepare-function :type (or symbol function)
+                    :reader sod-slot-prepare-function))
+  (:documentation
+   "Special class for slots defined on SodClass.
+
+   These slots need class-specific initialization.  It's easier to keep all
+   of the information (name, type, and how to initialize them) about these
+   slots in one place, so that's what we do here."))
+
+(defmethod shared-initialize :after
+    ((slot sod-class-slot) slot-names &key pset)
+  (declare (ignore slot-names))
+  (default-slot (slot 'initializer-function)
+    (get-property pset :initializer-function t nil))
+  (default-slot (slot 'prepare-function)
+    (get-property pset :prepare-function t nil)))
+
+(defclass sod-class-effective-slot (effective-slot)
+  ((initializer-function :initarg :initializer-function
+                        :type (or symbol function)
+                        :reader effective-slot-initializer-function)
+   (prepare-function :initarg :prepare-function :type (or symbol function)
+                    :reader effective-slot-prepare-function))
+  (:documentation
+   "Special class for slots defined on SodClass.
+
+   This class ignores any explicit initializers and computes initializer
+   values using the slot's INIT-FUNC slot and a magical protocol during
+   metaclass instance construction."))
+
+(defmethod compute-effective-slot ((class sod-class) (slot sod-class-slot))
+  (make-instance 'sod-class-effective-slot
+                :slot slot
+                :initializer-function (sod-slot-initializer-function slot)
+                :prepare-function (sod-slot-prepare-function slot)
+                :initializer (find-slot-initializer class slot)))
+
+;;;--------------------------------------------------------------------------
+;;; Class slots table.
+
+(defparameter *sod-class-slots*
+  `(
+
+    ;; Basic informtion.
+    ("name" ,(c-type const-string)
+           :initializer-function
+           ,(lambda (class)
+              (prin1-to-string (sod-class-name class))))
+    ("nick" ,(c-type const-string)
+           :initializer-function
+           ,(lambda (class)
+              (prin1-to-string (sod-class-nickname class))))
+
+    ;; Instance allocation and initialization.
+    ("instsz" ,(c-type size-t)
+             :initializer-function
+             ,(lambda (class)
+                (format nil "sizeof(struct ~A)"
+                        (ilayout-struct-tag class))))
+    ("imprint" ,(c-type (* (fun (* void) ("p" (* void)))))
+              :prepare-function 'output-imprint-function
+              :initializer-function
+              ,(lambda (class)
+                 (format nil "~A__imprint" class)))
+    ("init" ,(c-type (* (fun (* void) ("p" (* void)))))
+           :prepare-function 'output-init-function
+           :initializer-function
+           ,(lambda (class)
+              (format nil "~A__init" class)))
+
+    ;; Superclass structure.
+    ("n_supers" ,(c-type size-t)
+               :initializer-function
+               ,(lambda (class)
+                  (length (sod-class-direct-superclasses class))))
+    ("supers" ,(c-type (* (* (class "SodClass" :const) :const)))
+             :prepare-function 'output-supers-vector
+             :initializer-function
+             ,(lambda (class)
+                (if (sod-class-direct-superclasses class)
+                    (format nil "~A__supers" class)
+                    0)))
+    ("n_cpl" ,(c-type size-t)
+            :initializer-function
+               ,(lambda (class)
+                  (length (sod-class-precedence-list class))))
+    ("cpl" ,(c-type (* (* (class "SodClass" :const) :const)))
+          :prepare-function 'output-cpl-vector
+          :initializer-function
+          ,(lambda (class)
+             (format nil "~A__cpl" class)))
+
+    ;; Chain structure.
+    ("link" ,(c-type (* (class "SodClass" :const)))
+           :initializer-function
+           ,(lambda (class)
+              (let ((link (sod-class-chain-link class)))
+                (if link
+                    (format nil "~A__class" link)
+                    0))))
+    ("head" ,(c-type (* (class "SodClass" :const)))
+           :initializer-function
+           ,(lambda (class)
+              (format nil "~A__class" (sod-class-chain-head class))))
+    ("level" ,(c-type size-t)
+            :initializer-function
+            ,(lambda (class)
+               (position class (reverse (sod-class-chain class)))))
+    ("n_chains" ,(c-type size-t)
+               :initializer-function
+               ,(lambda (class)
+                  (length (sod-class-chains class))))
+    ("chains" ,(c-type (* (struct "sod_chain" :const)))
+             :prepare-function 'output-chains-vector
+             :initializer-function
+             ,(lambda (class)
+                (format nil "~A__chains" class)))
+
+    ;; Class-specific layout.
+    ("off_islots" ,(c-type size-t)
+                 :initializer-function
+                 ,(lambda (class)
+                    (format nil "offsetof(struct ~A, ~A)"
+                            (ichain-struct-tag class
+                                               (sod-class-chain-head class))
+                            (sod-class-nickname class))))
+    ("islotsz" ,(c-type size-t)
+              :initializer-function
+              ,(lambda (class)
+                 (format nil "sizeof(struct ~A)"
+                         (islots-struct-tag class))))))
+
+;;;--------------------------------------------------------------------------
+;;; Bootstrapping the class graph.
+
+(defun bootstrap-classes (module)
+  (let* ((sod-object (make-sod-class "SodObject" nil
+                                    (make-property-set :nick 'obj)))
+        (sod-class (make-sod-class "SodClass" (list sod-object)
+                                   (make-property-set :nick 'cls)))
+        (classes (list sod-object sod-class)))
+
+    ;; Sort out the recursion.
+    (setf (slot-value sod-class 'chain-link) sod-object)
+    (dolist (class classes)
+      (setf (slot-value class 'metaclass) sod-class))
+
+    ;; Predeclare the class types.
+    (dolist (class classes)
+      (make-class-type (sod-class-name class)))
+
+    ;; Attach the class slots.
+    (loop for (name type . plist) in *sod-class-slots*
+         do (make-sod-slot sod-class name type
+                           (apply #'make-property-set
+                                  :lisp-class 'sod-class-slot
+                                  plist)))
+
+    ;; These classes are too closely intertwined.  We must partially finalize
+    ;; them together by hand.  This is cloned from FINALIZE-SOD-CLASS.
+    (dolist (class classes)
+      (with-slots (class-precedence-list chain-head chain chains) class
+       (setf class-precedence-list (compute-cpl class))
+       (setf (values chain-head chain chains) (compute-chains class))))
+
+    ;; Done.
+    (dolist (class classes)
+      (finalize-sod-class class)
+      (add-to-module module class))))
+
+(defun make-builtin-module ()
+  (let ((module (make-instance 'module
+                              :name (make-pathname :name "BUILTIN"
+                                                   :type "SOD"
+                                                   :case :common)
+                              :state nil))
+       (*type-map* (make-hash-table :test #'equal)))
+    (dolist (name '("va_list" "size_t" "ptrdiff_t"))
+      (add-to-module module (make-instance 'type-item :name name)))
+    (bootstrap-classes module)
+    module))
+
+(defun reset-builtin-module ()
+  (setf *builtin-module* (make-builtin-module))
+  (module-import *builtin-module*))
+
+;;;--------------------------------------------------------------------------
+;;; Testing.
+
+#+test
+(define-sod-class "AbstractStack" ("SodObject")
+  :nick 'abstk
+  (message "emptyp" (fun int))
+  (message "push" (fun void ("item" (* void))))
+  (message "pop" (fun (* void)))
+  (method "abstk" "pop" (fun void) #{
+     assert(!me->_vt.emptyp());
+   }
+   :role :before))
+
+;;;----- That's all, folks --------------------------------------------------
index 4e05a64..2d77d70 100644 (file)
     (check-method-type method message type)))
 
 ;;;--------------------------------------------------------------------------
-;;; Bootstrapping the class graph.
-;;;
-;;; FIXME: This is a daft place for this function.  It's also accumulating
-;;; all of the magic associated with initializing class instances.
-
-(defun output-imprint-function (class stream)
-  (let ((ilayout (sod-class-ilayout class)))
-    (format stream "~&~:
-static void *~A__imprint(void *p)
-{
-  struct ~A *sod__obj = p;
-
-  ~:{sod__obj.~A._vt = &~A;~:^~%  ~}
-  return (p);
-}~2%"
-           class
-           (ilayout-struct-tag class)
-           (mapcar (lambda (ichain)
-                     (list (sod-class-nickname (ichain-head ichain))
-                           (vtable-name class (ichain-head ichain))))
-                   (ilayout-ichains ilayout)))))
-
-(defun output-init-function (class stream)
-  ;; FIXME this needs a metaobject protocol
-  (let ((ilayout (sod-class-ilayout class)))
-    (format stream "~&~:
-static void *~A__init(void *p)
-{
-  struct ~A *sod__obj = ~0@*~A__imprint(p);~2%"
-           class
-           (ilayout-struct-tag class))
-    (dolist (ichain (ilayout-ichains ilayout))
-      (let ((ich (format nil "sod__obj.~A"
-                        (sod-class-nickname (ichain-head ichain)))))
-       (dolist (item (ichain-body ichain))
-         (etypecase item
-           (vtable-pointer
-            (format stream "  ~A._vt = &~A;~%"
-                    ich (vtable-name class (ichain-head ichain))))
-           (islots
-            (let ((isl (format nil "~A.~A"
-                               ich
-                               (sod-class-nickname (islots-class item)))))
-              (dolist (slot (islots-slots item))
-                (let ((dslot (effective-slot-direct-slot slot))
-                      (init (effective-slot-initializer slot)))
-                  (when init
-                    (ecase (sod-initializer-value-kind init)
-                      (:single
-                       (format stream "  ~A = ~A;~%"
-                               isl (sod-initializer-value-form slot)))
-                      (:compound
-                       (format stream "  ~A = (~A)~A;~%"
-                               isl (sod-slot-type dslot)
-                               (sod-initializer-value-form slot)))))))))))))
-    (format stream "~&~:
-  return (p);
-}~2%")))
-
-(defun output-supers-vector (class stream)
-  (let ((supers (sod-class-direct-superclasses class)))
-    (when supers
-      (format stream "~&~:
-static const SodClass *const ~A__supers[] = {
-  ~{~A__class~^,~%  ~}
-};~2%"
-             class supers))))
-
-(defun output-cpl-vector (class stream)
-  (format stream "~&~:
-static const SodClass *const ~A__cpl[] = {
-  ~{~A__class~^,~%  ~}
-};~2%"
-         class (sod-class-precedence-list class)))
-
-(defun output-chains-vector (class stream)
-  (let ((chains (sod-class-chains class)))
-    (format stream "~&~:
-~1@*~:{static const SodClass *const ~A__chain_~A[] = {
-~{  ~A__class~^,~%~}
-};~:^~2%~}
-
-~0@*static const struct sod_chain ~A__chains[] = {
-~:{  { ~3@*~A,
-    ~0@*&~A__chain_~A,
-    ~4@*offsetof(struct ~A, ~A),
-    (const struct sod_vtable *)&~A,
-    sizeof(struct ~A) }~:^,~%~}
-};~2%"
-           class                       ;0
-           (mapcar (lambda (chain)     ;1
-                     (let* ((head (sod-class-chain-head (car chain)))
-                            (chain-nick (sod-class-nickname head)))
-                       (list class chain-nick                      ;0 1
-                             (reverse chain)                       ;2
-                             (length chain)                        ;3
-                             (ilayout-struct-tag class) chain-nick ;4 5
-                             (vtable-name class head)              ;6
-                             (ichain-struct-tag class head))))     ;7
-                   chains))))
-
-(defparameter *sod-class-slots*
-  `(
-
-    ;; Basic informtion.
-    ("name" ,(c-type const-string)
-           :initializer-function
-           ,(lambda (class)
-              (prin1-to-string (sod-class-name class))))
-    ("nick" ,(c-type const-string)
-           :initializer-function
-           ,(lambda (class)
-              (prin1-to-string (sod-class-nickname class))))
-
-    ;; Instance allocation and initialization.
-    ("instsz" ,(c-type size-t)
-             :initializer-function
-             ,(lambda (class)
-                (format nil "sizeof(struct ~A)"
-                        (ilayout-struct-tag class))))
-    ("imprint" ,(c-type (* (fun (* void) ("p" (* void)))))
-              :prepare-function 'output-imprint-function
-              :initializer-function
-              ,(lambda (class)
-                 (format nil "~A__imprint" class)))
-    ("init" ,(c-type (* (fun (* void) ("p" (* void)))))
-           :prepare-function 'output-init-function
-           :initializer-function
-           ,(lambda (class)
-              (format nil "~A__init" class)))
-
-    ;; Superclass structure.
-    ("n_supers" ,(c-type size-t)
-               :initializer-function
-               ,(lambda (class)
-                  (length (sod-class-direct-superclasses class))))
-    ("supers" ,(c-type (* (* (class "SodClass" :const) :const)))
-             :prepare-function 'output-supers-vector
-             :initializer-function
-             ,(lambda (class)
-                (if (sod-class-direct-superclasses class)
-                    (format nil "~A__supers" class)
-                    0)))
-    ("n_cpl" ,(c-type size-t)
-            :initializer-function
-               ,(lambda (class)
-                  (length (sod-class-precedence-list class))))
-    ("cpl" ,(c-type (* (* (class "SodClass" :const) :const)))
-          :prepare-function 'output-cpl-vector
-          :initializer-function
-          ,(lambda (class)
-             (format nil "~A__cpl" class)))
-
-    ;; Chain structure.
-    ("link" ,(c-type (* (class "SodClass" :const)))
-           :initializer-function
-           ,(lambda (class)
-              (let ((link (sod-class-chain-link class)))
-                (if link
-                    (format nil "~A__class" link)
-                    0))))
-    ("head" ,(c-type (* (class "SodClass" :const)))
-           :initializer-function
-           ,(lambda (class)
-              (format nil "~A__class" (sod-class-chain-head class))))
-    ("level" ,(c-type size-t)
-            :initializer-function
-            ,(lambda (class)
-               (position class (reverse (sod-class-chain class)))))
-    ("n_chains" ,(c-type size-t)
-               :initializer-function
-               ,(lambda (class)
-                  (length (sod-class-chains class))))
-    ("chains" ,(c-type (* (struct "sod_chain" :const)))
-             :prepare-function 'output-chains-vector
-             :initializer-function
-             ,(lambda (class)
-                (format nil "~A__chains" class)))
-
-    ;; Class-specific layout.
-    ("off_islots" ,(c-type size-t)
-                 :initializer-function
-                 ,(lambda (class)
-                    (format nil "offsetof(struct ~A, ~A)"
-                            (ichain-struct-tag class
-                                               (sod-class-chain-head class))
-                            (sod-class-nickname class))))
-    ("islotsz" ,(c-type size-t)
-              :initializer-function
-              ,(lambda (class)
-                 (format nil "sizeof(struct ~A)"
-                         (islots-struct-tag class))))))
-
-(defclass sod-class-slot (sod-slot)
-  ((initializer-function :initarg :initializer-function
-                        :type (or symbol function)
-                        :reader sod-slot-initializer-function)
-   (prepare-function :initarg :prepare-function
-                    :type (or symbol function)
-                    :reader sod-slot-prepare-function))
-  (:documentation
-   "Special class for slots defined on sod_object.
-
-   These slots need class-specific initialization.  It's easier to keep all
-   of the information (name, type, and how to initialize them) about these
-   slots in one place, so that's what we do here."))
-
-(defmethod shared-initialize :after
-    ((slot sod-class-slot) slot-names &key pset)
-  (declare (ignore slot-names))
-  (default-slot (slot 'initializer-function)
-    (get-property pset :initializer-function t nil))
-  (default-slot (slot 'prepare-function)
-    (get-property pset :prepare-function t nil)))
-
-(defclass sod-class-effective-slot (effective-slot)
-  ((initializer-function :initarg :initializer-function
-                        :type (or symbol function)
-                        :reader effective-slot-initializer-function)
-   (prepare-function :initarg :prepare-function
-                        :type (or symbol function)
-                        :reader effective-slot-prepare-function))
-  (:documentation
-   "Special class for slots defined on slot_object.
-
-   This class ignores any explicit initializers and computes initializer
-   values using the slot's INIT-FUNC slot and a magical protocol during
-   metaclass instance construction."))
-
-(defmethod compute-effective-slot ((class sod-class) (slot sod-class-slot))
-  (make-instance 'sod-class-effective-slot
-                :slot slot
-                :initializer-function (sod-slot-initializer-function slot)
-                :prepare-function (sod-slot-prepare-function slot)
-                :initializer (find-slot-initializer class slot)))
-
-(defun bootstrap-classes ()
-  (let* ((sod-object (make-sod-class "SodObject" nil
-                                    (make-property-set :nick 'obj)))
-        (sod-class (make-sod-class "SodClass" (list sod-object)
-                                   (make-property-set :nick 'cls)))
-        (classes (list sod-object sod-class)))
-
-    ;; Sort out the recursion.
-    (setf (slot-value sod-class 'chain-link) sod-object)
-    (dolist (class classes)
-      (setf (slot-value class 'metaclass) sod-class))
-
-    ;; Predeclare the class types.
-    (dolist (class classes)
-      (make-class-type (sod-class-name class)))
-
-    ;; Attach the class slots.
-    (loop for (name type . plist) in *sod-class-slots*
-         do (make-sod-slot sod-class name type
-                           (apply #'make-property-set
-                                  :lisp-class 'sod-class-slot
-                                  plist)))
-
-    ;; These classes are too closely intertwined.  We must partially finalize
-    ;; them together by hand.  This is cloned from FINALIZE-SOD-CLASS.
-    (dolist (class classes)
-      (with-slots (class-precedence-list chain-head chain chains) class
-       (setf class-precedence-list (compute-cpl class))
-       (setf (values chain-head chain chains) (compute-chains class))))
-
-    ;; Done.
-    (dolist (class classes)
-      (finalize-sod-class class)
-      (record-sod-class class))))
-
-;;;--------------------------------------------------------------------------
 ;;; Builder macro.
 
 (defmacro define-sod-class (name (&rest superclasses) &body body)
index 2df0605..1583b11 100644 (file)
--- a/lex.lisp
+++ b/lex.lisp
 (defparameter *sod-keywords*
   (make-keyword-table
 
-   ;; Words with important meanings to us.
-   "class"
-   "import" "load" "lisp" "typename"
-   "code"
-   "extern"
-
    ;; Words with a meaning to C's type system.
    "char" "int" "float" "void"
    "long" "short" "signed" "unsigned" "double"
index bcfc912..36b2c85 100644 (file)
 (cl:in-package #:sod)
 
 ;;;--------------------------------------------------------------------------
+;;; Module basics.
+
+(defclass module ()
+  ((name :initarg :name :type pathname :reader module-name)
+   (pset :initarg :pset :initform (make-pset) :type pset :reader module-pset)
+   (items :initarg :items :initform nil :type list :accessor module-items)
+   (dependencies :initarg :dependencies :initform nil
+                :type list :accessor module-dependencies)
+   (state :initarg :state :initform nil :accessor module-state))
+  (:documentation
+   "A module is a container for the definitions made in a source file.
+
+   Modules are the fundamental units of translation.  The main job of a
+   module is to remember which definitions it contains, so that they can be
+   translated and written to output files.  The module contains the following
+   handy bits of information:
+
+     * A (path) name, which is the filename we used to find it.  The default
+       output filenames are derived from this.  (We use the file's truename
+       as the hash key to prevent multiple inclusion, and that's a different
+       thing.)
+
+     * A property list containing other useful things.
+
+     * A list of the classes defined in the source file.
+
+     * Lists of C fragments to be included in the output header and C source
+       files.
+
+     * A list of other modules that this one depends on.
+
+   Modules are usually constructed by the PARSE-MODULE function, which is in
+   turn usually invoked by IMPORT-MODULE, though there's nothing to stop
+   fancy extensions building modules programmatically."))
+
+(defparameter *module* nil
+  "The current module under construction.
+
+   This is always an instance of MODULE.  Once we've finished constructing
+   it, we'll call CHANGE-CLASS to turn it into an instance of whatever type
+   is requested in the module's :LISP-CLASS property.")
+
+(defgeneric module-import (object)
+  (:documentation
+   "Import definitions into the current environment.
+
+   Instructs the OBJECT to import its definitions into the current
+   environment.  Modules pass the request on to their constituents.  There's
+   a default method which does nothing at all.
+
+   It's not usual to modify the current module.  Inserting things into the
+   *TYPE-MAP* is a good plan.")
+  (:method (object) nil))
+
+(defgeneric add-to-module (module item)
+  (:documentation
+   "Add ITEM to the MODULE's list of accumulated items.
+
+   The module items participate in the MODULE-IMPORT and ADD-OUTPUT-HOOKS
+   protocols."))
+
+(defgeneric finalize-module (module)
+  (:documentation
+   "Finalizes a module, setting everything which needs setting.
+
+   This isn't necessary if you made the module by hand.  If you've
+   constructed it incrementally, then it might be a good plan.  In
+   particular, it will change the class (using CHANGE-CLASS) of the module
+   according to the class choice set in the module's :LISP-CLASS property.
+   This has the side effects of calling SHARED-INITIALIZE, setting the
+   module's state to T, and checking for unrecognized properties.  (Therefore
+   subclasses should add a method to SHARED-INITIALIZE should take care of
+   looking at interesting properties, just to make sure they're ticked
+   off.)"))
+
+(defmethod module-import ((module module))
+  (dolist (item (module-items module))
+    (module-import item)))
+
+(defmethod add-to-module ((module module) item)
+  (setf (module-items module)
+       (nconc (module-items module) (list item)))
+  (module-import item))
+
+(defmethod shared-initialize :after ((module module) slot-names &key pset)
+  "Tick off known properties on the property set."
+  (declare (ignore slot-names))
+  (when pset
+    (dolist (prop '(:guard))
+      (get-property pset prop nil))))
+
+(defmethod finalize-module ((module module))
+  (let* ((pset (module-pset module))
+        (class (get-property pset :lisp-class :symbol 'module)))
+
+    ;; Always call CHANGE-CLASS, even if it's the same one; this will
+    ;; exercise the property-set fiddling in SHARED-INITIALIZE and we can
+    ;; catch unknown-property errors.
+    (change-class module class :state t :pset pset)
+    (check-unused-properties pset)
+    module))
+
+;;;--------------------------------------------------------------------------
+;;; Module importing.
+
+(defun read-module (pathname &key (truename (truename pathname)) location)
+  "Reads a module.
+
+   The module is returned if all went well; NIL is returned if an error
+   occurred.
+
+   The PATHNAME argument is the file to read.  TRUENAME should be the file's
+   truename, if known: often, the file will have been searched for using
+   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.
+  (let ((module (gethash truename *module-map*)))
+    (cond ((typep (module-state module) 'file-location)
+          (error "Module ~A already being imported at ~A"
+                 pathname (module-state module)))
+         (module
+          (return-from read-module module))))
+
+  ;; Make a new module.  Be careful to remove the module from the map if we
+  ;; didn't succeed in constructing it.
+  (let ((*module* (make-instance 'module
+                                :name pathname
+                                :state (file-location location)))
+       (*type-map* (make-hash-table :test #'equal)))
+    (module-import *builtin-module*)
+    (setf (gethash truename *module-map*) *module*)
+    (unwind-protect
+        (with-open-file (f-stream pathname :direction :input)
+          (let* ((*module* (make-instance 'module :name pathname))
+                 (pai-stream (make-instance 'position-aware-input-stream
+                                            :stream f-stream
+                                            :file pathname))
+                 (lexer (make-instance 'sod-lexer :stream pai-stream)))
+            (with-default-error-location (lexer)
+              (next-char lexer)
+              (next-token lexer)
+              (parse-module lexer *module*)
+              (finalize-module *module*))))
+      (unless (eq (module-state *module*) t)
+       (remhash truename *module-map*)))))
+
+;;;--------------------------------------------------------------------------
+;;; Module parsing protocol.
+
+(defgeneric parse-module-declaration (tag lexer pset)
+  (:method (tag lexer pset)
+    (error "Unexpected module declaration ~(~A~)" tag)))
+
+(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)))))
+
+;;;--------------------------------------------------------------------------
+;;; Type definitions.
+
+(defclass type-item ()
+  ((name :initarg :name :type string :reader type-name)))
+
+(defmethod module-import ((item type-item))
+  (let* ((name (type-name item))
+        (def (gethash name *type-map*))
+        (type (make-simple-type name)))
+    (cond ((not def)
+          (setf (gethash name *type-map*) type))
+         ((not (eq def type))
+          (error "Conflicting types `~A'" name)))))
+
+(defmethod module-import ((class sod-class))
+  (record-sod-class class))
+
+;;;--------------------------------------------------------------------------
 ;;; File searching.
 
 (defparameter *module-dirs* nil
            (t
             (funcall thunk path probe))))))
 
+(defmethod parse-module-declaration ((tag (eql :import)) lexer pset)
+  (let ((name (require-token lexer :string)))
+    (when name
+      (find-file lexer
+                (merge-pathnames name
+                                 (make-pathname :type "SOD" :case :common))
+                "module"
+                (lambda (path true)
+                  (handler-case
+                      (let ((module (read-module path :truename true)))
+                        (when module
+                          (module-import module)
+                          (pushnew module (module-dependencies *module*))))
+                    (file-error (error)
+                      (cerror* "Error reading module ~S: ~A"
+                               path error)))))
+      (require-token lexer #\;))))
+
+(defmethod parse-module-declaration ((tag (eql :load)) lexer pset)
+  (let ((name (require-token lexer :string)))
+    (when name
+      (find-file lexer
+                (merge-pathnames name
+                                 (make-pathname :type "LISP" :case :common))
+                "Lisp file"
+                (lambda (path true)
+                  (handler-case (load true :verbose nil :print nil)
+                    (error (error)
+                      (cerror* "Error loading Lisp file ~S: ~A"
+                               path error)))))
+      (require-token lexer #\;))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Modules.
 
-(defclass module ()
-  ((name :initarg :name
-        :type pathname
-        :accessor module-name)
-   (plist :initform nil
-         :initarg :plist
-         :type list
-         :accessor module-plist)
-   (classes :initform nil
-           :initarg :classes
-           :type list
-           :accessor module-classes)
-   (source-fragments :initform nil
-                    :initarg :source-fragments
-                    :type list
-                    :accessor module-source-fragments)
-   (header-fragments :initform nil
-                    :initarg :header-fragments
-                    :type list
-                    :accessor module-header-fragments)
-   (dependencies :initform nil
-                :initarg :dependencies
-                :type list
-                :accessor module-dependencies))
-  (:documentation
-   "A module is a container for the definitions made in a source file.
-
-   Modules are the fundamental units of translation.  The main job of a
-   module is to remember which definitions it contains, so that they can be
-   translated and written to output files.  The module contains the following
-   handy bits of information:
-
-     * A (path) name, which is the filename we used to find it.  The default
-       output filenames are derived from this.  (We use the file's truename
-       as the hash key to prevent multiple inclusion, and that's a different
-       thing.)
-
-     * A property list containing other useful things.
-
-     * A list of the classes defined in the source file.
-
-     * Lists of C fragments to be included in the output header and C source
-       files.
-
-     * A list of other modules that this one depends on.
-
-   Modules are usually constructed by the PARSE-MODULE function, which is in
-   turn usually invoked by IMPORT-MODULE, though there's nothing to stop
-   fancy extensions building modules programmatically."))
-
-(defun import-module (pathname &key (truename (truename pathname)))
-  "Import a module.
-
-   The module is returned if all went well; NIL is returned if an error
-   occurred.
-
-   The PATHNAME argument is the file to read.  TRUENAME should be the file's
-   truename, if known: often, the file will have been searched for using
-   PROBE-FILE or similar, which drops the truename into your lap."
-
-  (let ((module (gethash truename *module-map*)))
-    (cond
-
-      ;; The module's not there.  (The *MODULE-MAP* never maps things to
-      ;; NIL.)
-      ((null module)
-
-       ;; Mark the module as being in progress.  Another attempt to import it
-       ;; will fail.
-       (setf (gethash truename *module-map*) :in-progress)
-
-       ;; Be careful to restore the state of the module map on exit.
-       (unwind-protect
-
-           ;; Open the module file and parse it.
-           (with-open-file (f-stream pathname :direction :input)
-             (let* ((pai-stream (make-instance 'position-aware-input-stream
-                                               :stream f-stream
-                                               :file pathname))
-                    (lexer (make-instance 'sod-lexer :stream pai-stream)))
-               (with-default-error-location (lexer)
-                 (restart-case
-                     (progn
-                       (next-char lexer)
-                       (next-token lexer)
-                       (setf module (parse-module lexer)))
-                   (continue ()
-                     :report "Ignore the import and continue"
-                     nil))))))
-
-        ;; If we successfully parsed the module, then store it in the table;
-        ;; otherwise remove it because we might want to try again.  (That
-        ;; might not work very well, but it could be worth a shot.)
-        (if module
-            (setf (gethash truename *module-map*) module)
-            (remhash truename *module-map*)))
-
-      ;; A module which is being read can't be included again.
-      ((eql module :in-progress)
-       (error "Cyclic module dependency involving module ~A" pathname))
-
-      ;; A module which was successfully read.  Just return it.
-      (t
-       module))))
-
+#+(or)
 (defun parse-module (lexer)
   "Parse a module from the given LEXER.
 
            (next-token lexer)
            (go top))
 
-          ;; module-def : `import' string `;'
-          ;;
-          ;; Read another module of definitions from a file.
-          (:import
-           (next-token lexer)
-           (let ((name (require-token lexer :string)))
-             (when name
-               (find-file lexer
-                          (merge-pathnames name (make-pathname
-                                                 :type "SOD"
-                                                 :case :common))
-                          "module"
-                          (lambda (path true)
-                            (handler-case
-                                (let ((module (import-module path
-                                                           :truename true)))
-                                  (when module
-                                    (push module deps)))
-                              (file-error (error)
-                                (cerror* "Error reading module ~S: ~A"
-                                         path error)))))))
-           (go semicolon))
-
-          ;; module-def : `load' string `;'
-          ;;
-          ;; Load a Lisp extension from a file.
-          (:load
-           (next-token lexer)
-           (let ((name (require-token lexer :string)))
-             (when name
-               (find-file lexer
-                          (merge-pathnames name
-                                           (make-pathname :type "LISP"
-                                                          :case :common))
-                          "Lisp file"
-                          (lambda (path true)
-                            (handler-case (load true
-                                                :verbose nil
-                                                :print nil)
-                              (error (error)
-                                (cerror* "Error loading Lisp file ~S: ~A"
-                                         path error)))))))
-           (go semicolon))
-
           ;; module-def : `lisp' sexp
           ;;
           ;; Process an in-line Lisp form immediately.
index 7652812..9db8c4f 100644 (file)
--- a/pset.lisp
+++ b/pset.lisp
 (defun store-property
     (pset name value &key (type (property-type value)) location)
   "Store a property in PSET."
-  (%pset-store pset
-              (make-property name value :type type :location location)))
+  (pset-store pset
+             (make-property name value :type type :location location)))
 
 (defun get-property (pset name type &optional default)
   "Fetch a property from a property set.
 
    If PSET is nil, then return DEFAULT."
 
-  (let ((prop (and pset (%pset-get pset (property-key name)))))
+  (let ((prop (and pset (pset-get pset (property-key name)))))
     (with-default-error-location ((and prop (p-location prop)))
       (cond ((not prop)
             (values default nil))
    alternative is manufacturing a PROPERTY-VALUE object by hand and stuffing
    into the set."
 
-  (do ((pset (%make-pset))
+  (do ((pset (make-pset))
        (plist plist (cddr plist)))
       ((endp plist) pset)
     (add-property pset (car plist) (cadr plist))))
   (print-unreadable-object (pset stream :type t)
     (pprint-logical-block (stream nil)
       (let ((firstp t))
-       (%pset-map (lambda (prop)
-                    (cond (firstp (setf firstp nil))
-                          (t (write-char #\space stream)
-                             (pprint-newline :linear stream)))
-                    (format stream "~:@<~S ~@_~S ~@_~S~:>"
-                            (p-name prop) (p-type prop) (p-value prop)))
-                  pset)))))
+       (pset-map (lambda (prop)
+                   (cond (firstp (setf firstp nil))
+                         (t (write-char #\space stream)
+                            (pprint-newline :linear stream)))
+                   (format stream "~:@<~S ~@_~S ~@_~S~:>"
+                           (p-name prop) (p-type prop) (p-value prop)))
+                 pset)))))
 
 (defun check-unused-properties (pset)
   "Issue errors about unused properties in PSET."
-  (%pset-map (lambda (prop)
-              (unless (p-seenp prop)
-                (cerror*-with-location (p-location prop)
-                                       "Unknown property `~A'"
-                                       (p-name prop))))
-            pset))
+  (when pset
+    (pset-map (lambda (prop)
+               (unless (p-seenp prop)
+                 (cerror*-with-location (p-location prop)
+                                        "Unknown property `~A'"
+                                        (p-name prop))))
+             pset)))
 
 ;;;--------------------------------------------------------------------------
 ;;; Expression parser.
index 9bd4d5a..a639770 100644 (file)
    object.  This is how we find classes by name: the C-CLASS-TYPE object has
    a reference to the underlying SOD-CLASS instance.")
 
+(defparameter *builtin-module* nil
+  "Built-in module; populated later.")
+
 ;;;--------------------------------------------------------------------------
 ;;; Utilities.
 
 (defparameter *clear-the-decks-functions*
   '(reset-type-and-module-map
-    populate-type-map
-    bootstrap-classes))
+    reset-builtin-module))
 
 (defun reset-type-and-module-map ()
   "Reset the main hash tables, clearing the translator's state.