Static instance support.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 6 Oct 2019 22:03:42 +0000 (23:03 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 6 Oct 2019 23:18:28 +0000 (00:18 +0100)
13 files changed:
doc/SYMBOLS
doc/meta.tex
doc/output.tex
doc/syntax.tex
src/class-make-impl.lisp
src/class-make-proto.lisp
src/class-output.lisp
src/classes.lisp
src/module-output.lisp
src/module-parse.lisp
test/bad.ref
test/bad.sod
test/test.sod

index 7e18dcd..dc1e784 100644 (file)
@@ -354,16 +354,20 @@ class-make-proto.lisp
   make-sod-slot-initarg                         generic
   make-sod-slot-initarg-using-slot              generic
   make-sod-user-initarg                         generic
+  make-static-instance                          generic
   sod-initarg-argument                          generic
   sod-message-method-class                      generic
 
 class-output.lisp
   *instance-class*                              variable
+  *static-instance*                             variable
+  declare-static-instance                       generic
   emit-class-conversion-macro                   generic
   emit-class-object-decl                        generic
   emit-class-typedef                            generic
   emit-message-macro                            generic
   emit-message-macro-defn                       generic
+  output-static-instance-initializer            generic
 
 class-utilities.lisp
   argument-lists-compatible-p                   function
@@ -441,6 +445,12 @@ classes.lisp
   sod-slot-name                                 generic
   sod-slot-type                                 generic
   sod-user-initarg                              class
+  static-instance                               class
+  static-instance-class                         generic
+  static-instance-const-p                       generic
+  static-instance-extern-p                      generic
+  static-instance-initializers                  generic setf-generic
+  static-instance-name                          generic
 
 codegen-impl.lisp
   codegen                                       class
@@ -829,6 +839,7 @@ cl:t
           delegating-direct-method
       sod-slot
         sod-class-slot
+      static-instance
       temporary-name
         temporary-argument
         temporary-function
@@ -1022,6 +1033,8 @@ compute-vtables
   sod-class
 compute-vtmsgs
   sod-class sod-class sod-class sod-class
+declare-static-instance
+  t t
 decode-property
   cl:character
   cl:cons
@@ -1262,6 +1275,7 @@ sod-parser:file-location
   sod-message
   sod-method
   sod-slot
+  static-instance
 finalize-module
   module
 finalize-sod-class
@@ -1292,15 +1306,18 @@ hook-output
   code-fragment-item t t
   delegating-direct-method (eql :c) t
   effective-slot (eql cl:class) t
+  effective-slot (eql static-instance) t
   ichain (eql :h) t
   ichain (eql cl:class) t
   ichain (eql ilayout) t
+  ichain (eql static-instance) t
   ichain t t [:after]
   ilayout (eql :h) t [:after]
   ilayout (eql :h) t
   ilayout t t [:after]
   islots (eql :h) t
   islots (eql cl:class) t
+  islots (eql static-instance) t
   islots t t [:after]
   method-entry (eql :c) t
   method-entry (eql vtmsgs) t
@@ -1311,16 +1328,21 @@ hook-output
   sod-class (eql :c) t
   sod-class (eql :h) t [:after]
   sod-class (eql :h) t
+  sod-class (eql static-instance) t
   sod-class t t [:after]
   sod-class-effective-slot (eql cl:class) t
   sod-method (eql :c) t
   sod-method (eql :h) t
   sod-slot (eql islots) t
+  static-instance (eql :c) t [:after]
+  static-instance (eql :c) t
+  static-instance (eql :h) t
   vtable (eql :c) t
   vtable (eql :h) t
   vtable t t [:after]
   vtable-pointer (eql :h) t
   vtable-pointer (eql cl:class) t
+  vtable-pointer (eql static-instance) t
   vtmsgs (eql :c) t
   vtmsgs (eql :h) t
   vtmsgs (eql vtmsgs) t
@@ -1465,6 +1487,8 @@ make-sod-slot-initarg-using-slot
   sod-class t sod-slot t
 make-sod-user-initarg
   sod-class t t t
+make-static-instance
+  sod-class t t t t
 method-entry-chain-head
   method-entry
 method-entry-chain-tail
@@ -1512,6 +1536,8 @@ module-state
   module
 (setf module-state)
   t module
+output-static-instance-initializer
+  static-instance effective-slot t
 pprint-c-storage-specifier
   cl:symbol t
   alignas-storage-specifier t
@@ -1576,6 +1602,7 @@ cl:print-object
   sod-slot t
   sod-slot-initarg t
   sod-user-initarg t
+  static-instance t
   temporary-name t
   update-inst t
   var-inst t
@@ -1613,6 +1640,7 @@ cl:shared-initialize
   sod-method t [:after]
   sod-slot t [:after]
   sod-token-scanner t [:after]
+  static-instance t [:after]
 simple-method-body
   aggregating-effective-method t t
   sod::lifecycle-effective-method t t
@@ -1766,6 +1794,18 @@ sod-slot-prepare-function
   sod-class-slot
 sod-slot-type
   sod-slot
+static-instance-class
+  static-instance
+static-instance-const-p
+  static-instance
+static-instance-extern-p
+  static-instance
+static-instance-initializers
+  static-instance
+(setf static-instance-initializers)
+  t static-instance
+static-instance-name
+  static-instance
 temp-tag
   temporary-name
 temporary-var
@@ -2207,6 +2247,7 @@ file-location
   sod:sod-message
   sod:sod-method
   sod:sod-slot
+  sod:static-instance
   character-scanner
   charbuf-scanner-place
   condition-with-location
index dc06abf..f341414 100644 (file)
     {check-method-argument-lists @<method-type> @<message-type>}
 \end{describe}
 
+\begin{describe}{cls}
+    {static-instance ()
+      \&key :name :location :externp :constp :class :initializers}
+\end{describe}
+
+\begin{describe*}
+    {\dhead{gf}{static-instance-name @<static-instance> @> @<string>}
+     \dhead{gf}{static-instance-extern-p @<static-instance>
+                 @> @<generalized-boolean>}
+     \dhead{gf}{static-instance-const-p @<static-instance>
+                 @> @<generalized-boolean>}
+     \dhead{gf}{static-instance-class @<static-instance> @> @<class>}
+     \dhead{gf}{static-instance-initializers @<static-instance> @> @<list>}
+     \dhead{gf}
+       {setf (static-instance-initializers @<static-instance>) @<list>}}
+\end{describe*}
+
+\begin{describe}{gf}
+    {make-static-instance @<class> @<name> @<initializers>
+      @<pset> @<location> \&key
+     \nlret @<static-instance>}
+\end{describe}
+
 %%%--------------------------------------------------------------------------
 \section{Class finalization protocol} \label{sec:meta.finalization}
 
index 22d8552..1615119 100644 (file)
@@ -414,6 +414,18 @@ The following notes may be helpful.
   %
   (:classes :end)                                               \\ \hlx{v/}
   %
+  (:static-instances :start)    & static-instance, :h
+        & \begin{nprog} \banner{Public static instances} \\ \end{nprog}
+                                                                \\*\hlx{v}
+  :static-instances             & static-instance, :h
+        & \begin{nprog}
+            extern $[@"const"]$ struct @<class>{}__ilayout
+              @<inst>{}__instance;                              \\
+            \#define @<inst> (\&@<inst>{}__instance.@<h>.@<c>)
+          \end{nprog}                                           \\*
+  (:static-instances :end)      & static-instance, :h
+        &                                                       \\ \hlx{v/}
+  %
   (:user :start)                                                \\*
   :user                                                         \\*
   (:user :end)                                                  \\ \hlx{v/}
@@ -464,6 +476,39 @@ The following notes may be helpful.
   :early                                                        \\*
   (:early-user :end)                                            \\ \hlx{v/}
   %
+  (:static-instances :start)    & static-instance, :c
+        & \begin{nprog} \banner{Static instance definitions} \\ \end{nprog}
+                                                                \\*\hlx{v}
+  (:static-instances :decls)    & static-instance, :c
+        & \begin{nprog}
+            /* Forward declarations. */                         \\+
+            static $[@"const"]$ struct @<class>{}__ilayout
+              @<inst>{}__instance;                              \\
+            \#define @<inst> (\&@<inst>{}__instance.@<h>.@<c>)
+          \end{nprog}                                           \\*
+  (:static-instances :gap)      & static-instance, :c
+        &                                                       \\
+  (@<inst> :start)              & sod-class, 'static-instance
+        & \begin{nprog}
+            /* Static instance `@<inst>'. */                    \\
+            $[@"static"]$ $[@"const"]$ struct @<inst>{}__ilayout
+              @<inst>{}__instance = \{
+          \end{nprog}                                           \\*\hlx{v}
+  (@<inst> :chain @<chain-head> :start) & ichain, 'static-instance
+        & \quad \{ \{ /* @<n> ichain */                         \\*
+  (@<inst> :vtable @<chain-head>) & vtable-pointer, 'static-instance
+        & \slotlabel{3}{_vt} \&@<class>{}__vtable_@<h>.@<c>,    \\*
+  (@<inst> :slots @<super> :start) & islots, 'static-instance
+        & \quad\quad \{ /* Class @<super> */                    \\*
+  (@<inst> :slots @<super>)     & effective-slot, 'static-instance
+        & \slotlabel{4}{@<slot>} @<value>,                      \\*
+  (@<inst> :slots @<super> :end) & islots, 'static-instance
+        & \quad\quad \},                                        \\*
+  (@<inst> :chain @<chain-head> :end) & ichain, 'static-instance
+        & \quad \} \},                                          \\*
+  (@<inst> :end)                & sod-class, 'static-instance
+        & \begin{nprog} \}; \\ \end{nprog}                      \\ \hlx{v/}
+  %
   (:classes :start)                                             \\*\hlx{v}
   %
   (@<class> :banner)            & sod-class, :c
@@ -686,6 +731,17 @@ The following notes may be helpful.
                   \>@<in-names> @<out-names> @<stream>}}
 \end{describe*}
 
