chiark / gitweb /
src/c-types-parse.lisp (parse-declarator): Explain how it works.
[sod] / src / c-types-parse.lisp
index e3ac62553735784fd9d253c696997f4dcd5162fd..c797010762b14ed50eb8611e664cccbfee2dc428 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
@@ -70,7 +70,7 @@ (defclass declspec ()
   ;; accessor functions later.
   ((label :type keyword :initarg :label :reader ds-label)
    (name :type string :initarg :name :reader ds-name)
-   (kind :type (member type sign size qualifier)
+   (kind :type (member type complexity sign size qualifier)
         :initarg :kind :reader ds-kind)
    (taggedp :type boolean :initarg :taggedp
            :initform nil :reader ds-taggedp))
@@ -89,7 +89,10 @@   (default-slot (ds 'name slot-names)
 
 (defparameter *declspec-map*
   (let ((map (make-hash-table :test #'equal)))
-    (dolist (item '((type :void :char :int :float :double)
+    (dolist (item '((type :void :char :int :float :double
+                         (:bool :name "_Bool"))
+                   (complexity (:complex :name "_Complex")
+                               (:imaginary :name "_Imaginary"))
                    ((type :taggedp t) :enum :struct :union)
                    (size :short :long (:long-long :name "long long"))
                    (sign :signed :unsigned)
@@ -110,6 +113,8 @@ (defparameter *declspec-map*
                                     :taggedp taggedp)))
              (setf (gethash name map) ds
                    (gethash label map) ds))))))
+    (dolist (label '(:complex :imaginary :bool))
+      (setf (gethash (string-downcase label) map) (gethash label map)))
     map)
   "Maps symbolic labels and textual names to `declspec' instances.")
 
@@ -119,6 +124,7 @@ (defclass declspecs ()
   ;; This could have been done with `defstruct' just as well, but a
   ;; `defclass' can be tweaked interactively, which is a win at the moment.
   ((type :initform nil :initarg :type :reader ds-type)
+   (complexity :initform nil :initarg :complexity :reader ds-complexity)
    (sign :initform nil :initarg :sign :reader ds-sign)
    (size :initform nil :initarg :size :reader ds-size)
    (qualifier :initform nil :initarg :qualifiers :reader ds-qualifiers))
@@ -142,19 +148,22 @@ (defmethod ds-name ((ty c-type)) (princ-to-string ty))
 (defmethod ds-kind ((ty c-type)) 'type)
 
 (defparameter *good-declspecs*
-  '(((:int) (:signed :unsigned) (:short :long :long-long))
-    ((:char) (:signed :unsigned) ())
-    ((:double) () (:long))
-    (t () ()))
+  '(((:int) (:signed :unsigned) (:short :long :long-long) ())
+    ((:char) (:signed :unsigned) () ())
+    ((:double) () (:long) (:complex :imaginary))
+    (t () () ()))
   "List of good collections of declaration specifiers.
 
-   Each item is a list of the form (TYPES SIGNS SIZES).  Each of TYPES, SIGNS
-   and SIZES is either a list of acceptable specifiers of the appropriate
-   kind, or T, which matches any specifier.")
+   Each item is a list of the form (TYPES SIGNS SIZES COMPLEXITIES).  Each of
+   TYPES, SIGNS, SIZES, and COMPLEXITIES, is either a list of acceptable
+   specifiers of the appropriate kind, or T, which matches any specifier.")
 
 (defun good-declspecs-p (specs)
   "Are SPECS a good collection of declaration specifiers?"
-  (let ((speclist (list (ds-type specs) (ds-sign specs) (ds-size specs))))
+  (let ((speclist (list (ds-type specs)
+                       (ds-sign specs)
+                       (ds-size specs)
+                       (ds-complexity specs))))
     (some (lambda (it)
            (every (lambda (spec pat)
                     (or (eq pat t) (null spec)
@@ -189,10 +198,11 @@ (defun declspecs-type (specs)
   (let ((type (ds-type specs))
        (size (ds-size specs))
        (sign (ds-sign specs))
+       (cplx (ds-complexity specs))
        (quals (mapcar #'ds-label (ds-qualifiers specs))))
     (cond ((typep type 'c-type)
           (qualify-c-type type quals))
-         ((or type size sign)
+         ((or type size sign cplx)
           (when (and sign (eq (ds-label sign) :signed)
                      (eq (ds-label type) :int))
             (setf sign nil))
@@ -204,7 +214,8 @@ (defun declspecs-type (specs)
           (make-simple-type (format nil "~{~@[~A~^ ~]~}"
                                     (mapcar #'ds-name
                                             (remove nil
-                                                    (list sign size type))))
+                                                    (list sign cplx
+                                                          size type))))
                             quals))
          (t
           nil))))
@@ -229,11 +240,12 @@ (defun scan-declspec
   ;; Turns out to be easier to do this by hand.
   (let ((ds (and (eq (token-type scanner) :id)
                 (let ((kw (token-value scanner)))
-                  (or (gethash kw *module-type-map*)
+                  (or (and (boundp '*module-type-map*)
+                           (gethash kw *module-type-map*))
                       (gethash kw *declspec-map*))))))
     (cond ((or (not ds) (and predicate (not (funcall predicate ds))))
           (values (list indicator) nil nil))
-         ((ds-taggedp ds)
+         ((and (typep ds 'declspec) (ds-taggedp ds))
           (scanner-step scanner)
           (if (eq (token-type scanner) :id)
               (let ((ty (make-c-tagged-type (ds-label ds)
@@ -290,7 +302,7 @@ (defun parse-c-type (scanner)
 ;;; `parse-declarator' will be of this form.
 
 (export 'parse-declarator)
-(defun parse-declarator (scanner base-type &key centre abstractp)
+(defun parse-declarator (scanner base-type &key kernel abstractp)
   "Parse a C declarator, returning a pair (C-TYPE . NAME).
 
    The SCANNER is a token scanner to read from.  The BASE-TYPE is the type
@@ -299,16 +311,26 @@ (defun parse-declarator (scanner base-type &key centre abstractp)
 
    The result contains both the resulting constructed C-TYPE (with any
    qualifiers etc. as necessary), and the name from the middle of the
-   declarator.  The name is parsed using the CENTRE parser provided, and
+   declarator.  The name is parsed using the KERNEL parser provided, and
    defaults to matching a simple identifier `:id'.  This might, e.g., be
    (? :id) to parse an `abstract declarator' which has optional names.
 
-   There's an annoying ambiguity in the syntax, if an empty CENTRE is
+   There's an annoying ambiguity in the syntax, if an empty KERNEL is
    permitted.  In this case, you must ensure that ABSTRACTP is true so that
    the appropriate heuristic can be applied.  As a convenience, if ABSTRACTP
-   is true then `(? :id)' is used as the default CENTRE."
+   is true then `(? :id)' is used as the default KERNEL."
+
+  ;; This is a bit confusing.  This is a strangely-shaped operator grammer,
+  ;; which wouldn't be so bad, but the `values' being operated on are pairs
+  ;; of the form (FUNC . NAME).  The NAME is whatever the KERNEL parser
+  ;; produces as its result, and will be passed out unchanged.  The FUNC is a
+  ;; type-constructor function which will be eventually be applied to the
+  ;; input BASE-TYPE, but we can't calculate the actual result as we go along
+  ;; because of the rather annoying inside-out nature of the declarator
+  ;; syntax.
+
   (with-parser-context (token-scanner-context :scanner scanner)
-    (let ((centre-parser (cond (centre centre)
+    (let ((kernel-parser (cond (kernel kernel)
                               (abstractp (parser () (? :id)))
                               (t (parser () :id)))))
 
@@ -336,13 +358,18 @@ (defun parse-declarator (scanner base-type &key centre abstractp)
                                             (make-pointer-type type quals)))
                                  (cdr state))))))
 
-              (next-declspec-p ()
-                ;; Ansert whether the next token is a valid declaration
-                ;; specifier, without consuming it.
-                (and (eq (token-type scanner) :id)
-                     (let ((id (token-value scanner)))
-                       (or (gethash id *module-type-map*)
-                           (gethash id *declspec-map*)))))
+              (predict-argument-list-p ()
+                ;; See `prefix-lparen'.  Predict an argument list rather
+                ;; than a nested declarator if (a) abstract declarators are
+                ;; permitted and (b) the next token is a declaration
+                ;; specifier or ellipsis.
+                (let ((type (token-type scanner))
+                      (value (token-value scanner)))
+                  (and abstractp
+                       (or (eq type :ellipsis)
+                           (and (eq type :id)
+                                (or (gethash value *module-type-map*)
+                                    (gethash value *declspec-map*)))))))
 
               (prefix-lparen ()
                 ;; Prefix: `('
@@ -357,25 +384,44 @@ (defun parse-declarator (scanner base-type &key centre abstractp)
                 ;; specifier, then we have a postfix argument list.
                 (parse
                   (peek (seq (#\(
-                              (nil (if (and abstractp (next-declspec-p))
+                              (nil (if (predict-argument-list-p)
                                        (values nil nil nil)
                                        (values t t nil))))
                           (lparen #\))))))
 
-              (centre ()
-                (parse (seq ((name (funcall centre-parser)))
+              (kernel ()
+                (parse (seq ((name (funcall kernel-parser)))
                          (cons #'identity name))))
 
               (argument-list ()
-                ;; [ argument [ `,' argument ]* ]
-
-                (parse (list ()
-                             (seq ((base-type (parse-c-type scanner))
-                                   (dtor (parse-declarator scanner
-                                                           base-type
-                                                           :abstractp t)))
-                               (make-argument (cdr dtor) (car dtor)))
-                             #\,)))
+                ;; [argument [`,' argument]* [`,' `...']] | `...'
+                ;;
+                ;; The possibility of a trailing `,' `...' means that we
+                ;; can't use the standard `list' parser.  Note that, unlike
+                ;; `real' C, we allow an ellipsis even if there are no
+                ;; explicit arguments.
+
+                (let ((args nil))
+                  (loop
+                    (when (eq (token-type scanner) :ellipsis)
+                      (push :ellipsis args)
+                      (scanner-step scanner)
+                      (return))
+                    (multiple-value-bind (arg winp consumedp)
+                        (parse (seq ((base-type (parse-c-type scanner))
+                                     (dtor (parse-declarator scanner
+                                                             base-type
+                                                             :abstractp t)))
+                                 (make-argument (cdr dtor) (car dtor))))
+                      (unless winp
+                        (if (or consumedp args)
+                            (return-from argument-list (values arg nil t))
+                            (return)))
+                      (push arg args))
+                    (unless (eq (token-type scanner) #\,)
+                      (return))
+                    (scanner-step scanner))
+                  (values (nreverse args) t args)))
 
               (postfix-lparen ()
                 ;; Postfix: `(' argument-list `)'
@@ -408,7 +454,7 @@ (defun parse-declarator (scanner base-type &key centre abstractp)
        (parse (seq ((value (expr (:nestedp nestedp)
 
                              ;; An actual operand.
-                             (centre)
+                             (kernel)
 
                              ;; Binary operators.  There aren't any.
                              nil