X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/ea578bb4b9eb4a03b2eb4ed151e058d699c216ea..9761db0da830385bcc0fca81f56f24536a46aeda:/src/c-types-parse.lisp diff --git a/src/c-types-parse.lisp b/src/c-types-parse.lisp index 5f2e438..6a622b7 100644 --- a/src/c-types-parse.lisp +++ b/src/c-types-parse.lisp @@ -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 @@ ;; 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 @@ (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 @@ :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 @@ ;; 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-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 @@ (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 @@ (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 @@ ;; 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 @@ ;;; `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 @@ -303,10 +315,23 @@ 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))) @@ -326,23 +351,34 @@ '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)))))) - (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,7 +393,7 @@ ;; 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 #\)))))) @@ -366,25 +402,92 @@ (parse (seq ((name (funcall kernel-parser))) (cons #'identity name)))) - (argument-list () - ;; [ argument [ `,' argument ]* ] + (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))))) - (parse (list () - (seq ((base-type (parse-c-type scanner)) - (dtor (parse-declarator scanner - base-type - :abstractp 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-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) + (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) + (argument) + (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 (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 () @@ -400,6 +503,7 @@ (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)))))))