+\begin{describe}{var}{*static-instance*}
+\end{describe}
+
+\begin{describe}{gf}{declare-static-instance @<static-instance> @<stream>}
+\end{describe}
+
+\begin{describe}{gf}
+    {output-static-instance-initializer @<static-instance> @<effective-slot>
+                                        @<stream>}
+\end{describe}
+
 %%%----- That's all, folks --------------------------------------------------
 
 %%% Local variables:
index 72329a4..6c05acc 100644 (file)
@@ -710,6 +710,27 @@ preprocessor directives in order to declare types and functions for use
 elsewhere in the generated output files.
 
 
+\subsection{Static instance definitions} \label{sec:syntax.module.instance}
+
+\begin{grammar}
+<static-instance-definition> ::=
+  "instance" <identifier> <identifier>
+  @[":" <list>$[\mbox{@<instance-initializer>}]$@] ";"
+
+<instance-initializer> ::= <identifier> "." <identifier> "=" <c-fragment>
+\end{grammar}
+
+Properties:
+\begin{description}
+\item[@"extern"] A boolean flag: if true, then the instance is public, and
+  will be declared in the output header file; if false (the default), then
+  the instance is only available to code defined within the module.
+\item[@"const"] A boolean flag: if true (the default), then the instance is
+  read-only, and may end up in write-protected storage at run-time; if false,
+  then the instance will be writable.
+\end{description}
+
+
 \subsection{Class definitions} \label{sec:syntax.module.class}
 
 \begin{grammar}
