chiark / gitweb /
src/method-impl.lisp: Abolish the `emf-entry-tail' variable.
[sod] / src / module-parse.lisp
index 2fa13f136581c551e57313629110a5c523d90816..95220852b25ed6e7e07e8da35c8b23d4d49d9962 100644 (file)
@@ -7,7 +7,7 @@
 
 ;;;----- Licensing notice ---------------------------------------------------
 ;;;
-;;; This file is part of the Sensble Object Design, an object system for C.
+;;; This file is part of the Sensible Object Design, an object system for C.
 ;;;
 ;;; SOD is free software; you can redistribute it and/or modify
 ;;; it under the terms of the GNU General Public License as published by
@@ -28,8 +28,6 @@ (in-package #:sod)
 ;;;--------------------------------------------------------------------------
 ;;; Toplevel syntax.
 
-(export 'module)
-
 ;;; Type names.
 
 (define-pluggable-parser module typename (scanner pset)
@@ -50,32 +48,43 @@ (define-pluggable-parser module typename (scanner pset)
 ;;; Fragments.
 
 (define-pluggable-parser module code (scanner pset)
-  ;; `code' id `:' id [constraints] `{' c-fragment `}'
+  ;; `code' id `:' item-name [constraints] `{' c-fragment `}'
   ;;
   ;; constrains ::= `[' constraint-list `]'
-  ;; constraint ::= id+
+  ;; constraint ::= item-name+
+  ;; item-name ::= id | `(' id+ `)'
   (declare (ignore pset))
   (with-parser-context (token-scanner-context :scanner scanner)
-    (parse (seq ("code"
-                (reason :id)
-                #\:
-                (name :id)
-                (constraints (? (seq (#\[
-                                      (constraints (list (:min 1)
-                                                     (list (:min 1) :id)
-                                                     #\,))
-                                      #\])
-                                  constraints)))
-                (fragment (parse-delimited-fragment scanner #\{ #\})))
-            (add-to-module *module* (make-instance 'code-fragment-item
-                                                   :fragment fragment
-                                                   :constraints constraints
-                                                   :reason reason
-                                                   :name name))))))
+    (labels ((kw ()
+              (parse (seq ((kw :id))
+                       (intern (frob-identifier kw) 'keyword))))
+            (item ()
+              (parse (or (kw)
+                         (seq (#\( (names (list (:min 1) (kw))) #\))
+                           names)))))
+      (parse (seq ("code"
+                  (reason (kw))
+                  #\:
+                  (name (item))
+                  (constraints (? (seq (#\[
+                                        (constraints (list (:min 1)
+                                                       (list (:min 1)
+                                                         (item))
+                                                       #\,))
+                                        #\])
+                                    constraints)))
+                  (fragment (parse-delimited-fragment scanner #\{ #\})))
+              (add-to-module *module*
+                             (make-instance 'code-fragment-item
+                                            :fragment fragment
+                                            :constraints constraints
+                                            :reason reason
+                                            :name name)))))))
 
 ;;; External files.
 
-(defun read-module (pathname &key (truename (truename pathname)) location)
+(export 'read-module)
+(defun read-module (pathname &key (truename nil truep) location)
   "Parse the file at PATHNAME as a module, returning it.
 
    This is the main entry point for parsing module files.  You may well know
@@ -86,6 +95,9 @@ (defun read-module (pathname &key (truename (truename pathname)) location)
    `file-location' object, though it might be anything other than `t' which
    can be printed in the event of circular imports."
 
+  (setf pathname (merge-pathnames pathname
+                                 (make-pathname :type "SOD" :case :common)))
+  (unless truep (setf truename (truename pathname)))
   (define-module (pathname :location location :truename truename)
     (with-open-file (f-stream pathname :direction :input)
       (let* ((*readtable* (copy-readtable))
@@ -143,6 +155,29 @@ (define-pluggable-parser module file (scanner pset)
                                 (cerror* "Error loading Lisp file ~S: ~A"
                                          path error)))))))))))
 
+;;; Setting properties.
+
+(define-pluggable-parser module set (scanner pset)
+  ;; `set' property-list `;'
+  (with-parser-context (token-scanner-context :scanner scanner)
+    (parse (and "set"
+               (lisp (let ((module-pset (module-pset *module*)))
+                       (when pset
+                         (pset-map (lambda (prop)
+                                     (add-property module-pset
+                                                   (p-name prop)
+                                                   (p-value prop)
+                                                   :type (p-type prop)
+                                                   :location (p-location prop))
+                                     (setf (p-seenp prop) t))
+                                   pset))
+                       (parse (skip-many (:min 0)
+                                (error (:ignore-unconsumed t)
+                                  (parse-property scanner module-pset)
+                                  (skip-until (:keep-end t) #\, #\;))
+                                #\,))))
+               #\;))))
+
 ;;; Lisp escape.
 
 (define-pluggable-parser module lisp (scanner pset)
@@ -162,6 +197,8 @@ (define-pluggable-parser module lisp (scanner pset)
 ;;;--------------------------------------------------------------------------
 ;;; Class declarations.
 
+(export 'class-item)
+
 (defun parse-class-body (scanner pset name supers)
   ;; class-body ::= `{' class-item* `}'
   ;;
@@ -192,9 +229,16 @@ (defun parse-class-body (scanner pset name supers)
               (parse-message-item (sub-pset type name)
                 ;; message-item ::=
                 ;;     declspec+ declarator -!- (method-body | `;')
-                (make-sod-message class name type sub-pset scanner)
-                (parse (or #\; (parse-method-item sub-pset
-                                                  type nick name))))
+                ;;
+                ;; Don't allow a method-body here if the message takes a
+                ;; varargs list, because we don't have a name for the
+                ;; `va_list' parameter.
+                (let ((message (make-sod-message class name type
+                                                 sub-pset scanner)))
+                  (if (varargs-message-p message)
+                      (parse #\;)
+                      (parse (or #\; (parse-method-item sub-pset
+                                                        type nick name))))))
 
               (parse-method-item (sub-pset type sub-nick name)
                 ;; method-item ::=
@@ -212,13 +256,46 @@ (defun parse-class-body (scanner pset name supers)
                 ;;
                 ;; Return (VALUE-KIND . VALUE-FORM), ready for passing to a
                 ;; `sod-initializer' constructor.
-                (parse (or (peek (seq (#\= (frag (parse-delimited-fragment
-                                                  scanner #\{ #\})))
-                                   (cons :compound frag)))
-                           (seq ((frag (parse-delimited-fragment
-                                        scanner #\= '(#\; #\,)
-                                        :keep-end t)))
-                             (cons :simple frag)))))
+
+                ;; This is kind of tricky because we have to juggle both
+                ;; layers of the parsing machinery.  The character scanner
+                ;; will already have consumed the lookahead token (which, if
+                ;; we're going to do anything, is `=').
+                (let ((char-scanner (token-scanner-char-scanner scanner)))
+
+                  ;; First, skip the character-scanner past any whitespace.
+                  ;; We don't record this consumption, which is a bit
+                  ;; naughty, but nobody will actually mind.
+                  (loop
+                    (when (or (scanner-at-eof-p char-scanner)
+                              (not (whitespace-char-p
+                                    (scanner-current-char char-scanner))))
+                      (return))
+                    (scanner-step char-scanner))
+
+                  ;; Now maybe read an initializer.
+                  (cond ((not (eql (token-type scanner) #\=))
+                         ;; It's not an `=' after all.  There's no
+                         ;; initializer.
+                         (values '(#\=) nil nil))
+
+                        ((and (not (scanner-at-eof-p char-scanner))
+                              (char= (scanner-current-char char-scanner)
+                                     #\{))
+                         ;; There's a brace after the `=', so we should
+                         ;; consume the `=' here, and read a compound
+                         ;; initializer enclosed in braces.
+                         (parse (seq (#\= (frag (parse-delimited-fragment
+                                                 scanner #\{ #\})))
+                                  (cons :compound frag))))
+
+                        (t
+                         ;; No brace, so read from the `=' up to, but not
+                         ;; including, the trailing `,' or `;' delimiter.
+                         (parse (seq ((frag (parse-delimited-fragment
+                                             scanner #\= '(#\; #\,)
+                                             :keep-end t)))
+                                  (cons :simple frag)))))))
 
               (parse-slot-item (sub-pset base-type type name)
                 ;; slot-item ::=
@@ -315,10 +392,9 @@ (defun parse-class-body (scanner pset name supers)
        (parse (seq (#\{
                     (nil (skip-many ()
                            (seq ((sub-pset (parse-property-set scanner))
-                                 (nil (error ()
-                                             (parse-raw-class-item sub-pset))))
+                                 (nil (parse-raw-class-item sub-pset)))
                              (check-unused-properties sub-pset))))
-                    #\})
+                    (nil (error () #\})))
                 (finalize-sod-class class)
                 (add-to-module *module* class)))))))