;;;----- 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
;; 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))
(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)
: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.")
;; 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))
(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)
(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))
(make-simple-type (format nil "~{~@[~A~^ ~]~}"
(mapcar #'ds-name
(remove nil
- (list sign size type))))
+ (list sign cplx
+ size type))))
quals))
(t
nil))))
;; 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)
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 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 ((kernel-parser (cond (kernel kernel)
(abstractp (parser () (? :id)))
(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: `('
;; 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 #\))))))
(cons #'identity name))))
(argument-list ()
- ;; [ argument [ `,' argument ]* ]
-
- (parse (list (:min 0)
- (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 `)'