index 02fd5f5..b3347bd 100644 (file)
     (check-method-return-type-against-message type msgtype)
     (check-method-argument-lists type msgtype)))
 
+;;;--------------------------------------------------------------------------
+;;; Static instances.
+
+(defmethod shared-initialize :after
+    ((instance static-instance) slot-names &key pset)
+  "Initialize a static instance."
+  (default-slot-from-property (instance 'externp slot-names)
+      (pset :extern :boolean)
+    nil)
+  (default-slot-from-property (instance 'constp slot-names)
+      (pset :const :boolean)
+    t))
+
+(defmethod make-static-instance ((class sod-class) name initializers
+                                pset location &key)
+
+  ;; Check that the initializers are all for distinct slots.
+  (find-duplicates (lambda (initializer previous)
+                    (let ((slot (sod-initializer-slot initializer)))
+                      (cerror*-with-location initializer
+                                             "Duplicate initializer for ~
+                                              instance slot `~A' in ~
+                                              static instance `~A'"
+                                             slot name)
+                      (info-with-location previous
+                                          "Previous definition was here")))
+                  initializers
+                  :key #'sod-initializer-slot)
+
+  ;; Ensure that every slot will have an initializer, either defined directly
+  ;; on the instance or as part of some class definition.
+  (let ((have (make-hash-table)))
+
+    ;; First, populate the hash table with all of the slots for which we have
+    ;; initializers.
+    (flet ((seen-slot-initializer (init)
+            (setf (gethash (sod-initializer-slot init) have) t)))
+      (mapc #'seen-slot-initializer
+           initializers)
+      (dolist (super (sod-class-precedence-list class))
+       (mapc #'seen-slot-initializer
+             (sod-class-instance-initializers super))))
+
+    ;; Now go through all of the slots and check that they have initializers.
+    (dolist (super (sod-class-precedence-list class))
+      (dolist (slot (sod-class-slots super))
+       (unless (gethash slot have)
+         (cerror*-with-location location
+                                "No initializer for instance slot `~A', ~
+                                 required by static instance `~A'"
+                                slot name)
+         (info-with-location slot "Slot `~A' defined here" slot)))))
+
+  ;; Make the instance.
+  (make-instance 'static-instance
+                :class class
+                :name name
+                :initializers initializers
+                :location (file-location location)
+                :pset pset))
+
 ;;;----- That's all, folks --------------------------------------------------
index 09b9f98..a978ca3 100644 (file)
    This is separated out of `shared-initialize', where it's called, so that
    it can be overridden conveniently by subclasses."))
 
+;;;--------------------------------------------------------------------------
+;;; Static instances.
+
+(export 'make-static-instance)
+(defgeneric make-static-instance (class name initializers pset location &key)
+  (:documentation
+   "Construct a new static instance of the given CLASS.
+
+   This is the main constructor functoin for static instances.  This is a
+   generic function so that the CLASS can construct static instances in a
+   special way."))
+
 ;;;----- That's all, folks --------------------------------------------------
index 45a4909..0dfd30a 100644 (file)
                 (sod-class-nickname target-head)))))))
 
 ;;;--------------------------------------------------------------------------
+;;; Static instance declarations.
+
+(export 'declare-static-instance)
+(defgeneric declare-static-instance (instance stream)
+  (:documentation
+   "Write a declaration for the static INSTANCE to STREAM.
+
+   Note that, according to whether the instance is external or private, this
+   may be written as part of the `:h' or `:c' reasons."))
+(defmethod declare-static-instance (instance stream)
+  (with-slots ((class %class) name externp constp) instance
+    (format stream "~:[static~;extern~] ~:[~;const ~]struct ~
+                     ~A ~A__instance;~%~
+                   #define ~A (&~A__instance.~A.~A)~%"
+           externp constp (ilayout-struct-tag class) name
+           name name (sod-class-nickname (sod-class-chain-head class))
+           (sod-class-nickname class))))
+
+(defmethod hook-output
+    ((instance static-instance) (reason (eql :h)) sequencer)
+  "Write an `extern' declaration for an external static instance."
+  (with-slots (externp) instance
+    (when externp
+      (one-off-output 'static-instances-banner sequencer
+                     '(:static-instances :start)
+                     (lambda (stream)
+                       (banner "Public static instances" stream)))
+      (one-off-output 'static-instances-end sequencer
+                     '(:static-instances :end)
+                     #'terpri)
+      (sequence-output (stream sequencer)
+       (:static-instances
+        (declare-static-instance instance stream))))))
+
+;;;--------------------------------------------------------------------------
 ;;; Implementation output.
 
 (export '*instance-class*)
@@ -869,4 +904,134 @@ const struct ~A ~A__classobj = {~%"
        ((instance :object super :slots)
         (output-class-initializer slot instance stream))))))
 
+;;;--------------------------------------------------------------------------
+;;; Static instances.
+
+(export '*static-instance*)
+(defvar-unbound *static-instance*
+  "The static instance currently being output.
+
+   This is bound during the `hook-output' traversal of a static instance for
+   `:c', since the slots traversed need to be able to look up initializers
+   from the static instance definition.")
+
+(defmethod hook-output ((instance static-instance)
+                       (reason (eql :c)) sequencer)
+  "Write a static instance definition."
+  (with-slots (externp) instance
+    (one-off-output 'static-instances-banner sequencer
+                   '(:static-instances :start)
+                   (lambda (stream)
+                     (banner "Static instance definitions" stream)))
+    (unless externp
+      (one-off-output 'static-instances-forward sequencer
+                     '(:static-instances :start)
+                     (lambda (stream)
+                       (format stream "/* Forward declarations. */~%")))
+      (one-off-output 'static-instances-forward-gap sequencer
+                     '(:static-instances :gap)
+                     #'terpri)
+      (sequence-output (stream sequencer)
+       ((:static-instances :decls)
+        (declare-static-instance instance stream))))))
+
+(defmethod hook-output ((class sod-class)
+                       (reason (eql 'static-instance)) sequencer)
+  "Output the framing around a static instance initializer."
+  (let ((instance *static-instance*))
+    (with-slots ((class %class) name externp constp) instance
+      (sequence-output (stream sequencer)
+       :constraint ((:static-instances :gap)
+                    (*static-instance* :start)
+                    (*static-instance* :end)
+                    (:static-instances :end))
+       ((*static-instance* :start)
+        (format stream "/* Static instance `~A'. */~%~
+                      ~:[static ~;~]~:[~;const ~]~
+                        struct ~A ~A__instance = {~%"
+                name
+                externp constp
+                (ilayout-struct-tag class) name))
+       ((*static-instance* :end)
+        (format stream "};~2%"))))))
+
+(defmethod hook-output ((ichain ichain)
+                       (reason (eql 'static-instance)) sequencer)
+  "Output the initializer for an ichain."
+  (with-slots ((class %class) chain-head chain-tail) ichain
+    (sequence-output (stream sequencer)
+      :constraint ((*static-instance* :start)
+                  (*static-instance* :ichain chain-head :start)
+                  (*static-instance* :ichain chain-head :end)
+                  (*static-instance* :end))
+      ((*static-instance* :ichain chain-head :start)
+       (format stream "  { { /* ~A ichain */~%"
+              (sod-class-nickname chain-head)))
+      ((*static-instance* :ichain chain-head :end)
+       (format stream "  } },~%")))))
+
+(defmethod hook-output ((islots islots)
+                       (reason (eql 'static-instance)) sequencer)
+  "Initialize a static instance's slots."
+  (with-slots ((class %class)) islots
+    (let ((chain-head (sod-class-chain-head class)))
+      (sequence-output (stream sequencer)
+       :constraint
+       ((*static-instance* :ichain chain-head :start)
+        (*static-instance* :slots class :start)
+        (*static-instance* :slots class)
+        (*static-instance* :slots class :end)
+        (*static-instance* :ichain chain-head :end))
+       ((*static-instance* :slots class :start)
+        (format stream "      { /* Class ~A */~%" class))
+       ((*static-instance* :slots class :end)
+        (format stream "      },~%"))))))
+
+(defmethod hook-output ((vtptr vtable-pointer)
+                       (reason (eql 'static-instance)) sequencer)
+  "Initialize a vtable pointer in a static instance.."
+  (with-slots ((class %class) chain-head chain-tail) vtptr
+    (sequence-output (stream sequencer)
+      :constraint ((*static-instance* :ichain chain-head :start)
+                  (*static-instance* :vtable chain-head)
+                  (*static-instance* :ichain chain-head :end))
+      ((*static-instance* :vtable chain-head)
+       (format stream "      /* ~17@A = */ &~A.~A,~%"
+              "_vt"
+              (vtable-name class chain-head)
+              (sod-class-nickname chain-tail))))))
+
+(export 'output-static-instance-initializer)
+(defgeneric output-static-instance-initializer (instance slot stream)
+  (:documentation
+   "Output an initializer for an effective SLOT in a static INSTANCE."))
+(defmethod output-static-instance-initializer ((instance static-instance)
+                                              (slot effective-slot)
+                                              stream)
+  (let* ((direct-slot (effective-slot-direct-slot slot))
+        (init (or (find direct-slot
+                        (static-instance-initializers instance)
+                        :key #'sod-initializer-slot)
+                  (effective-slot-initializer slot))))
+    (format stream "        /* ~15@A = */ ~A,~%"
+           (sod-slot-name direct-slot)
+           (sod-initializer-value init))))
+
+(defmethod hook-output ((slot effective-slot)
+                       (reason (eql 'static-instance)) sequencer)
+  "Initialize a slot in a static instance."
+  (with-slots ((class %class) initializers) *static-instance*
+    (with-slots ((dslot slot)) slot
+      (let ((super (sod-slot-class dslot))
+           (instance *static-instance*))
+      (sequence-output (stream sequencer)
+       ((instance :slots super)
+        (output-static-instance-initializer instance slot stream)))))))
+
+(defmethod hook-output :after
+    ((instance static-instance) (reason (eql :c)) sequencer)
+  (with-slots ((class %class)) instance
+    (let ((*static-instance* instance))
+      (hook-output class 'static-instance sequencer))))
+
 ;;;----- That's all, folks --------------------------------------------------
index 69df4d1..b21f3c0 100644 (file)
            (sod-method-message method)
            (sod-method-class method))))
 
+;;;--------------------------------------------------------------------------
+;;; Instances.
+
+(export '(static-instance static-instance-name static-instance-extern-p
+         static-instance-const-p static-instance-class
+         static-instance-initializers))
+(defclass static-instance ()
+  ((name :initarg :name :type string :reader static-instance-name)
+   (location :initarg :location :initform (file-location nil)
+            :type file-location :reader file-location)
+   (externp :initarg :extern :initform nil :type t
+           :reader static-instance-extern-p)
+   (constp :initarg :const :initform t :type t
+          :reader static-instance-const-p)
+   (%class :initarg :class :type sod-class :reader static-instance-class)
+   (initializers :initarg :initializers :initform nil
+                :type list :accessor static-instance-initializers))
+  (:documentation
+   "A static instance is a class instance built at (C) compile time.
+
+   The slots are as follows.
+
+     * The `name' gives the C identifier naming the instance, as a string.
+
+     * The `externp' flag is non-nil if the instance is to be visible outside
+       of the translation unit.
+
+     * The `location' states where, in the user's source, the instance was
+       defined.  This gets used in error messages.
+
+     * The `class' specifies the class of the instance to construct.
+
+     * The `initializers' are a list of `sod-instance-initializer' objects
+       which override any existing slot initializers defined on the class."))
+
+(defmethod print-object ((instance static-instance) stream)
+  (with-slots (name (class %class) externp constp initializers) instance
+    (maybe-print-unreadable-object (instance stream :type t)
+      (format stream "~:[~;extern ~@_~]~:[~;const ~@_~]~A ~@_~A"
+             externp constp class name)
+      (when initializers
+       (princ ": " stream)
+       (pprint-indent :block 2 stream)
+       (let ((first t))
+         (dolist (init initializers)
+           (if first (setf first nil) (princ ", "))
+           (pprint-newline :linear stream)
+           (with-slots (slot (super %class) value) init
+             (format stream "~@<~A.~A = ~2I~@_~A~:>"
+                     (sod-class-nickname super)
+                     (sod-slot-name slot)
+                     value))))))))
+
 ;;;----- That's all, folks --------------------------------------------------
index 90ecb75..d09dfd8 100644 (file)
      (:includes :start) :includes :early-decls (:includes :end)
      (:early-user :start) :early-user (:early-user :end)
      (:classes :start) (:classes :end)
+     (:static-instances :start) :static-instances (:static-instances :end)
      (:user :start) :user (:user :end)
      (:guard :end)
      :epilogue)
     (:prologue
      (:includes :start) :includes (:includes :end)
      (:early-user :start) :early-user (:early-user :end)
+     (:static-instances :start)
+     (:static-instances :decls) (:static-instances :gap)
+     (:static-instances :end)
      (:classes :start) (:classes :end)
      (:user :start) :user (:user :end)
      :epilogue)
index 8344281..eff4af7 100644 (file)
             (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)
index de3b354..3188354 100644 (file)
@@ -29,9 +29,9 @@
 * test/bad.sod:40:11: note: Previous definition was here
 * test/bad.sod:40:2: error: Duplicate primary direct method for message `void wrong.frob(void)' in classs `Wrong'
 * test/bad.sod:38:2: note: Previous definition was here
-* test/bad.sod:48:29: syntax error: Expected `class', `set', `code', `typename', `import', `load', `lisp', or `demo' but found `{'
-* test/bad.sod:48:49: syntax error: Expected `class', `set', `code', `typename', `import', `load', `lisp', or `demo' but found `}'
-* test/bad.sod:49:0: syntax error: Expected `class', `set', `code', `typename', `import', `load', `lisp', or `demo' but found `}'
+* test/bad.sod:48:29: syntax error: Expected `class', `lisp', `load', `import', `typename', `code', `set', `instance', or `demo' but found `{'
+* test/bad.sod:48:49: syntax error: Expected `class', `lisp', `load', `import', `typename', `code', `set', `instance', or `demo' but found `}'
+* test/bad.sod:49:0: syntax error: Expected `class', `lisp', `load', `import', `typename', `code', `set', `instance', or `demo' but found `}'
 * test/bad.sod:54:30: error: Type mismatch for keyword argument `x' in methods for message `void fail.badkw(?int x)' applicable to class `Arrgh'
 * test/bad.sod:56:2: note: Type `double' declared in primary direct method of `Arrgh' (defined here)
 * test/bad.sod:51:43: note: Type `int' declared in message definition in `Fail' (here)
 * test/bad.sod:90:0: error: Class `dismissed' is incomplete
 * test/bad.sod:90:0: syntax error: Expected `{' but found `;'
 * test/bad.sod:90:0: syntax error: Expected `}' but found `;'
-* test/bad.sod:90:0: syntax error: Expected `class', `set', `code', `typename', `import', `load', `lisp', or `demo' but found `;'
-* sod: Finished with 38 errors
+* test/bad.sod:90:0: syntax error: Expected `class', `lisp', `load', `import', `typename', `code', `set', `instance', or `demo' but found `;'
+* test/bad.sod:93:16: error: No initializer for instance slot `int st.x', required by static instance `noinit'
+* test/bad.sod:92:43: note: Slot `int st.x' defined here
+* test/bad.sod:93:16: error: No initializer for instance slot `int st.y', required by static instance `noinit'
+* test/bad.sod:92:46: note: Slot `int st.y' defined here
+* test/bad.sod:94:43: error: Duplicate initializer for instance slot `int st.x' in static instance `dupinit'
+* test/bad.sod:94:33: note: Previous definition was here
+* sod: Finished with 41 errors
 ; rc = 2
index 2799e0e..6451482 100644 (file)
@@ -88,3 +88,7 @@ class dismissed;
 
 class hopeful: dismissed
 ;
+
+[nick = st] class Static: SodObject { int x, y; }
+instance Static noinit;
+instance Static dupinit: st.x = 1, st.x = 2, st.y = 3;
index 72febd5..5a029cd 100644 (file)
@@ -353,4 +353,26 @@ code c: tests {
   DONE(3);
 }
 
+/*----- Static instances --------------------------------------------------*/
+
+[link = SodObject, nick = st]
+class StaticObject: SodObject {
+  int x, y = 2, z = 3;
+  [readonly = t] void step() { STEP(me->st.x); }
+}
+
+[extern = t] instance StaticObject mystatic: st.x = 0, st.z = 69;
+[const = nil] instance StaticObject otherstatic: st.x = 3, st.y = 42;
+
+code c: tests {
+  prepare("static instance");
+  StaticObject_step(mystatic);
+  if (mystatic->st.y == 2) STEP(1);
+  if (mystatic->st.z == 69) STEP(2);
+  StaticObject_step(otherstatic);
+  if (otherstatic->st.y == 42) STEP(4);
+  if (otherstatic->st.z == 3) STEP(5);
+  DONE(6);
+}
+
 /*----- That's all, folks -------------------------------------------------*/