;; 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))
;;; `parse-declarator' will be of this form.
(export 'parse-declarator)
-(defun parse-declarator (scanner base-type &key kernel abstractp)
+(defun parse-declarator (scanner base-type &key kernel abstractp keywordp)
"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
defaults to matching a simple identifier `:id'. This might, e.g., be
(? :id) to parse an `abstract declarator' which has optional names.
+ If KEYWORDP is true, then a keyword argument list is permitted in
+ function declarations.
+
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 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)))
'qualifier)))))))
(mapcar #'ds-label quals))))
+ (disallow-keyword-functions (type)
+ (when (typep type 'c-keyword-function-type)
+ (error "Functions with keyword arguments are only ~
+ allowed at top-level.")))
+
(star ()
;; Prefix: `*' qualifiers
(parse (seq (#\* (quals (qualifiers)))
(preop "*" (state 9)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
(make-pointer-type type quals)))
(cdr state))))))
(parse (seq ((name (funcall kernel-parser)))
(cons #'identity name))))
+ (arg-decl (abstractp)
+ (parse (seq ((base-type (parse-c-type scanner))
+ (dtor (parse-declarator scanner base-type
+ :abstractp abstractp)))
+ dtor)))
+
+ (argument ()
+ ;; argument ::= type abstract-declspec
+
+ (parse (seq ((dtor (arg-decl t)))
+ (make-argument (cdr dtor) (car dtor)))))
+
+ (kw-argument ()
+ ;; kw-argument ::= type declspec [= c-fragment]
+
+ (parse (seq ((dtor (arg-decl nil))
+ (dflt (? (when (eq (token-type scanner) #\=)
+ (parse-delimited-fragment
+ scanner #\= '(#\, #\))
+ :keep-end t)))))
+ (make-argument (cdr dtor) (car dtor) dflt))))
+
(argument-list ()
- ;; [argument [`,' argument]* [`,' `...']] | `...'
+ ;; argument-list ::=
+ ;; [argument [`,' argument]* [`,' argument-tail]]
+ ;; | argument-tail
+ ;;
+ ;; argument-tail ::= `...' | keyword-tail
+ ;;
+ ;; keyword-tail ::= `?' [kw-argument [`,' kw-argument]*]
+ ;;
+ ;; kw-argument ::= argument [= c-fragment]
;;
;; 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))
+ (let ((args nil)
+ (keys nil)
+ (keysp nil))
(loop
(when (eq (token-type scanner) :ellipsis)
(push :ellipsis args)
(scanner-step scanner)
(return))
+ (when (and keywordp (eq (token-type scanner) #\?))
+ (setf keysp t)
+ (scanner-step scanner)
+ (multiple-value-bind (arg winp consumedp)
+ (parse (list (:min 0) (kw-argument) #\,))
+ (declare (ignore consumedp))
+ (unless winp
+ (return-from argument-list (values arg nil t)))
+ (setf keys arg)
+ (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))))
+ (argument)
(unless winp
(if (or consumedp args)
(return-from argument-list (values arg nil t))
(unless (eq (token-type scanner) #\,)
(return))
(scanner-step scanner))
- (values (nreverse args) t args)))
+ (values (let ((rargs (nreverse args))
+ (rkeys (nreverse keys)))
+ (if keysp
+ (lambda (ret)
+ (make-keyword-function-type
+ ret rargs rkeys))
+ (lambda (ret)
+ (make-function-type ret rargs))))
+ t
+ (or args keysp))))
(postfix-lparen ()
;; Postfix: `(' argument-list `)'
- (parse (seq (#\( (args (argument-list)) #\))
+ (parse (seq (#\( (make (argument-list)) #\))
(postop "()" (state 10)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
- (make-function-type type args)))
+ (funcall make type)))
(cdr state))))))
(dimension ()
(parse (seq ((dims (list (:min 1) (dimension))))
(postop "[]" (state 10)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
(make-array-type type dims)))
(cdr state)))))))