From: Mark Wooding Date: Thu, 26 May 2016 08:26:09 +0000 (+0100) Subject: Merge branches 'mdw/kwargs' and 'mdw/c11' X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/sod/commitdiff_plain/f64eb323a5798e155cc494043f5f750abf50a482?ds=inline;hp=-c Merge branches 'mdw/kwargs' and 'mdw/c11' * mdw/kwargs: (29 commits) New feature: initialization keyword arguments. src/method-{proto,impl}.lisp: New `method-keyword-argument-lists' protocol. New feature: proper object lifecycle protocol; init and teardown fragments. src/builtin.lisp: Bind `me' around slot initializers, and define the order. Replace the `init' class-slot function with an `init' message. Compatibility: the `init' function no longer calls `imprint' for you. src/method-{proto,impl}.lisp: Introduce `effective-method-live-p' protocol. doc/runtime.tex, lib/sod.3: Restructure the runtime library reference. doc/concepts.tex: Reorganize the instance lifecycle material. doc/: Miscellaneous clarifications and rewordings. doc/concepts.tex: Don't highlight `primary' as literal, because it's not. src/: Abolish the distinction between different kinds of initializers. src/method-{proto,impl}.lisp: Abolish `sod-message-no-varargs-tail'. New feature: messages with keyword arguments! lib/: Pure C machinery for handling `keyword arguments' to functions. doc/runtime.tex: Demote the object-system support stuff to a section. src/c-types-*.lisp: New type for functions which take keyword arguments. src/c-types-parse.lisp (parse-declarator): Refactor argument list parsing. src/c-types-parse.lisp (parse-declarator): Explain how it works. src/: New function `reify-variable-argument-tail'. ... * mdw/c11: src/c-types-impl.lisp, src/c-types-parse.lisp: Support C11 `_Alignas'. src/c-types-{proto,impl,parse}.lisp: Add `storage specifiers' to the model. src/c-types-{impl,parse}.lisp: Support C11 `_Atomic'. src/c-types-{impl,parse}.tex: Support `_Atomic' types. src/c-types-parse.lisp: Introduce a pluggable parser for declspecs. src/c-types-parse.lisp: Hoist up the `ds-FOO' methods for raw types. src/c-types-parse.lisp: Improve handling of compatibility keywords. src/final.lisp: Add function for interactively testing type parsing. src/c-types-proto.lisp, src/c-types-impl.lisp: Qualifier name protocol. src/module-parse.lisp (parse-raw-class-item): Commit after declarator. Add a new class slot `align', holding the instance layout alignment. src/c-types-impl.lisp (make-or-intern-c-type): Pull out useful function. src/c-types-parse.lisp, src/c-types-proto.lisp: Some minor cleanups. src/parser/parser-proto.lisp: Add functions returning standard parsers. src/parser/: Allow parsers to commit to a parse while peeking. doc/clang.tex: Improve documentation of C type spec expansion. Conflicts: doc/SYMBOLS (regenerated) doc/syntax.tex (hand-merge; extra parameter to ) lib/sod.h (simple hand-merge) src/sod-module.5 (extra parameter to , added by hand) --- f64eb323a5798e155cc494043f5f750abf50a482 diff --combined doc/SYMBOLS index 00bfb3b,20e2eef..cec9fa3 --- a/doc/SYMBOLS +++ b/doc/SYMBOLS @@@ -16,15 -16,17 +16,19 @@@ c-types-class-impl.lis c-types-impl.lisp cl:* variable function c-type + alignas + alignas-storage-specifier class cl:array class c-type + atomic c-type bool c-type c-array-dimensions generic c-array-type class + c-atomic-type class c-enum-type class c-function-arguments generic + c-function-keywords generic c-function-type class + c-keyword-function-type class c-pointer-type class c-struct-type class c-tagged-type-kind generic @@@ -84,22 -86,18 +88,23 @@@ long-long c-type long-long-int c-type make-array-type function + make-atomic-type function make-c-tagged-type function make-enum-type function make-function-type function + make-keyword-function-type function make-pointer-type function make-simple-type function make-struct-type function make-union-type function + merge-keyword-lists function cl:nil constant c-type parser pointer c-type + pprint-argument-list function + pprint-c-function-type function ptr c-type ptrdiff-t c-type + reify-variable-argument-tail function cl:schar function setf c-type short c-type short-int c-type @@@ -117,6 -115,7 +122,7 @@@ size-t c-type sllong c-type slong c-type + specs c-type sshort c-type cl:string function class c-type opthandler struct c-type @@@ -148,30 -147,39 +154,40 @@@ c-types-parse.lis c-types-proto.lisp argument class + argument-default function argument-name function argument-type function argumentp function c-name-case function + c-qualifier-keyword generic + c-storage-specifiers-type class c-type macro class c-type-alias macro c-type-equal-p generic + c-type-qualifier-keywords function c-type-qualifiers generic c-type-space function + c-type-specifiers generic c-type-subtype generic canonify-qualifiers function commentify-argument-name generic defctype macro + define-c-storage-specifier-syntax macro define-c-type-syntax macro + expand-c-storage-specifier generic + expand-c-storage-specifier-form generic expand-c-type-form generic expand-c-type-spec generic format-qualifiers function make-argument function maybe-in-parens macro + pprint-c-storage-specifier generic pprint-c-type generic + print-c-storage-specifier generic print-c-type generic qualifiable-c-type class qualify-c-type generic + wrap-c-type function class-finalize-impl.lisp c3-cpl function @@@ -219,7 -227,6 +235,7 @@@ class-layout-proto.lis effective-slot-class generic effective-slot-direct-slot generic effective-slot-initializer generic + find-slot-initargs generic find-slot-initializer generic ichain class ichain-body generic @@@ -251,28 -258,17 +267,28 @@@ vtmsgs-entries generic vtmsgs-subclass generic +class-make-impl.lisp + check-method-argument-lists function + check-method-return-type function + check-method-return-type-against-message function + class-make-proto.lisp check-message-type generic check-method-type generic make-sod-class function + make-sod-class-initfrag generic make-sod-class-initializer generic + make-sod-class-tearfrag generic make-sod-initializer-using-slot generic make-sod-instance-initializer generic make-sod-message generic make-sod-method generic make-sod-method-using-message generic make-sod-slot generic + make-sod-slot-initarg generic + make-sod-slot-initarg-using-slot generic + make-sod-user-initarg generic + sod-initarg-argument generic sod-message-method-class generic class-output.lisp @@@ -307,7 -303,6 +323,7 @@@ classes.lis sod-class-class-initializers generic setf sod-class-direct-superclasses generic sod-class-ilayout generic + sod-class-initfrags generic setf sod-class-initializer class sod-class-instance-initializers generic setf sod-class-messages generic setf @@@ -318,14 -313,13 +334,14 @@@ sod-class-precedence-list generic sod-class-slots generic setf sod-class-state generic + sod-class-tearfrags generic setf sod-class-type generic sod-class-vtables generic + sod-initarg class sod-initializer class sod-initializer-class generic sod-initializer-slot generic - sod-initializer-value-form generic - sod-initializer-value-kind generic + sod-initializer-value generic sod-instance-initializer class sod-message class sod-message-class generic @@@ -338,10 -332,8 +354,10 @@@ sod-method-type generic sod-slot class sod-slot-class generic + sod-slot-initarg class sod-slot-name generic sod-slot-type generic + sod-user-initarg class codegen-impl.lisp codegen class @@@ -353,7 -345,6 +369,7 @@@ codegen-proto.lis *null-pointer* variable *sod-ap* variable *sod-master-ap* variable + banner-inst class block-inst class break-inst class call-inst class @@@ -364,22 -355,18 +380,22 @@@ codegen-pop-block generic codegen-pop-function generic codegen-push generic + cond-inst class continue-inst class convert-stmts function definst macro deliver-call function deliver-expr function do-while-inst class + emit-banner function emit-decl generic emit-decls generic emit-inst generic emit-insts generic ensure-var generic expr-inst class + for-inst class + format-banner-comment function format-compound-statement macro format-temporary-name generic function-inst class @@@ -387,12 -374,9 +403,12 @@@ inst class inst-alt generic inst-args generic + inst-banner generic + inst-banner-args generic inst-body generic inst-cond generic inst-conseq generic + inst-control generic inst-decls generic inst-expr generic inst-func generic @@@ -401,17 -385,13 +417,17 @@@ inst-name generic inst-op generic inst-type generic + inst-update generic inst-var generic + make-banner-inst function make-block-inst function make-break-inst function make-call-inst function + make-cond-inst function make-continue-inst function make-do-while-inst function make-expr-inst function + make-for-inst function make-function-inst function make-if-inst function make-return-inst function @@@ -435,6 -415,7 +451,7 @@@ final.lis *sod-version* variable exercise function test-module function + test-parse-c-type function test-parser macro fragment-parse.lisp @@@ -490,8 -471,6 +507,8 @@@ method-proto.lis effective-method-basic-argument-names generic effective-method-class generic effective-method-function-name generic + effective-method-keywords generic + effective-method-live-p generic effective-method-message generic ensure-ilayout-var function inst-chain-head generic @@@ -499,7 -478,6 +516,7 @@@ inst-expr generic invoke-delegation-chain function invoke-method function + keyword-message-p function make-convert-to-ilayout-inst function make-method-entries generic make-trampoline function @@@ -511,11 -489,11 +528,11 @@@ method-entry-function-name generic method-entry-function-type generic method-entry-slot-name generic + method-keyword-argument-lists generic primary-method-class generic simple-method-body generic sod-message-argument-tail generic sod-message-effective-method-class generic - sod-message-no-varargs-tail generic sod-method-function-name generic sod-method-function-type generic sod-method-next-method-type generic @@@ -609,13 -587,11 +626,14 @@@ pset-proto.lis store-property function with-pset-iterator macro +Leaked slot names: cl:type + sod-initarg: cl:type + Classes: cl:t sb-pcl::slot-object cl:standard-object + alignas-storage-specifier base-offset sod::basic-codegen codegen @@@ -624,8 -600,9 +642,10 @@@ c-type c-array-type c-function-type + c-keyword-function-type + c-storage-specifiers-type qualifiable-c-type + c-atomic-type c-pointer-type simple-c-type c-class-type @@@ -648,16 -625,13 +668,16 @@@ ichain ilayout inst + banner-inst block-inst break-inst call-inst + cond-inst continue-inst convert-to-ilayout-inst do-while-inst expr-inst + for-inst function-inst if-inst return-inst @@@ -677,9 -651,6 +697,9 @@@ cl:class [sb-pcl::definition-source-mixin sb-pcl::standard-specializer] sequencer sod-class + sod-initarg + sod-slot-initarg + sod-user-initarg sod-initializer sod-class-initializer sod-instance-initializer @@@ -730,8 -701,9 +750,11 @@@ c-fragment-tex c-fragment c-function-arguments c-function-type +c-function-keywords + c-keyword-function-type + c-qualifier-keyword + (eql :atomic) + cl:symbol c-tagged-type-kind c-enum-type c-struct-type @@@ -743,10 -715,12 +766,13 @@@ c-type-clas c-type-equal-p t t c-array-type c-array-type + c-atomic-type c-atomic-type c-class-type c-class-type c-function-type c-function-type + c-keyword-function-type c-keyword-function-type c-pointer-type c-pointer-type + c-storage-specifiers-type c-type + c-type c-storage-specifiers-type qualifiable-c-type qualifiable-c-type simple-c-type simple-c-type tagged-c-type tagged-c-type @@@ -754,10 -728,14 +780,14 @@@ c-type-nam simple-c-type c-type-qualifiers qualifiable-c-type + c-type-specifiers + c-storage-specifiers-type c-type-subtype c-array-type + c-atomic-type c-function-type c-pointer-type + c-storage-specifiers-type c-type-tag tagged-c-type chain-offset-chain-head @@@ -847,7 -825,6 +877,7 @@@ compute-chain compute-cpl sod-class compute-effective-method-body + basic-effective-method t t simple-effective-method t t compute-effective-methods sod-class @@@ -862,8 -839,7 +892,7 @@@ compute-islot sod-class sod-class compute-method-entry-functions basic-effective-method - simple-effective-method + effective-method - simple-effective-method compute-sod-effective-method sod-message sod-class compute-vtable @@@ -895,11 -871,6 +924,11 @@@ effective-method-clas effective-method effective-method-function-name effective-method +effective-method-keywords + effective-method +effective-method-live-p + sod::lifecycle-effective-method + simple-effective-method effective-method-message effective-method effective-slot-class @@@ -921,6 -892,12 +950,12 @@@ ensure-sequencer-ite sequencer t ensure-var sod::basic-codegen t t + expand-c-storage-specifier + cl:list + cl:symbol + expand-c-storage-specifier-form + (eql alignas) t + (eql sod-parser:lisp) t expand-c-type-form (eql cl:*) t (eql cl:array) t @@@ -930,6 -907,7 +965,7 @@@ (eql cl:function) t (eql cl:nil) t (eql cl:union) t + (eql atomic) t (eql bool) t (eql double) t (eql double-complex) t @@@ -952,6 -930,7 +988,7 @@@ (eql short) t (eql signed-char) t (eql size-t) t + (eql specs) t (eql struct) t (eql unsigned) t (eql unsigned-char) t @@@ -1026,7 -1005,6 +1063,7 @@@ sod-parser:file-locatio c-fragment property sod-class + sod-initarg sod-initializer sod-message sod-method @@@ -1035,8 -1013,6 +1072,8 @@@ finalize-modul module finalize-sod-class sod-class +find-slot-initargs + sod-class sod-slot find-slot-initializer sod-class sod-slot format-temporary-name @@@ -1101,19 -1077,12 +1138,19 @@@ ilayout-clas ilayout-ichains ilayout inst-alt + cond-inst if-inst inst-args + banner-inst call-inst +inst-banner + function-inst +inst-banner-args + function-inst inst-body block-inst do-while-inst + for-inst function-inst while-inst inst-chain-head @@@ -1121,16 -1090,11 +1158,16 @@@ inst-class convert-to-ilayout-inst inst-cond + cond-inst do-while-inst + for-inst if-inst while-inst inst-conseq + cond-inst if-inst +inst-control + banner-inst inst-decls block-inst inst-expr @@@ -1142,27 -1106,22 +1179,27 @@@ inst-func call-inst inst-init + for-inst var-inst inst-metric cl:list cl:null t + banner-inst block-inst break-inst call-inst + cond-inst continue-inst convert-to-ilayout-inst do-while-inst expr-inst + for-inst function-inst if-inst return-inst set-inst + sod::suppliedp-struct-inst update-inst var-inst while-inst @@@ -1174,11 -1133,8 +1211,11 @@@ inst-o inst-type function-inst var-inst +inst-update + for-inst inst-var set-inst + sod::suppliedp-struct-inst update-inst invoke-sequencer-items sequencer @@@ -1202,16 -1158,12 +1239,16 @@@ make-method-entrie basic-effective-method sod-class sod-class sod-parser:make-scanner-stream sod-token-scanner +make-sod-class-initfrag + sod-class t t make-sod-class-initializer - sod-class t t t t t + sod-class t t t t +make-sod-class-tearfrag + sod-class t t make-sod-initializer-using-slot - sod-class sod-slot t t t t t + sod-class sod-slot t t t t make-sod-instance-initializer - sod-class t t t t t + sod-class t t t t make-sod-message sod-class t t t make-sod-method @@@ -1220,12 -1172,6 +1257,12 @@@ make-sod-method-using-messag sod-message sod-class t t t t make-sod-slot sod-class t t t +make-sod-slot-initarg + sod-class t t t t +make-sod-slot-initarg-using-slot + sod-class t sod-slot t +make-sod-user-initarg + sod-class t t t method-entry-chain-head method-entry method-entry-chain-tail @@@ -1238,9 -1184,6 +1275,9 @@@ method-entry-function-typ method-entry method-entry-slot-name method-entry +method-keyword-argument-lists + effective-method t + sod::initialization-effective-method t module-dependencies module (setf module-dependencies) @@@ -1258,26 -1201,35 +1295,37 @@@ module-nam module module-pset module + pprint-c-storage-specifier + cl:symbol t + alignas-storage-specifier t pprint-c-type t t t c-array-type t t + c-atomic-type t t c-function-type t t + c-keyword-function-type t t c-pointer-type t t + c-storage-specifiers-type t t simple-c-type t t tagged-c-type t t primary-method-class simple-message standard-message + print-c-storage-specifier + t cl:symbol + t t + t alignas-storage-specifier print-c-type t c-array-type + t c-atomic-type t c-class-type t c-function-type t c-pointer-type + t c-storage-specifiers-type t simple-c-type t tagged-c-type cl:print-object + banner-inst t base-offset t block-inst t break-inst t @@@ -1286,14 -1238,12 +1334,14 @@@ call-inst t chain-offset t class-pointer t + cond-inst t continue-inst t convert-to-ilayout-inst t do-while-inst t effective-method t effective-slot t expr-inst t + for-inst t function-inst t ichain t if-inst t @@@ -1333,8 -1283,6 +1381,8 @@@ cl:shared-initializ aggregating-message t basic-direct-method t c-function-type t + c-keyword-function-type t + effective-method t method-codegen t module t sequencer t @@@ -1348,13 -1296,13 +1396,13 @@@ sod-token-scanner t simple-method-body aggregating-effective-method t t + sod::lifecycle-effective-method t t standard-effective-method t t cl:slot-unbound t basic-direct-method (eql sod::function-type) t basic-effective-method (eql sod::basic-argument-names) t basic-effective-method (eql sod::functions) t basic-message (eql sod::argument-tail) - t basic-message (eql sod::no-varargs-tail) t delegating-direct-method (eql sod::function-type) t delegating-direct-method (eql sod::next-method-type) t sod-class (eql sod::%ilayout) @@@ -1376,10 -1324,6 +1424,10 @@@ sod-class-direct-superclasse sod-class sod-class-ilayout sod-class +sod-class-initfrags + sod-class +(setf sod-class-initfrags) + t sod-class sod-class-instance-initializers sod-class (setf sod-class-instance-initializers) @@@ -1406,21 -1350,17 +1454,21 @@@ sod-class-slot t sod-class sod-class-state sod-class +sod-class-tearfrags + sod-class +(setf sod-class-tearfrags) + t sod-class sod-class-type sod-class sod-class-vtables sod-class +sod-initarg-argument + sod-initarg sod-initializer-class sod-initializer sod-initializer-slot sod-initializer -sod-initializer-value-form - sod-initializer -sod-initializer-value-kind +sod-initializer-value sod-initializer sod-message-argument-tail basic-message @@@ -1430,9 -1370,7 +1478,9 @@@ sod-message-combinatio aggregating-message sod-message-effective-method-class aggregating-message + sod::initialization-message standard-message + sod::teardown-message sod-message-kernel-function aggregating-message sod-message-method-class @@@ -1441,6 -1379,8 +1489,6 @@@ sod-message sod-class t sod-message-name sod-message -sod-message-no-varargs-tail - basic-message sod-message-type sod-message sod-method-body @@@ -1576,6 -1516,7 +1624,7 @@@ parser-proto.lis cl:char function setf c-type parser character-parser-context class combine-parser-failures function + commit parser cond-parse macro define-pluggable-parser macro defparse macro @@@ -1592,6 -1533,8 +1641,8 @@@ cl:not function parser cl:or macro parser parse + parse-empty function + parse-fail function parser macro parser-at-eof-p generic parser-capture-place generic @@@ -1781,6 -1724,7 +1832,7 @@@ expand-parser-for t (eql cl:t) t t (eql cl:when) t t (eql ?) t + t (eql commit) t t (eql expr) t t (eql label) t t (eql lisp) t @@@ -1818,7 -1762,6 +1870,7 @@@ file-locatio sod:c-fragment sod:property sod:sod-class + sod:sod-initarg sod:sod-initializer sod:sod-message sod:sod-method diff --combined doc/clang.tex index 6f7e218,ce34265..0c6c4bb --- a/doc/clang.tex +++ b/doc/clang.tex @@@ -46,10 -46,10 +46,11 @@@ The class hierarchy is shown in~\xref{f @|c-struct-type| \\ @|c-union-type| \\ @|c-enum-type| \- \\ + @|c-atomic-type| \\ @|c-pointer-type| \- \\ @|c-array-type| \\ - @|c-function-type| + @|c-function-type| \\ \ind + @|c-keyword-function-type| \- \end{tabbing}} \caption{Classes representing C types} \label{fig:codegen.c-types.classes} @@@ -165,8 -165,16 +166,16 @@@ type specifier. Type specifiers fit in type specifiers among its arguments. \end{describe} - \begin{describe}{fun}{expand-c-type-spec @ @> @
} + \begin{describe}{gf}{expand-c-type-spec @ @> @} Returns the Lisp form that @|(c-type @)| would expand into. + + If @ is a list, then \descref{expand-c-type-form}{fun} is + invoked. + \end{describe} + + \begin{describe}{gf}{expand-c-type-form @ @ @> @} + Returns the Lisp form that @|(c-type (@ . @)| would expand + into. \end{describe} \begin{describe}{gf} @@@ -299,6 -307,29 +308,29 @@@ argument lists for methods. This is do \subsection{Type qualifiers and qualifiable types} \label{sec:clang.ctypes.qual} + Qualifiers -- @|const|, @|volatile|, and so on -- are represented as lists of + keywords attached to types. Not all C types can carry qualifiers: notably, + function and array types cannot be qualified. + + For the most part, the C qualifier keywords correspond to like-named Lisp + keywords, only the Lisp keyword names are in uppercase. The correspondence + is shown in \xref{tab:clang.ctypes.qual}. + + \begin{table} + \begin{tabular}[C]{*2{>{\codeface}l}l} \hlx*{hv} + \thd{\textbf{C name}} & \thd{\textbf{Lisp name}} \\ \hlx{vhv} + _Atomic & :atomic \\ + const & :const \\ + restrict & :restrict \\ + volatile & :volatile \\ \hlx*{vh} + \end{tabular} + \caption{C and Lisp qualifier names} \label{tab:clang.ctypes.qual} + \end{table} + + The default behaviour, on output, is to convert keywords to lowercase and + hope for the best: special cases can be dealt with by adding appropriate + methods to \descref{c-qualifier-keyword}{gf}. + \begin{describe}{cls}{qualifiable-c-type (c-type) \&key :qualifiers} The class @|qualifiable-c-type| describes C types which can bear `qualifiers' (\Cplusplus\ calls them `cv-qualifiers'): @|const|, @@@ -336,6 -367,113 +368,113 @@@ non-null then the final character of the returned string will be a space. \end{describe} + \begin{describe}{gf}{c-qualifier-keyword @ @> @} + Return, as a string, the C keyword corresponding to the Lisp @. + + There is a standard method, which deals with many qualifiers. Additional + methods exist for qualifier keywords which need special handling, such as + @|:atomic|; they are not listed here explicitly. + + \begin{describe}{meth}{c-qualifier-keyword @ @> @} + Returns the @'s print-name, in lower case. This is sufficient + for the standard qualifiers @|:const|, @|:restrict|, and @|:volatile|. + \end{describe} + \end{describe} + + \begin{describe}{fun}{c-type-qualifier-keywords @ @> @} + Return the @'s qualifiers, as a list of C keyword names. + \end{describe} + + + \subsection{Storage specifiers} \label{sec:clang.ctypes.specs} + + Some declaration specifiers, mostly to do with how to store the specific + object in question, are determinedly `top level', and, unlike qualifiers, + don't stay attached to the base type when acted on by declarator operators. + Sod calls these `storage specifiers', though no such category exists in the C + standard. They have their own protocol, which is similar in many ways to + that of C types. + + Every Lisp keyword is potentially a storage specifier, which simply maps to + its lower-case print name in C; but other storage specifiers may be more + complicated objects. + + \begin{describe}{cls} + {c-storage-specifiers-type (c-type) \&key :subtype :specifiers} + A type which carries storage specifiers. The @ is the actual + type, and may be any C type; the @ are a list of + storage-specifier objects. + + The type specifier @|(specs @ @^*)| wraps the + @ in a @|c-storage-specifiers-type|, carrying the @s, + which are a list of storage specifiers in S-expression notation. + \end{describe} + + \begin{describe}{fun}{c-type-specifiers @ @> @} + Returns the list of type specifiers attached to the @ object, which + must be a @|c-storage-specifiers-type|. + \end{describe} + + \begin{describe}{mac} + {define-c-storage-specifier-syntax @ @ \\ \ind + @[[ @^* @! @ @]] \\ + @^* \- + \nlret @} + + Defines the symbol @ as a new storage-specifier operator. When a + list of the form @|(@ @^*)| is used as a storage specifier, + the @s are bound to fresh variables according to the + @ (a destructuring lambda-list) and the @s evaluated in + order in the resulting lexical environment as an implicit @. The + value should be a Lisp form which will evaluate to the storage-specifier + object described by the arguments. + + The @s may call @|expand-c-storage-specifier| in order to recursively + expand storage specifiers among its arguments. + \end{describe} + + \begin{describe}{gf}{expand-c-storage-specifier @ @> @} + Returns the Lisp form that @ expands to within @|(c-type (specs + @ @))|. + + If @ is a list, then \descref{expand-c-storage-specifier-form} is + invoked. + \end{describe} + + \begin{describe}{gf}{expand-c-storage-specifier-form @ @> @} + Returns the Lisp form that @|(@ . @)| expands to within + @|(c-type (specs @ (@ . @)))|. + \end{describe} + + \begin{describe}{gf}{pprint-c-storage-specifier @ @} + \end{describe} + + \begin{describe}{gf} + {print-c-storage-specifier @ @ + \&optional @ @} + \end{describe} + + \begin{describe}{fun}{wrap-c-type @ @ @> @} + Apply @ to the underlying C type of @ to create a new + `wrapped' type, and attach the storage specifiers of @ to the + wrapped type. + + If @ is \emph{not} a @|c-storage-specifiers-type|, then return + @|(funcall @ @)|. Otherwise, return a new + @|c-storage-specifiers-type|, with the same specifiers, but whose subtype + is the result of applying @ to the subtype of the original + @. + \end{describe} + + \begin{describe}{cls}{alignas-storage-specifier () \&key :alignment} + The class of @|_Alignas| storage specifiers; an instance denotes the + specifier @|_Alignas(@)|. The @ parameter may be any + printable object, but is usually a string or C fragment. + + The storage specifier form @|(alignas @)| returns a storage + specifier @|_Alignas(@)|, where @ is evaluated. + \end{describe} + \subsection{Leaf types} \label{sec:clang.c-types.leaf} @@@ -535,6 -673,38 +674,38 @@@ protocol \end{describe} + \subsection{Atomic types} \label{sec:clang.c-types.atomic} + + Atomic types are compound types. The subtype of an atomic type is simply the + underlying type of the object. Note that, as far as Sod is concerned, atomic + types are not the same as atomic-qualified types: you must be consistent + about which you use. + + \begin{describe}{cls} + {c-atomic-type (qualifiable-c-type) \&key :qualifiers :subtype} + Represents an atomic type. An instance denotes the C type + @|_Atomic(@)|. + + The @ may be any C type.\footnote{% + C does not permit atomic function or array types.} % + Two atomic types are equal if and only if their subtypes are equal and they + have matching qualifiers. It is possible, though probably not useful, to + have an atomic-qualified atomic type. + + The type specifier @|(atomic @ @^*)| returns a type + qualified atomic @, where @ is the type specified by + @ and the @s are qualifier keywords (which are + evaluated). + \end{describe} + + \begin{describe}{fun} + {make-atomic-type @ \&optional @ @> @} + Return an object describing the type qualified atomic @. If + @ is interned, then the returned atomic type object is interned + also. + \end{describe} + + \subsection{Pointer types} \label{sec:clang.c-types.pointer} Pointers are compound types. The subtype of a pointer type is the type it @@@ -624,8 -794,7 +795,8 @@@ function type is the type of the functi not return nil. \end{describe} -\begin{describe}{fun}{make-argument @ @ @> @} +\begin{describe}{fun} + {make-argument @ @ \&optional @ @> @} Construct and a return a new @ object. The argument has type @, which must be a @|c-type| object, and is named @. @@@ -634,21 -803,14 +805,21 @@@ suitable for function definitions. If @ is not nil, then the @'s print representation, with @|*print-escape*| nil, is used as the argument name. + + A @ may be supplied. If the argument is used in a + keyword-argument list (e.g., in a \descref{c-keyword-function-type} + [object]{cls}), and the @ value is provided and non-nil, then its + (unescaped) printed representation is used to provide a default value if + the keyword argument is not supplied by the caller. \end{describe} \begin{describe*} {\dhead{fun}{argument-name @ @> @} - \dhead{fun}{argument-type @ @> @}} - Accessor functions for @|argument| objects. They return the name (for - @|argument-name|) or type (for @|argument-type|) from the object, as passed - to @|make-argument|. + \dhead{fun}{argument-type @ @> @} + \dhead{fun}{argument-default @ @> @}} + Accessor functions for @|argument| objects. They return the appropriate + component of the object, as set by to @|make-argument|. The @ is + nil if no default was provided to @|make-argument|. \end{describe*} \begin{describe}{gf} @@@ -745,72 -907,10 +916,72 @@@ \end{prog} \end{describe} +\begin{describe}{cls} + {c-keyword-function-type (c-function-type) + \&key :subtype :arguments :keywords} + Represents `functions' which accept keyword arguments. Of course, actual C + functions can't accept keyword arguments directly, but this type is useful + for describing messages and methods which deal with keyword arguments. + + An instance denotes the type of C function which accepts the position + argument list @, and keyword arguments from the @ + list, and returns @. Either or both of the @ and + @ lists may be empty. (It is important to note the distinction + between a function which doesn't accept keyword arguments, and one which + does but for which no keyword arguments are defined. In particular, the + latter function can be changed later to accept a keyword argument without + breaking compatibility with old code.) The @ and @ + lists must \emph{not} contain @|:ellipsis| markers: a function can accept + keywords, or a variable-length argument tail, but not both. + + Keyword arguments may (but need not) have a \emph{default value} which is + supplied to the function body if the keyword is omitted. + + Keyword functions are never considered to be the same as ordinary + functions. Two keyword function types are considered to be the same if + their return types are the same, and their positional argument lists consist of + arguments with the same type, in the same order: the keyword arguments + accepted by the functions is not significant. + + Keyword functions are constructed using an extended version of the @|fun| + specifier used for ordinary C function types. The extended syntax is as + follows. + \begin{prog} + (fun \=@ + @{ (@ @) @}^* \+ \\ + @{ \=:keys @{ (@ @ @[@@]) @}^* + @[. @@] @! \+ \\ + . @ @} + \end{prog} + where either the symbol @|:keys| appears literally in the specifier, or the + @ evaluates to a list containing the symbol @|:keys|. (If neither of + these circumstances obtains, then the specifier constructs an ordinary + function type.) + + See the description of \descref{c-function-type}{cls} for how a trailing + @ is handled. + + The list of @s and @s describes the positional + arguments. The list of @s, @s and @s + describes the keyword arguments. +\end{describe} + \begin{describe}{fun} {make-function-type @ @ @> @} Construct and return a new function type, returning @ and accepting the @. + + If the @ list contains a @|:keys| marker, then a + \descref{c-keyword-function-type}[object]{cls} is returned: those arguments + preceding the @|:keys| marker form the positional argument list, and those + following the marker form the list of keyword arguments. +\end{describe} + +\begin{describe}{fun} + {make-keyword-function-type @ @ @ + \nlret @} + Construct and return a new keyword-function type, returning @ and + accepting the @ and @. \end{describe} \begin{describe}{gf} @@@ -827,62 -927,6 +998,62 @@@ @|commentify-argument-names| to the argument list of the given type. \end{describe} +\begin{describe}{fun}{reify-variable-argument-tail @ @> @} + If the @ list contains an @|:ellipsis| marker, then replace it + with a @|va_list|. The name for the new argument, if any, is taken from + the \descref{*sod-ap*}[variable]{var}. The new list is returned; the + original list is not modified, but may share structure with the new list. +\end{describe} + +\begin{describe}{fun}{merge-keyword-lists @ @> @} + Merge a number of keyword-argument lists together and return the result. + + The @ parameter is a list consisting of a number of @|(@ + . @)| pairs: in each pair, @ is a list of + \descref{argument}{cls} objects, and @ is either nil or an object + whose printed representation describes the origin of the corresponding + @ list, suitable for inclusion in an error message. + + The resulting list contains exactly one argument for each distinct argument + name appearing in the input @; this argument will contain the + default value from the earliest occurrence in the input @ of an + argument with that name. + + If the same name appears multiple times with different types, an error is + signalled quoting the name, conflicting types, and (if non-nil) the origins + of the offending argument objects. +\end{describe} + +\begin{describe}{fun} + {pprint-c-function-type @ @ + @ @} + Provides the top-level structure for printing C function types. + + Output is written to @ to describe a function type returning + @, whose declarator kernel (containing the name, and any + further type operands) will be printed by @, and whose + arguments, if any, will be printed by @. + + The @ function is a standard kernel-printing function + following the \descref{pprint-c-type}[protocol]{gf}. + + The @ function is given a single argument, which is the + @ to print on. It should not print the surrounding parentheses. + + The output written to @ looks approximately like + \begin{prog} + @ @(@) + \end{prog} +\end{describe} + +\begin{describe}{fun}{pprint-argument-list @ @ @> @} + Print an argument list to @. + + The @ is a list of \descref{argument}[objects]{cls}, optionally + containing an @|:ellipsis| marker. The function returns true if any + arguments were actually printed. +\end{describe} + \subsection{Parsing C types} \label{sec:clang.c-types.parsing} @@@ -1029,10 -1073,6 +1200,10 @@@ Temporary names are represented by obje @^*} \end{describe} +\begin{describe}{fun} + {format-banner-comment @ @ \&rest @} +\end{describe} + \begin{table} \begin{tabular}[C]{ll>{\codeface}l} \hlx*{hv} \thd{Class name} & @@@ -1044,8 -1084,6 +1215,8 @@@ @|set| & @ @ & @ = @; \\ \hlx{v} @|update| & @ @ @ & @ @= @; \\ \hlx{v} + @|cond| & @ @ @ & @ ? @ : @ + \\ \hlx{v} @|return| & @ & return @[@@]; \\ \hlx{v} @|break| & --- & break; \\ \hlx{v} @@@ -1054,26 -1092,18 +1225,26 @@@ @|call| & @ @|\&rest| @ & @(@_1, $\ldots$, - @_n) \\ \hlx{vhv} + @_n) \\ \hlx{v} + @|banner| & @ @|\&rest| @ + & /* @ */ \\ \hlx{vhv} @|block| & @ @ & \{ @[@@] @ \} \\ \hlx{v} @|if| & @ @ @|\&optional| @ & if (@) @ @[else @@] \\ \hlx{v} + @|for| & @ @ @ @ & + for (@; @; @) @ \\ \hlx{v} @|while| & @ @ & while (@) @ \\ \hlx{v} @|do-while| & @ @ & do @ while (@); \\ \hlx{v} - @|function| & @ @ @ & - \vtop{\hbox{\strut @_0 @(@_1 @_1, $\ldots$, + @|function| & + \vtop{\hbox{\strut @ @ @} + \hbox{\strut \quad @|\&optional @|} + \hbox{\strut \quad @|\&rest| @}} & + \vtop{\hbox{\strut @[/* @ */@]} + \hbox{\strut @_0 @(@_1 @_1, $\ldots$, @_n @_n @[, \dots@])} \hbox{\strut \quad @}} \\ \hlx*{vh} \end{tabular} @@@ -1103,9 -1133,6 +1274,9 @@@ \begin{describe}{gf}{emit-decls @ @} \end{describe} +\begin{describe}{fun}{emit-banner @ @ \&rest @} +\end{describe} + \begin{describe}{gf}{codegen-push @} \end{describe} diff --combined doc/structures.tex index 42739ef,b63a336..3826bdc --- a/doc/structures.tex +++ b/doc/structures.tex @@@ -32,8 -32,8 +32,8 @@@ works very differently from the standar The concrete types described in \xref{sec:structures.common} and \ref{sec:structures.root} are declared by the header file @||. -The definitions described in sections \ref{sec:structures.layout} are defined -in the header file generated by the containing module. +The definitions described in \xref{sec:structures.layout} are defined in the +header file generated by the containing module. %%%-------------------------------------------------------------------------- \section{Common instance structure} \label{sec:structures.common} @@@ -92,7 -92,7 +92,7 @@@ recommended \begin{figure}[tbp] \begin{tabular}{p{10pt}p{10pt}} - \begin{prog} + \begin{nprog} struct SodObject__ilayout \{ \\ \ind union \{ \\ \ind struct SodObject__ichain_obj \{ \\ \ind @@@ -100,19 -100,14 +100,19 @@@ \} obj; \- \\ \} obj; \- \\ \}; - \end{prog} + \end{nprog} & - \begin{prog} + \begin{nprog} struct SodObject__vt_obj \{ \\ \ind const SodClass *_class; \\ - size_t _base; \- \\ + size_t _base; \\ + struct SodObject__vtmsgs_obj \{ \\ \ind + void (*init)(SodObject *me, ...); \\ + void (*init__v)(SodObject *me, va_list); \\ + int (*teardown)(SodObject *me); \- \\ + \} obj; \- \\ \}; - \end{prog} \\ + \end{nprog} \\ \end{tabular} \caption{Instance and vtable layout of @|SodObject|} \label{fig:structures.root.sodobject} @@@ -120,79 -115,19 +120,79 @@@ \begin{describe}[SodObject]{cls} {[nick = obj, metaclass = SodClass, lisp_metaclass = sod_class] \\ - class SodObject \{ \}} + class SodObject \{ \\ \ind + void init(?); + \}} - The @|SodObject| class defines no slots or messages. Because @|SodObject| - has no direct superclasses, there is only one chain, and no inherited - slots or messages, so the single chain contains only a vtable pointer. + The @|SodObject| class defines no slots. Because @|SodObject| has no + direct superclasses, there is only one chain, and no inherited slots or + messages, so the single chain contains only a vtable pointer. - Since there are no messages, and @|SodClass| also has only one chain, the - vtable contains only the standard class pointer and offset-to-base members. - In a direct instance of @|SodObject| (why would you want one?) the class - pointer contains the address of @|SodObject__class| and the offset is zero. + Since @|SodClass| also has only one chain, the vtable contains only the + standard class pointer and offset-to-base members. In a direct instance of + @|SodObject| (why would you want one?) the class pointer contains the + address of @|SodObject__class| and the offset is zero. The instance and vtable layout of @|SodObject| is shown in \xref{fig:structures.root.sodobject}. + + The following messages are defined. + + \begin{describe}[obj.init]{msg}{void init(?);} + Initialize a newly allocated instance. + + This message uses a custom method combination which works like the + standard method combination except that default behaviour specific to the + receiver's direct class is invoked if no primary or around method + overrides. This default behaviour may be invoked multiple times if some + method calls on its @|next_method| function more than once. + + This default behaviour is to initialize the instance's slots using the + defined slot initializers, and execute the initialization fragments. + Each slot is initialized using the most specific applicable initializer, + if any. Slots without an initializer are left uninitialized. + + Slots are initialized and initialization fragments executed together, a + superclass at a time: first, the superclass's slots are initialized (if + any); then the superclass's initialization fragments (if any) are + executed, starting with the least specific superclass first. Slots and + initialization fragments defined by the same class are processed in the + order in which they appear in the class definition. + + There are no standard keyword arguments; methods on subclasses are free + to introduce their own in the usual way. + + It is usual to provide complex initialization behaviour as @|after| + methods. This ensures that slots have been initialized as necessary + before the method executes. + + For more details on instance construction, see + \xref{sec:concepts.lifecycle.birth}. + \end{describe} + + \begin{describe}[obj.teardown]{msg}{int teardown();} + Teardown an instance which is no longer required. + + The message returns an integer flag. A zero value means that the + instance is safe to deallocate. A nonzero value means that the instance + should not be deallocated, and that it is safe for the caller to simply + forget about it. This simple protocol may be used, for example, to + implement a reference-counting system. + + This message uses a custom method combination which works like the + standard method combination except that default behaviour is invoked if + no primary or around method overrides. + + This default behaviour is to execute each superclass's teardown + fragments, most specific first, and then return zero to indicate that the + object is ready for deallocation. Teardown fragments defined by the same + class are processed in the order in which they appear in the class + definition. + + It is usual to provide complex teardown behaviour as @|before| methods. + Logic to decide whether to allow deallocation is usually implemented as + @|around| methods. + \end{describe} \end{describe} @@@ -204,7 -139,9 +204,8 @@@ const char *name; \\ const char *nick; \\ size_t initsz; \\ + size_t align; \\ void *(*imprint)(void *@

); \\ - void *(*init)(void *@

); \\ size_t n_supers; \\ const SodClass *const *supers; \\ size_t n_cpl; \\ @@@ -218,9 -155,9 +219,9 @@@ size_t islotsz; \- \\ \}} - The @|SodClass| class defines no messages, but there are a number of slots. - Its only direct superclass is @|SodObject| and so (like its superclass) its - vtable is trivial. + The @|SodClass| class defines no additional messages , but there are a + number of slots. Its only direct superclass is @|SodObject| and so (like + its superclass) its vtable is simple. The slots defined are as follows. \begin{description} \let\makelabel\code @@@ -231,12 -168,20 +232,14 @@@ \item[initsz] The size in bytes required to store an instance of the class. + \item[align] A sufficient alignment for the class's instance storage. + \item[imprint] A pointer to a function: given a pointer @

to at least @ bytes of appropriately aligned memory, `imprint' this memory it so that it becomes a minimally functional instance of the class: all of the vtable and class pointers are properly initialized, but the slots are left untouched. The function returns its argument @

. - \item[init] A pointer to a function: given a pointer @

to at least - @ bytes of appropriately aligned memory, initialize an instance - of the class in it: all of the vtable and class pointers are initialized, - as are slots for which initializers are defined. Other slots are left - untouched. The function returns its argument @

. - \item[n_supers] The number of direct superclasses. (This is zero exactly in the case of @|SodObject|.) @@@ -365,7 -310,7 +368,7 @@@ type @|struct $C$__ilayout| union $B$__ichainu_$i$ $i$; \\ \quad$\vdots$ \- \\ \}; - \\[\bigskipamount] + \\+ typedef struct $C$__ichain_$h$ $C$; \end{prog} @@@ -470,7 -415,7 +473,7 @@@ structure \quad$\vdots$ \- \\ \} $c$; \- \\ \}; - \\[\bigskipamount] + \\+ extern const union $C$__vtu_$h$ $C$__vtable_$h$; \end{prog} @@@ -584,16 -529,10 +587,16 @@@ defined a \begin{prog} @_0 $m$(@_1 @_1, $\ldots$, @_n @_n, \dots); \end{prog} +or a standard message which takes keyword arguments, defined as +\begin{prog} + @_0 $m$(\=@_1 @_1, $\ldots$, @_n @_n? \+ \\ + @_{n+1} @_{n+1} @[= @_{n+1}@], $\ldots$, + @_m @_m @[= @_m@]); +\end{prog} two entry points are defined: the usual `main' entry point which accepts a variable number of arguments, and a `valist' entry point which accepts an argument of type @|va_list| in place of the variable portion of the argument -list. +list or keywords. \begin{prog} @_0 $m$($C$ *me, @_1 @_1, $\ldots$, @_n @_n, \dots); \\ @@@ -614,10 -553,10 +617,10 @@@ For each message $m$ directly defined b which makes sending the message $m$ to an instance of (any subclass of) $C$ somewhat less ugly. -If $m$ takes a variable number of arguments, the macro is more complicated -and is only available in compilers advertising C99 support, but the effect is -the same. For each variable-argument message, there is also an additional -macro for calling the `valist' entry point. +If $m$ takes a variable number of arguments, or keyword arguments, the macro +is more complicated and is only available in compilers advertising C99 +support, but the effect is the same. For each variable-argument message, +there is also an additional macro for calling the `valist' entry point. \begin{prog} \#define $C$_$m$__v(@, $\ldots$, @) @@->_vt@->$c$.$m$__v(@, $\ldots$, @) diff --combined doc/syntax.tex index 3034b1e,b8ae797..21f835e --- a/doc/syntax.tex +++ b/doc/syntax.tex @@@ -451,8 -451,19 +451,19 @@@ recognized \alt "bool" | "_Bool" \alt "imaginary" | "_Imaginary" | "complex" | "_Complex" \alt + \alt + \alt - ::= "const" | "volatile" | "restrict" + ::= | "const" | "volatile" | "restrict" + + ::= + "(" @^+ ")" + + ::= "atomic" | "_Atomic" + + ::= "(" ")" + + ::= "alignas" "_Alignas" ::= \end{grammar} @@@ -469,9 -480,11 +480,11 @@@ defined in the built-in module Declaration specifiers may appear in any order. However, not all combinations are permitted. A declaration specifier must consist of zero or - more @s, and one of the following, up to reordering. + more @s, zero or more @s, and one of the + following, up to reordering. \begin{itemize} \item @ + \item @ \item @"struct" @, @"union" @, @"enum" @ \item @"void" \item @"_Bool", @"bool" @@@ -493,27 -506,29 +506,30 @@@ All of these have their usual C meaning \subsubsection{Declarators} \begin{grammar} -$[k]$ ::= @^* $[k]$ +$[k, a]$ ::= @^* $[k, a]$ -$[k]$ ::= $k$ -\alt "(" $[k]$ ")" -\alt $[k]$ @ +$[k, a]$ ::= $k$ +\alt "(" $[k, a]$ ")" +\alt $[k, a]$ @$[a]$ ::= "*" @^* - ::= "[" "]" -\alt "(" ")" +$[a]$ ::= "[" "]" +\alt "(" $a$ ")" ::= $\epsilon$ | "..." \alt $[\mbox{@}]$ @["," "..."@] ::= @^+ - ::= $[\epsilon]$ ++ ::= $[\epsilon, \mbox{@}]$ + + ::= $[\mbox{@ @! $\epsilon$}]$ + ::= + $[\mbox{@ @! $\epsilon$}, \mbox{@}]$ - ::= $[\mbox{@}]$ - - ::= "." + ::= + $[\mbox{@}, \mbox{@}]$ \end{grammar} The declarator syntax is taken from C, but with some differences. @@@ -528,24 -543,6 +544,24 @@@ The remaining differences are (I hope) a matter of presentation rather than substance. +There is additional syntax to support messages and methods which accept +keyword arguments. + +\begin{grammar} + ::= @["=" @] + + ::= + @[$[\mbox{@}]$@] + "?" @[$[\mbox{@}]$@] + + ::= @! + + ::= "." + +$[k]$ ::= + $[k, \mbox{@}]$ +\end{grammar} + \subsection{Class definitions} \label{sec:syntax.module.class} @@@ -585,8 -582,6 +601,8 @@@ class Sub : Super ::= \alt +\alt +\alt \alt \alt \end{grammar} @@@ -664,9 -659,9 +680,9 @@@ class Example : Super \begin{grammar} ::= @["class"@] $[\mbox{@}]$ ";" - ::= "=" + ::= @["=" @] - :: "{" "}" | + :: \end{grammar} An @ provides an initial value for one or more slots. If @@@ -677,28 -672,15 +693,28 @@@ The first component of the @ enclosed in braces denotes an aggregate initializer. - This is suitable for initializing structure, union or array slots. -\item A @ \emph{not} beginning with an open brace is a `bare' - initializer, and continues until the next @`,' or @`;' which is not within - nested brackets. Bare initializers are suitable for initializing scalar - slots, such as pointers or integers, and strings. -\end{itemize} +An @|initarg| property may be set on an instance slot initializer (or a +direct slot definition). See \xref{sec:concepts.lifecycle.birth} for the +details. An initializer item must have either an @|initarg| property, or an +initializer expression, or both. + +Each class may define at most one initializer item with an explicit +initializer expression for a given slot. + +\subsubsection{Initarg items} +\begin{grammar} + ::= + "initarg" + @^+ + $[\mbox{@}]$ ";" +\end{grammar} + +\subsubsection{Fragment items} +\begin{grammar} + ::= "{" "}" + + ::= "init" | "teardown" +\end{grammar} \subsubsection{Message items} \begin{grammar} diff --combined lib/sod-structs.3 index 029cabd,5624d33..35f668f --- a/lib/sod-structs.3 +++ b/lib/sod-structs.3 @@@ -69,11 -69,6 +69,11 @@@ struct sod_vtable struct SodObject__vt_obj { \h'2n'const SodClass *_class; \h'2n'size_t _base; +\h'2n'struct SodObject__vtmsgs_obj { +\h'4n'void (*init)(SodObject *\fIme\fB, ...); +\h'4n'void (*init__v)(SodObject *\fIme\fB, va_list); +\h'4n'int (*teardown)(SodObject *\fIme\fB); +\h'2n'} obj; }; struct SodObject__ilayout { @@@ -90,11 -85,6 +90,11 @@@ extern const struct SodClass__ilayout S struct SodClass__vt_obj { \h'2n'const SodClass *_class; \h'2n'size_t _base; +\h'2n'struct SodClass__vtmsgs_obj { +\h'4n'void (*init)(SodClass *\fIme\fB, ...); +\h'4n'void (*init__v)(SodClass *\fIme\fB, va_list); +\h'4n'int (*teardown)(SodClass *\fIme\fB); +\h'2n'} obj; }; struct SodObject__ilayout { @@@ -105,7 -95,9 +105,8 @@@ \h'8n'const char *name; \h'8n'const char *nick; \h'8n'size_t initsz; + \h'8n'size_t align; \h'8n'void *(*imprint)(void *\fIp\fB); -\h'8n'void *(*init)(void *\fIp\fB); \h'8n'size_t n_supers; \h'8n'const SodClass *const *supers; \h'8n'size_t n_cpl; @@@ -221,7 -213,7 +222,7 @@@ and not really to be recommended .SS The SodObject class The .B SodObject -class defines no slots or messages. +class defines no slots. Because .B SodObject has no direct superclasses, @@@ -229,7 -221,8 +230,7 @@@ there is only one chain and no inherited slots or messages, so the single chain contains only a vtable pointer. .PP -Since there are no messages, -and +Since .B SodClass also has only one chain, the vtable contains only the standard class pointer and offset-to-base @@@ -240,79 -233,6 +241,79 @@@ In an actual instance o the class pointer contains the address of .B SodObject__class and the offset is zero. +.PP +The +.B init +message is used to initialize a newly allocated instance. +.PP +This message uses a custom method combination +which works like the standard method combination +except that default behaviour +specific to the receiver's direct class +is invoked if no primary or around method overrides. +This default behaviour may be invoked multiple times +if some method calls on its +.B next_method +function more than once. +.PP +This default behaviour is to initialize the instance's slots +using the defined slot initializers, +and execute the initialization fragments. +Each slot is initialized +using the most specific applicable initializer, +if any. +Slots without an initializer +are left uninitialized. +.PP +Slots are initialized and initialization fragments executed together, +a superclass at a time: +first, the superclass's slots are initialized (if any); +then the superclass's initialization fragments (if any) are executed, +starting with the least specific superclass first. +Slots and initialization fragments defined by the same class +are processed in the order in which they appear in the class definition. +.PP +There are no standard keyword arguments; +methods on subclasses are free to +introduce their own in the usual way. +.PP +It is usual to provide complex initialization behaviour as +.B after +methods. +This ensures that slots have been initialized as necessary +before the method executes. +.PP +The +.B teardown +message is used to tear down an instance which is no longer required. +.PP +The message returns an integer flag. +A zero value means that the instance is safe to deallocate. +A nonzero value means that the instance should not be deallocated, +and that it is safe for the caller to simply forget about it. +This simple protocol may be used, for example, +to implement a reference-counting system. +.PP +This message uses a custom method combination +which works like the standard method combination +except that default behaviour is invoked if +no primary or around method overrides. +This default behaviour is to execute +each superclass's teardown fragments, +most specific first, +and then return zero to indicate +that the object is ready for deallocation. +Teardown fragments defined by the same class +are processed in the order in which they appear +in the class definition. +.PP +It is usual to provide complex teardown behaviour as +.B before +methods. +Logic to decide whether to allow deallocation +is usually implemented as +.B around +methods. . .SS The SodClass class The @@@ -334,6 -254,9 +335,9 @@@ A pointer to the class's nickname .B size_t initsz; The size in bytes required to store an instance of the class. .TP + .B size_t align; + A sufficient alignment for the class's instance storage. + .TP .BI "void *(*imprint)(void *" p ); A pointer to a function: given a pointer @@@ -348,6 -271,20 +352,6 @@@ but the slots are left untouched The function returns its argument .IR p . .TP -.BI "void *(*init)(void *" p ); -A pointer to a function: -given a pointer -.I p -to at least -.I initsz -bytes of appropriately aligned memory, -initialize an instance of the class in it: -all of the vtable and class pointers are initialized, -as are slots for which initializers are defined. -Other slots are left untouched. -The function returns its argument -.IR p . -.TP .B size_t n_supers; The number of direct superclasses. (This is zero exactly in the case of @@@ -960,27 -897,6 +964,27 @@@ defined a .IB an , .B ...); .PP +or a standard message which takes keyword arguments, +defined as +.IP +.I tr +.IB m ( \c +.I t1 +.IB a1 , +.RB ... , +.I tn +.IB an ?\& +.IR tn +1 +.IR kn +1 +.RB [ = +.IR dn +1] \c +.B , +.I tm +.I km +.RB [ = +.IR dm ] \c +); +.PP two entry points are defined: the usual `main' entry point which accepts a variable number of @@@ -988,8 -904,7 +992,8 @@@ arguments and a `valist' entry point which accepts an argument of type .B va_list -in place of the variable portion of the argument list. +in place of the variable portion of the argument list +or keywords. .IP .I tr .BI (* m )( \c @@@ -1039,7 -954,6 +1043,7 @@@ somewhat less ugly If .I m takes a variable number of arguments, +or keyword arguments, the macro is more complicated and is only available in compilers advertising C99 support, but the effect is the same. diff --combined lib/sod.h index 8945810,943ed4b..4588626 --- a/lib/sod.h +++ b/lib/sod.h @@@ -34,6 -34,16 +34,16 @@@ /*----- Preliminary utilities ---------------------------------------------*/ + /* Various hacks for checking compiler versions. */ + #define SOD__GCC_P(maj, min) \ + (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) + + #ifdef __GNUC__ + # define SOD__EXTENSION __extension__ + #else + # define SOD__EXTENSION + #endif + /* --- @SOD__HAVE_VARARGS_MACROS@ --- * * * Use: Defined if the compiler supports C99-style variadic macros. @@@ -49,7 -59,7 +59,7 @@@ # define SOD__HAVE_VARARGS_MACROS - #elif __GNUC__ >= 3 + #elif SOD__GCC_P(3, 0) /* We're using GCC, which is trying to deny it but we don't believe it. * Unfortunately there's a fly in the ointment: if `-pedantic' -- or, * worse, `-pedantic-errors' -- is set, then GCC will warn about these @@@ -77,15 -87,26 +87,35 @@@ /* We're going to want to make use of this ourselves. */ SOD__VARARGS_MACROS_PREAMBLE + /* --- @SOD__ALIGNOF@ --- * + * + * Arguments: @type@ = a C type name, consisting of declaration specifiers + * and `*[QUALIFIERS]' declarator operators + * + * Returns: A sufficient alignment for objects of the given @type@, as a + * @size_t@. + */ + + #if __STDC_VERSION__ >= 201112 + # define SOD__ALIGNOF(type) _Alignof(type) + #elif SOD__GCC_P(4, 7) + # define SOD__ALIGNOF(type) __extension__ _Alignof(type) + #elif defined(__GNUC__) + # define SOD__ALIGNOF(type) __alignof__(type) + #else + # define SOD__ALIGNOF(type) \ + offsetof(struct { char sod__x; type sod__y; }, sod__y) + #endif + +/* --- @SOD__IGNORE@ --- * + * + * Arguments: @var@ = some variable name + * + * Use: Suppress any warning that @var@ isn't used. + */ + +#define SOD__IGNORE(var) ((void)(var)) + /* --- @SOD__CAR@ --- * * * Arguments: @...@ = a nonempty list of arguments @@@ -103,7 -124,6 +133,7 @@@ #include #include +#include "keyword.h" #include "sod-base.h" /*----- Data structures ---------------------------------------------------*/ @@@ -236,42 -256,18 +266,42 @@@ struct sod_chain #define SOD_CONVERT(cls, obj) ((cls *)sod_convert(cls##__class, (obj))) +/* --- @SOD_INIT@ --- * + * + * Arguments: @cls@ = a class type name + * @p@ = pointer to storage to initialize + * @keys@ = a @KWARGS(...)@ keyword argument sequence + * + * Use: Initializes raw storage as an instance of @cls@. + */ + +#define SOD_INIT(cls, p, keys) ((cls *)sod_init(cls##__class, (p), keys)) + +/* --- @SOD_MAKE@ --- * + * + * Arguments: @cls@ = a class type name + * @keys@ = a @KWARGS(...)@ keyword argument sequence + * + * Use: Allocates (using @malloc@) eand initializes storage to be an + * instance of @cls@. Returns a null pointer if allocation + * fails. Use @sod_destroy@ to release the instance. + */ + +#define SOD_MAKE(cls, keys) ((cls *)sod_make(cls##__class, keys)) + /* --- @SOD_DECL@ --- * * - * Arguments: @cls_@ = a class type name - * @var_@ = a variable name + * Arguments: @cls@ = a class type name + * @var@ = a variable name + * @keys@ = a @KWARGS(...)@ keyword argument sequence * - * Use: Declare @var_@ as a pointer to an initialized instance of - * @cls_@ with automatic lifetime. + * Use: Declare @var@ as a pointer to an initialized instance of + * @cls@ with automatic lifetime. */ -#define SOD_DECL(cls_, var_) \ - struct cls_##__ilayout var_##__layout; \ - cls_ *var_ = cls_##__class->cls.init(&var_##__layout) +#define SOD_DECL(cls, var, keys) \ + struct cls##__ilayout var##__layout; \ + cls *var = (cls *)sod_init(cls##__class, &var##__layout, keys) /*----- Functions provided ------------------------------------------------*/ @@@ -312,95 -308,6 +342,95 @@@ extern int sod_subclassp(const SodClas extern void *sod_convert(const SodClass */*cls*/, const void */*obj*/); +/* --- @sod_init@, @sod_initv@ --- * + * + * Arguments: @const SodClass *cls@ = class object for new instance + * @void *p@ = pointer to storage for new instance + * @va_list ap, ...@ = initialization keyword arguments + * + * Returns: Pointer to the initialized instance. + * + * Use: Initializes an instance in pre-allocated storage, and returns + * a pointer to it. + * + * This function will imprint the storage, and then send an + * `initialize' message to the fresh instance containing the + * provided keyword arguments. + * + * It's usually convenient to use the macro @SOD_INIT@ rather + * than calling @sod_init@ directly. + */ + +extern KWCALL void *sod_init(const SodClass */*cls*/, void */*p*/, ...); +extern void *sod_initv(const SodClass */*cls*/, void */*p*/, va_list /*ap*/); + +/* --- @sod_make@, @sod_makev@ --- * + * + * Arguments: @const SodClass *cls@ = class object for new instance + * @va_list ap, ...@ = initialization keyword arguments + * + * Returns: Pointer to the newly-allocated initialized instance, or null. + * + * Use: Allocates storage for a new instance, initializes it, and + * returns a pointer to it. If allocation fails, a null pointer + * is returned instead. + * + * This function will allocate the storage using @malloc@, and + * then initialize it as for @sod_init@. + * + * It's usually convenient to use the macro @SOD_MAKE@ rather + * than calling @sod_make@ directly. + * + * (This function is not available in freestanding environments + * lacking @malloc@ and @free@.) + */ + +extern KWCALL void *sod_make(const SodClass */*cls*/, ...); +extern void *sod_makev(const SodClass */*cls*/, va_list /*ap*/); + +/* --- @sod_teardown@ --- * + * + * Arguments: @void *p@ = pointer to an instance to be torn down + * + * Returns: Zero if the object is torn down; nonzero if it refused for + * some reason. + * + * Use: Invokes the instance's `teardown' method to release any held + * resources. + * + * If this function returns nonzero, then the object is still + * active, and may still hold important resources. This is not + * intended to be a failure condition: failures in teardown are + * usually unrecoverable (or very hard to recover from) and + * should probably cause the program to abort. A refusal, on + * the other hand, means that the object is still live and + * shouldn't be deallocated, but that this is a normal situation + * and the caller shouldn't worry about it. + */ + +extern int sod_teardown(void */*p*/); + +/* --- @sod_destroy@ --- * + * + * Arguments: @void *p@ = pointer to an instance to be torn down, or null + * + * Returns: Zero if the object was freed; nonzero if it refused for some + * reason. + * + * Use: Invokes the instance's `teardown' method to release any held + * resources, and then calls @free@ to release the instance's + * storage. See @sod_teardown@ for details, especially + * regarding the return value's meaning. + * + * If @p@ is null, then this function does nothing except + * returns zero. + * + * (This function is not available in freestanding environments + * lacking @malloc@ and @free@.) + */ + +extern int sod_destroy(void */*p*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --combined src/builtin.lisp index 563766c,fbef93b..0787b8d --- a/src/builtin.lisp +++ b/src/builtin.lisp @@@ -85,13 -85,16 +85,16 @@@ (define-class-slot "initsz" (class) size-t (format nil "sizeof(struct ~A)" (ilayout-struct-tag class))) + (define-class-slot "align" (class) size-t + (format nil "SOD__ALIGNOF(struct ~A)" (ilayout-struct-tag class))) + (define-class-slot "imprint" (class stream) (* (fun (* void) ("/*p*/" (* void)))) (format nil "~A__imprint" class) (let ((ilayout (sod-class-ilayout class))) (format stream "~&~: -/* Imprint raw memory with instance structure. */ -static void *~A__imprint(void *p) +/* Imprint raw memory with class `~A' instance structure. */ +static void *~:*~A__imprint(void *p) { struct ~A *sod__obj = p; @@@ -109,6 -112,60 +112,6 @@@ (sod-class-nickname tail)))) (ilayout-ichains ilayout))))) -(define-class-slot "init" (class stream) - (* (fun (* void) ("/*p*/" (* void)))) - (format nil "~A__init" class) - - ;; FIXME this needs a metaobject protocol - (let ((ilayout (sod-class-ilayout class)) - (used nil)) - (format stream "~&~: -/* Provide initial values for an instance's slots. */ -static void *~A__init(void *p)~%{~%" class) - (dolist (ichain (ilayout-ichains ilayout)) - (let ((ich (format nil "sod__obj->~A.~A" - (sod-class-nickname (ichain-head ichain)) - (sod-class-nickname (ichain-tail ichain))))) - (dolist (item (ichain-body ichain)) - (etypecase item - (vtable-pointer - nil) - (islots - (let ((isl (format nil "~A.~A" - ich - (sod-class-nickname (islots-class item))))) - (dolist (slot (islots-slots item)) - (let ((dslot (effective-slot-direct-slot slot)) - (init (effective-slot-initializer slot))) - (when init - (unless used - (format stream - " struct ~A *sod__obj = ~A__imprint(p);~2%" - (ilayout-struct-tag class) class) - (setf used t)) - (format stream " {~% ") - (pprint-c-type (sod-slot-type dslot) stream - *sod-tmp-val*) - (format stream " =") - (ecase (sod-initializer-value-kind init) - (:simple (write (sod-initializer-value-form init) - :stream stream - :pretty nil :escape nil) - (format stream ";~%")) - (:compound (format stream " {") - (write (sod-initializer-value-form init) - :stream stream - :pretty nil :escape nil) - (format stream " };~%"))) - (format stream " ~A.~A = ~A;~% }~%" - isl (sod-slot-name dslot) - *sod-tmp-val*)))))))))) - (unless used - (format stream " ~A__imprint(p);~%" class)) - (format stream "~&~: - return (p); -}~2%"))) - ;;;-------------------------------------------------------------------------- ;;; Superclass structure. @@@ -203,296 -260,6 +206,296 @@@ static const SodClass *const ~A__cpl[] (islots-struct-tag class)) "0")) +;;;-------------------------------------------------------------------------- +;;; Built-in methods. + +;; Common protocol. + +(defclass lifecycle-message (standard-message) + ()) + +(defclass lifecycle-effective-method (standard-effective-method) + ()) + +(defmethod effective-method-live-p ((method lifecycle-effective-method)) + t) + +(defgeneric lifecycle-method-kernel (method codegen target) + (:documentation + "Compute (into CODEGEN) the class-specific part of the METHOD. + + The result, if any, needs to find its way to the TARGET, as usual.")) + +(defmethod simple-method-body + ((method lifecycle-effective-method) codegen target) + (invoke-delegation-chain codegen target + (effective-method-basic-argument-names method) + (effective-method-primary-methods method) + (lambda (target) + (lifecycle-method-kernel method + codegen + target)))) + +;; Utilities. + +(defun declare-me (codegen class) + "Emit, to CODEGEN, a declaration of `me' as a pointer to CLASS. + + The pointer refers to a part of the prevailing `sod__obj' object, which is + assumed to be a pointer to an appropriate `ilayout' structure." + (emit-decl codegen (make-var-inst "me" (c-type (* (class class))) + (format nil "&sod__obj->~A.~A" + (sod-class-nickname + (sod-class-chain-head class)) + (sod-class-nickname class))))) + +(defun collect-initarg-keywords (class) + "Return a list of keyword arguments corresponding to CLASS's initargs. + + For each distinct name among the initargs defined on CLASS and its + superclasses, return a single `argument' object containing the (agreed + common) type, and the (unique, if present) default value from the most + specific defining superclass. + + The arguments are not returned in any especially meaningful order." + + (let ((map (make-hash-table :test #'equal)) + (default-map (make-hash-table :test #'equal)) + (list nil)) + (dolist (super (sod-class-precedence-list class)) + (dolist (initarg (sod-class-initargs super)) + (let ((name (sod-initarg-name initarg)) + (default (sod-initarg-default initarg))) + (unless (gethash name default-map) + (when (or default (not (gethash name map))) + (setf (gethash name map) (sod-initarg-argument initarg))) + (when default + (setf (gethash name default-map) t)))))) + (maphash (lambda (key value) + (declare (ignore key)) + (push value list)) + map) + list)) + +(definst suppliedp-struct (stream) (flags var) + (format stream + "~@" + flags var)) + +;; Initialization. + +(defclass initialization-message (lifecycle-message) + ()) + +(defclass initialization-effective-method (lifecycle-effective-method) + ()) + +(defmethod sod-message-effective-method-class + ((message initialization-message)) + 'initialization-effective-method) + +(defmethod method-keyword-argument-lists + ((method initialization-effective-method) direct-methods) + (append (call-next-method) + (delete-duplicates + (mapcan (lambda (class) + (let ((initargs (sod-class-initargs class))) + (and initargs + (list (cons (mapcar #'sod-initarg-argument + initargs) + (format nil "initargs for ~A" + class)))))) + (sod-class-precedence-list + (effective-method-class method))) + :key #'argument-name))) + +(defmethod lifecycle-method-kernel + ((method initialization-effective-method) codegen target) + (let* ((class (effective-method-class method)) + (keywords (collect-initarg-keywords class)) + (ilayout (sod-class-ilayout class)) + (obj-tag (ilayout-struct-tag class)) + (kw-tag (effective-method-keyword-struct-tag method)) + (kw-tail (and keywords + (list (make-argument + "sod__kw" + (c-type (* (struct kw-tag :const))))))) + (func-type (c-type (fun void + ("sod__obj" (* (struct obj-tag))) + . kw-tail))) + (func-name (format nil "~A__init" class)) + (done-setup-p nil)) + + ;; Start building the initialization function. + (codegen-push codegen) + + (labels ((set-from-initializer (var type init) + ;; Store the value of INIT, which has the given TYPE, in VAR. + ;; INIT has the syntax of an initializer: declare and + ;; initialize a temporary, and then copy the result. + ;; Compilers seem to optimize this properly. Return the + ;; resulting code as an instruction. + (codegen-push codegen) + (emit-decl codegen (make-var-inst *sod-tmp-val* type init)) + (deliver-expr codegen var *sod-tmp-val*) + (codegen-pop-block codegen)) + (setup () + ;; Do any necessary one-time initialization required to set up + ;; the environment for the initialization code. + (unless done-setup-p + + ;; Extract the keyword arguments into local variables. + (when keywords + (emit-decl codegen + (make-suppliedp-struct-inst + (mapcar #'argument-name keywords) + "suppliedp")) + (emit-banner codegen "Collect the keyword arguments.") + (dolist (arg keywords) + (let* ((name (argument-name arg)) + (type (argument-type arg)) + (default (argument-default arg)) + (kwvar (format nil "sod__kw->~A" name)) + (kwset (make-set-inst name kwvar)) + (suppliedp (format nil "suppliedp.~A" name))) + (emit-decl codegen (make-var-inst name type)) + (deliver-expr codegen suppliedp + (format nil "sod__kw->~A__suppliedp" + name)) + (emit-inst + codegen + (if default + (make-if-inst suppliedp kwset + (set-from-initializer name + type + default)) + kwset)))) + + (deliver-call codegen :void + "SOD__IGNORE" "suppliedp") + (dolist (arg keywords) + (deliver-call codegen :void + "SOD__IGNORE" (argument-name arg)))) + + (setf done-setup-p t)))) + + ;; Initialize the structure defined by the various superclasses, in + ;; reverse precedence order. + (dolist (super (reverse (sod-class-precedence-list class))) + (let* ((ichain (find (sod-class-chain-head super) + (ilayout-ichains ilayout) + :key #'ichain-head)) + (islots (find super (ichain-body ichain) + :test (lambda (class item) + (and (typep item 'islots) + (eq (islots-class item) class))))) + (frags (sod-class-initfrags super)) + (this-class-focussed-p nil) + (isl (format nil "me->~A" (sod-class-nickname super)))) + + (flet ((focus-this-class () + ;; Delayed initial preparation. Don't bother defining the + ;; `me' pointer if there's actually nothing to do. + (setup) + (unless this-class-focussed-p + (emit-banner codegen + "Initialization for class `~A'." super) + (codegen-push codegen) + (declare-me codegen super) + (setf this-class-focussed-p t)))) + + ;; Work through each slot in turn. + (dolist (slot (and islots (islots-slots islots))) + (let ((dslot (effective-slot-direct-slot slot)) + (init (effective-slot-initializer slot)) + (initargs (effective-slot-initargs slot))) + (when (or init initargs) + (focus-this-class) + (let* ((slot-type (sod-slot-type dslot)) + (slot-default (sod-initializer-value init)) + (target (format nil "~A.~A" + isl (sod-slot-name dslot))) + (initinst (set-from-initializer target + slot-type + slot-default))) + + ;; If there are applicable initialization arguments, + ;; check to see whether they were supplied. + (dolist (initarg (reverse (remove-duplicates + initargs + :key #'sod-initarg-name + :test #'string=))) + (let ((arg-name (sod-initarg-name initarg))) + (setf initinst (make-if-inst + (format nil "suppliedp.~A" arg-name) + (make-set-inst target arg-name) + initinst)))) + + (emit-inst codegen initinst))))) + + ;; Emit the class's initialization fragments. + (when frags + (let ((used-me-p this-class-focussed-p)) + (focus-this-class) + (unless used-me-p + (deliver-call codegen :void "SOD__IGNORE" "me"))) + (dolist (frag frags) + (codegen-push codegen) + (emit-inst codegen frag) + (emit-inst codegen (codegen-pop-block codegen)))) + + ;; If we opened a block to initialize this class then close it + ;; again. + (when this-class-focussed-p + (emit-inst codegen (codegen-pop-block codegen))))))) + + ;; Done making the initialization function. + (codegen-pop-function codegen func-name func-type + "Instance initialization function ~:_~ + for class `~A'." + class) + + (apply #'deliver-call codegen :void func-name + "sod__obj" (and keywords (list (keyword-struct-pointer)))))) + +;; Teardown. + +(defclass teardown-message (lifecycle-message) + ()) + +(defclass teardown-effective-method (lifecycle-effective-method) + ()) + +(defmethod sod-message-effective-method-class ((message teardown-message)) + 'teardown-effective-method) + +(defmethod lifecycle-method-kernel + ((method teardown-effective-method) codegen target) + (let* ((class (effective-method-class method)) + (obj-tag (ilayout-struct-tag class)) + (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag)))))) + (func-name (format nil "~A__teardown" class))) + (codegen-push codegen) + (dolist (super (sod-class-precedence-list class)) + (let ((frags (sod-class-tearfrags super))) + (when frags + (emit-banner codegen "Teardown for class `~A'." super) + (codegen-push codegen) + (declare-me codegen super) + (deliver-call codegen :void "SOD__IGNORE" "me") + (dolist (frag frags) + (codegen-push codegen) + (emit-inst codegen frag) + (emit-inst codegen (codegen-pop-block codegen))) + (emit-inst codegen (codegen-pop-block codegen))))) + (codegen-pop-function codegen func-name func-type + "Instance teardown function ~:_~ + for class `~A'." + class) + (deliver-call codegen :void + (format nil "~A__teardown" class) "sod__obj") + (deliver-expr codegen target 0))) + ;;;-------------------------------------------------------------------------- ;;; Bootstrapping the class graph. @@@ -508,14 -275,6 +511,14 @@@ (make-property-set :nick 'cls))) (classes (list sod-object sod-class))) + ;; Attach the built-in messages. + (make-sod-message sod-object "init" + (c-type (fun void :keys)) + (make-property-set + :message-class 'initialization-message)) + (make-sod-message sod-object "teardown" (c-type (fun int)) + (make-property-set :message-class 'teardown-message)) + ;; Sort out the recursion. (setf (slot-value sod-class 'chain-link) sod-object) (dolist (class classes) diff --combined src/c-types-impl.lisp index 16351a3,97b6742..fe75d7a --- a/src/c-types-impl.lisp +++ b/src/c-types-impl.lisp @@@ -66,16 -66,95 +66,95 @@@ (assert (gethash k map)))) *c-type-intern-map*))) + (defun make-or-intern-c-type (new-type-class base-types &rest initargs) + "Return a possibly-new instance of NEW-TYPE-CLASS with the given INITARGS. + + If all of the BASE-TYPES are interned, then use `intern-c-type' to + construct the new type; otherwise just make a new one with + `make-instance'. BASE-TYPES may be a singleton type, or a sequence of + types." + (apply (if (if (typep base-types 'sequence) + (every (lambda (type) + (gethash type *c-type-intern-map*)) + base-types) + (gethash base-types *c-type-intern-map*)) + #'intern-c-type #'make-instance) + new-type-class + initargs)) + + ;;;-------------------------------------------------------------------------- + ;;; Qualifiers. + + (defmethod c-qualifier-keyword ((qualifier (eql :atomic))) "_Atomic") + (defmethod qualify-c-type ((type qualifiable-c-type) qualifiers) (let ((initargs (instance-initargs type))) (remf initargs :qualifiers) - (apply (if (gethash type *c-type-intern-map*) - #'intern-c-type #'make-instance) - (class-of type) + (apply #'make-or-intern-c-type (class-of type) type :qualifiers (canonify-qualifiers (append qualifiers (c-type-qualifiers type))) initargs))) + ;;;-------------------------------------------------------------------------- + ;;; Storage specifiers. + + (defmethod c-type-equal-p :around + ((type-a c-storage-specifiers-type) (type-b c-type)) + "Ignore storage specifiers when comparing C types." + (c-type-equal-p (c-type-subtype type-a) type-b)) + + (defmethod c-type-equal-p :around + ((type-a c-type) (type-b c-storage-specifiers-type)) + "Ignore storage specifiers when comparing C types." + (c-type-equal-p type-a (c-type-subtype type-b))) + + (defun make-storage-specifiers-type (subtype specifiers) + "Construct a type based on SUBTYPE, carrying the storage SPECIFIERS." + (if (null specifiers) subtype + (make-or-intern-c-type 'c-storage-specifiers-type subtype + :specifiers specifiers + :subtype subtype))) + + (defmethod pprint-c-type ((type c-storage-specifiers-type) stream kernel) + (dolist (spec (c-type-specifiers type)) + (pprint-c-storage-specifier spec stream) + (write-char #\space stream) + (pprint-newline :miser stream)) + (pprint-c-type (c-type-subtype type) stream kernel)) + + (defmethod print-c-type + (stream (type c-storage-specifiers-type) &optional colon atsign) + (declare (ignore colon atsign)) + (format stream "~:@" + (c-type-subtype type) (c-type-specifiers type))) + + (export 'specs) + (define-c-type-syntax specs (subtype &rest specifiers) + `(make-storage-specifiers-type + ,(expand-c-type-spec subtype) + (list ,@(mapcar #'expand-c-storage-specifier specifiers)))) + + ;;;-------------------------------------------------------------------------- + ;;; Some storage specifiers. + + (export 'alignas-storage-specifier) + (defclass alignas-storage-specifier () + ((alignment :initarg :alignment :reader spec-alignment))) + + (export 'alignas) + (define-c-storage-specifier-syntax alignas (alignment) + `(make-instance 'alignas-storage-specifier :alignment ,alignment)) + + (defmethod print-c-storage-specifier + (stream (spec alignas-storage-specifier) &optional colon atsign) + (declare (ignore colon atsign)) + (format stream "~:@<~S ~_~S~:>" 'alignas (spec-alignment spec))) + + (defmethod pprint-c-storage-specifier + ((spec alignas-storage-specifier) stream) + (format stream "_Alignas(~A)" (spec-alignment spec))) + ;;;-------------------------------------------------------------------------- ;;; Simple C types. @@@ -106,8 -185,8 +185,8 @@@ (defmethod pprint-c-type ((type simple-c-type) stream kernel) (pprint-logical-block (stream nil) - (format stream "~{~(~A~) ~@_~}~A" - (c-type-qualifiers type) + (format stream "~{~A ~@_~}~A" + (c-type-qualifier-keywords type) (c-type-name type)) (funcall kernel stream 0 t))) @@@ -248,8 -327,8 +327,8 @@@ (defmethod pprint-c-type ((type tagged-c-type) stream kernel) (pprint-logical-block (stream nil) - (format stream "~{~(~A~) ~@_~}~(~A~) ~A" - (c-type-qualifiers type) + (format stream "~{~A ~@_~}~(~A~) ~A" + (c-type-qualifier-keywords type) (c-tagged-type-kind type) (c-type-tag type)) (funcall kernel stream 0 t))) @@@ -263,6 -342,55 +342,55 @@@ (c-type-tag type) (c-type-qualifiers type))) + ;;;-------------------------------------------------------------------------- + ;;; Atomic types. + + ;; Class definition. + + (export 'c-atomic-type) + (defclass c-atomic-type (qualifiable-c-type) + ((subtype :initarg :subtype :type c-type :reader c-type-subtype)) + (:documentation "C atomic types.")) + + ;; Constructor function. + + (export 'make-atomic-type) + (defun make-atomic-type (subtype &optional qualifiers) + "Return a (maybe distinguished) atomic type." + (make-or-intern-c-type 'c-atomic-type subtype + :subtype subtype + :qualifiers (canonify-qualifiers qualifiers))) + + ;; Comparison protocol. + + (defmethod c-type-equal-p and ((type-a c-atomic-type) (type-b c-atomic-type)) + (c-type-equal-p (c-type-subtype type-a) (c-type-subtype type-b))) + + ;; C-syntax output protocol. + + (defmethod pprint-c-type ((type c-atomic-type) stream kernel) + (pprint-logical-block (stream nil) + (format stream "~{~A ~@_~}" (c-type-qualifier-keywords type)) + (write-string "_Atomic(" stream) + (pprint-indent :current 0 stream) + (pprint-c-type (c-type-subtype type) stream + (lambda (stream prio spacep) + (declare (ignore stream prio spacep)))) + (write-char #\) stream))) + + ;; S-expression notation protocol. + + (defmethod print-c-type (stream (type c-atomic-type) &optional colon atsign) + (declare (ignore colon atsign)) + (format stream "~:@" + (c-type-subtype type) + (c-type-qualifiers type))) + + (export 'atomic) + (define-c-type-syntax atomic (sub &rest quals) + "Return the type of atomic SUB." + `(make-atomic-type ,(expand-c-type-spec sub) (list ,@quals))) + ;;;-------------------------------------------------------------------------- ;;; Pointer types. @@@ -278,12 -406,9 +406,9 @@@ (export 'make-pointer-type) (defun make-pointer-type (subtype &optional qualifiers) "Return a (maybe distinguished) pointer type." - (let ((canonical (canonify-qualifiers qualifiers))) - (funcall (if (gethash subtype *c-type-intern-map*) - #'intern-c-type #'make-instance) - 'c-pointer-type - :subtype subtype - :qualifiers canonical))) + (make-or-intern-c-type 'c-pointer-type subtype + :subtype subtype + :qualifiers (canonify-qualifiers qualifiers))) ;; Comparison protocol. @@@ -298,8 -423,8 +423,8 @@@ (lambda (stream prio spacep) (when spacep (c-type-space stream)) (maybe-in-parens (stream (> prio 1)) - (format stream "*~{~(~A~)~^ ~@_~}" - (c-type-qualifiers type)) + (format stream "*~{~A~^ ~@_~}" + (c-type-qualifier-keywords type)) (funcall kernel stream 1 (c-type-qualifiers type)))))) ;; S-expression notation protocol. @@@ -416,79 -541,6 +541,79 @@@ (argument-type arg-b))))) list-a list-b))) +(defun fix-and-check-keyword-argument-list (list) + "Check the keyword argument LIST is valid; if so, fix it up and return it. + + Check that the keyword arguments have distinct names. Fix the list up by + sorting it by keyword name." + + (unless (every #'argumentp list) + (error "(INTERNAL) not an argument value")) + + (let ((list (sort (copy-list list) #'string< :key #'argument-name))) + (do ((list (cdr list) (cdr list)) + (this (car list) (car list)) + (prev nil this)) + ((endp list)) + (when prev + (let ((this-name (argument-name this)) + (prev-name (argument-name prev))) + (when (string= this-name prev-name) + (error "Duplicate keyword argument name `~A'." this-name))))) + list)) + +(export 'merge-keyword-lists) +(defun merge-keyword-lists (lists) + "Return the union of keyword argument lists. + + The LISTS parameter consists of pairs (ARGS . WHAT), where ARGS is a list + of `argument' objects, and WHAT is either nil or a printable object + describing the origin of the corresponding argument list suitable for + quoting in an error message. + + The resulting list contains exactly one argument for each distinct + argument name appearing in the input lists; this argument will contain the + default value corresponding to the name's earliest occurrence in the input + LISTS. + + If the same name appears in multiple input lists with different types, an + error is signalled; this error will quote the origins of a representative + conflicting pair of arguments." + + ;; The easy way through all of this is with a hash table mapping argument + ;; names to (ARGUMENT . WHAT) pairs. + + (let ((argmap (make-hash-table :test #'equal))) + + ;; Set up the table. When we find a duplicate, check that the types + ;; match. + (dolist (item lists) + (let ((args (car item)) + (what (cdr item))) + (dolist (arg args) + (let* ((name (argument-name arg)) + (other-item (gethash name argmap))) + (if (null other-item) + (setf (gethash name argmap) (cons arg what)) + (let* ((type (argument-type arg)) + (other (car other-item)) + (other-type (argument-type other)) + (other-what (cdr other-item))) + (unless (c-type-equal-p type other-type) + (error "Type mismatch for keyword argument `~A': ~ + ~A~@[ (~A)~] doesn't match ~A~@[ (~A)~]." + name + type what + other-type other-what)))))))) + + ;; Now it's just a matter of picking the arguments out again. + (let ((result nil)) + (maphash (lambda (name item) + (declare (ignore name)) + (push (car item) result)) + argmap) + (fix-and-check-keyword-argument-list result)))) + ;; Class definition. (export '(c-function-type c-function-arguments)) @@@ -511,46 -563,13 +636,46 @@@ nil arguments)))) +(export '(c-keyword-function-type c-function-keywords)) +(defclass c-keyword-function-type (c-function-type) + ((keywords :initarg :keywords :type list + :reader c-function-keywords)) + (:documentation + "C function types for `functions' which take keyword arguments.")) + +(defmethod shared-initialize :after + ((type c-keyword-function-type) slot-names &key (keywords nil keysp)) + (declare (ignore slot-names)) + (when keysp + (setf (slot-value type 'keywords) + (fix-and-check-keyword-argument-list keywords)))) + ;; Constructor function. (export 'make-function-type) (defun make-function-type (subtype arguments) - "Return a new function type, returning SUBTYPE and accepting ARGUMENTS." - (make-instance 'c-function-type :subtype subtype - :arguments arguments)) + "Return a new function type, returning SUBTYPE and accepting ARGUMENTS. + + As a helper for dealing with the S-expression syntax for keyword + functions, if ARGUMENTS has the form (ARGS ... :keys KEYWORDS ...)' then + return a keyword function with arguments (ARGS ...) and keywords (KEYWORDS + ...)." + (let ((split (member :keys arguments))) + (if split + (make-instance 'c-keyword-function-type + :subtype subtype + :arguments (ldiff arguments split) + :keywords (cdr split)) + (make-instance 'c-function-type + :subtype subtype + :arguments arguments)))) + +(export 'make-keyword-function-type) +(defun make-keyword-function-type (subtype arguments keywords) + "Return a new keyword-function type, returning SUBTYPE and accepting + ARGUMENTS and KEYWORDS." + (make-instance 'c-keyword-function-type :subtype subtype + :arguments arguments :keywords keywords)) ;; Comparison protocol. @@@ -560,78 -579,29 +685,78 @@@ (argument-lists-equal-p (c-function-arguments type-a) (c-function-arguments type-b)))) +(defmethod c-type-equal-p and + ((type-a c-keyword-function-type) (type-b c-keyword-function-type)) + ;; Actually, there's nothing to check here. I'm happy as long as both + ;; functions notionally accept keyword arguments. + t) + ;; C syntax output protocol. +(export 'pprint-c-function-type) +(defun pprint-c-function-type (return-type stream print-args print-kernel) + "Common top-level printing for function types. + + Prints RETURN-TYPE (KERNEL(ARGS)), where RETURN-TYPE is the actual return + type, and ARGS and KERNEL are whatever is printed by the PRINT-ARGS and + PRINT-KERNEL functions. + + The PRINT-KERNEL function is the standard such thing for the + `pprint-c-type' protocol; PRINT-ARGS accepts just an output stream." + (pprint-c-type return-type stream + (lambda (stream prio spacep) + (maybe-in-parens (stream (> prio 2)) + (when spacep (c-type-space stream)) + (funcall print-kernel stream 2 nil) + (pprint-indent :block 4 stream) + (pprint-newline :linear stream) + (pprint-logical-block + (stream nil :prefix "(" :suffix ")") + (funcall print-args stream)))))) + +(export 'pprint-argument-list) +(defun pprint-argument-list (args stream) + "Print an argument list. + + The ARGS is a list of `argument' objects, optionally containing an + `:ellipsis' marker. The output is written to STREAM. + + Returns non-nil if any arguments were actually printed." + (let ((anyp nil)) + (pprint-logical-block (stream nil) + (dolist (arg args) + (if anyp + (format stream ", ~_") + (setf anyp t)) + (etypecase arg + ((member :ellipsis) + (write-string "..." stream)) + (argument + (pprint-logical-block (stream nil) + (pprint-c-type (argument-type arg) stream (argument-name arg)) + (let ((default (argument-default arg))) + (when default + (format stream " = ~2I~_~A" default)))))))) + anyp)) + (let ((void-arglist (list (make-argument nil c-type-void)))) (defmethod pprint-c-type ((type c-function-type) stream kernel) - (pprint-c-type (c-type-subtype type) stream - (lambda (stream prio spacep) - (maybe-in-parens (stream (> prio 2)) - (when spacep (c-type-space stream)) - (funcall kernel stream 2 nil) - (pprint-indent :block 4 stream) - (pprint-logical-block - (stream nil :prefix "(" :suffix ")") - (let ((firstp t)) - (dolist (arg (or (c-function-arguments type) - void-arglist)) - (if firstp - (setf firstp nil) - (format stream ", ~_")) - (if (eq arg :ellipsis) - (write-string "..." stream) - (pprint-c-type (argument-type arg) - stream - (argument-name arg))))))))))) + (let ((args (or (c-function-arguments type) void-arglist))) + (pprint-c-function-type (c-type-subtype type) stream + (lambda (stream) + (pprint-argument-list args stream)) + kernel)))) + +(defmethod pprint-c-type ((type c-keyword-function-type) stream kernel) + (let ((args (c-function-arguments type)) + (keys (c-function-keywords type))) + (pprint-c-function-type (c-type-subtype type) stream + (lambda (stream) + (when (pprint-argument-list args stream) + (format stream ", ~_")) + (write-char #\? stream) + (pprint-argument-list keys stream)) + kernel))) ;; S-expression notation protocol. @@@ -639,32 -609,21 +764,32 @@@ (stream (type c-function-type) &optional colon atsign) (declare (ignore colon atsign)) (format stream "~:@<~ - FUN ~@_~:I~/sod:print-c-type/~ - ~{ ~_~:<~S ~@_~/sod:print-c-type/~:>~}~ + FUN ~@_~:I~ + ~/sod:print-c-type/~:[~; ~]~:*~_~ + ~<~@{~:<~S ~@_~/sod:print-c-type/~:>~^ ~_~}~:>~ + ~:[~2*~; ~_~S ~@_~<~@{~:<~S ~@_~/sod:print-c-type/~ + ~@[ ~@_~S~]~:>~^ ~_~}~:>~]~ ~:>" (c-type-subtype type) (mapcar (lambda (arg) (if (eq arg :ellipsis) arg (list (argument-name arg) (argument-type arg)))) - (c-function-arguments type)))) + (c-function-arguments type)) + (typep type 'c-keyword-function-type) + :keys + (and (typep type 'c-keyword-function-type) + (mapcar (lambda (arg) + (list (argument-name arg) + (argument-type arg) + (argument-default arg))) + (c-function-keywords type))))) (export '(fun function () func fn)) (define-c-type-syntax fun (ret &rest args) "Return the type of functions which returns RET and has arguments ARGS. - The ARGS are a list of arguments of the form (NAME TYPE). The NAME can be - NIL to indicate that no name was given. + The ARGS are a list of arguments of the form (NAME TYPE [DEFAULT]). The + NAME can be NIL to indicate that no name was given. If an entry isn't a list, it's assumed to be the start of a Lisp expression to compute the tail of the list; similarly, if the list is @@@ -682,21 -641,16 +807,21 @@@ `(make-function-type ,(expand-c-type-spec ret) ,(do ((args args (cdr args)) (list nil - (cons `(make-argument ,(caar args) - ,(expand-c-type-spec - (cadar args))) - list))) - ((or (atom args) (atom (car args))) + (if (keywordp (car args)) + (cons (car args) list) + (let* ((name (caar args)) + (type (expand-c-type-spec + (cadar args))) + (default (and (cddar args) + (caddar args))) + (arg `(make-argument + ,name ,type ,default))) + (cons arg list))))) + ((or (atom args) + (and (atom (car args)) + (not (keywordp (car args))))) (cond ((and (null args) (null list)) `nil) ((null args) `(list ,@(nreverse list))) - ((and (consp args) - (eq (car args) :ellipsis)) - `(list ,@(nreverse list) :ellipsis)) ((null list) `,args) (t `(list* ,@(nreverse list) ,args))))))) (c-type-alias fun function () func fn) @@@ -712,8 -666,7 +837,8 @@@ (mapcar (lambda (arg) (if (eq arg :ellipsis) arg (make-argument (commentify-argument-name (argument-name arg)) - (argument-type arg)))) + (argument-type arg) + (argument-default arg)))) arguments)) (export 'commentify-function-type) @@@ -725,11 -678,4 +850,11 @@@ (commentify-argument-names (c-function-arguments type)))) +(export 'reify-variable-argument-tail) +(defun reify-variable-argument-tail (arguments) + "Replace any `:ellipsis' item in ARGUMENTS with a `va_list' argument. + + The argument's name is taken from the variable `*sod-ap*'." + (substitute (make-argument *sod-ap* c-type-va-list) :ellipsis arguments)) + ;;;----- That's all, folks -------------------------------------------------- diff --combined src/c-types-parse.lisp index 6a622b7,450cee6..6f5db4d --- a/src/c-types-parse.lisp +++ b/src/c-types-parse.lisp @@@ -70,15 -70,15 +70,15 @@@ ;; accessor functions later. ((label :type keyword :initarg :label :reader ds-label) (name :type string :initarg :name :reader ds-name) - (kind :type (member type complexity sign size qualifier) + (kind :type (member type complexity sign size qualifier specs) :initarg :kind :reader ds-kind) (taggedp :type boolean :initarg :taggedp :initform nil :reader ds-taggedp)) (:documentation "Represents the important components of a declaration specifier. - The only interesting instances of this class are in the table - `*declspec-map*'.")) + The only interesting instances of this class are in the table + `*declspec-map*'.")) (defmethod shared-initialize :after ((ds declspec) slot-names &key) "If no name is provided then derive one from the label. @@@ -90,13 -90,14 +90,14 @@@ (defparameter *declspec-map* (let ((map (make-hash-table :test #'equal))) (dolist (item '((type :void :char :int :float :double - (:bool :name "_Bool")) - (complexity (:complex :name "_Complex") - (:imaginary :name "_Imaginary")) + (:bool :compat "_Bool")) + (complexity (:complex :compat "_Complex") + (:imaginary :compat "_Imaginary")) ((type :taggedp t) :enum :struct :union) (size :short :long (:long-long :name "long long")) (sign :signed :unsigned) - (qualifier :const :restrict :volatile))) + (qualifier :const :restrict :volatile + (:atomic :compat "_Atomic")))) (destructuring-bind (kind &key (taggedp nil)) (let ((spec (car item))) (if (consp spec) spec (list spec))) @@@ -104,20 -105,32 +105,32 @@@ (destructuring-bind (label &key (name (string-downcase label)) + compat (taggedp taggedp)) (if (consp spec) spec (list spec)) (let ((ds (make-instance 'declspec :label label - :name name + :name (or compat name) :kind kind :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))) + (gethash label map) ds) + (when compat + (setf (gethash compat map) ds))))))) map) "Maps symbolic labels and textual names to `declspec' instances.") + (defclass storespec () + ((spec :initarg :spec :reader ds-spec)) + (:documentation "Carrier for a storage specifier.")) + + (defmethod ds-label ((spec storespec)) spec) + (defmethod ds-kind ((spec storespec)) 'specs) + + (defmethod ds-label ((ty c-type)) :c-type) + (defmethod ds-name ((ty c-type)) (princ-to-string ty)) + (defmethod ds-kind ((ty c-type)) 'type) + ;; A collection of declaration specifiers, and how to merge them together. (defclass declspecs () @@@ -127,25 -140,21 +140,21 @@@ (complexity :initform nil :initarg :complexity :reader ds-complexity) (sign :initform nil :initarg :sign :reader ds-sign) (size :initform nil :initarg :size :reader ds-size) + (specs :initform nil :initarg :specs :reader ds-specs) (qualifier :initform nil :initarg :qualifiers :reader ds-qualifiers)) - (:documentation - "Represents a collection of declaration specifiers. - - This is used during type parsing to represent the type under - construction. Instances are immutable: we build new ones rather than - modifying existing ones. This leads to a certain amount of churn, but - we'll just have to live with that. + (:documentation "Represents a collection of declaration specifiers. - (Why are instances immutable? Because it's much easier to merge a new - specifier into an existing collection and then check that the resulting - thing is valid, rather than having to deal with all of the possible - special cases of what the new thing might be. And if the merged - collection isn't good, I must roll back to the previous version. So I - don't get to take advantage of a mutable structure.)")) + This is used during type parsing to represent the type under construction. + Instances are immutable: we build new ones rather than modifying existing + ones. This leads to a certain amount of churn, but we'll just have to + live with that. - (defmethod ds-label ((ty c-type)) :c-type) - (defmethod ds-name ((ty c-type)) (princ-to-string ty)) - (defmethod ds-kind ((ty c-type)) 'type) + (Why are instances immutable? Because it's much easier to merge a new + specifier into an existing collection and then check that the resulting + thing is valid, rather than having to deal with all of the possible + special cases of what the new thing might be. And if the merged + collection isn't good, I must roll back to the previous version. So I + don't get to take advantage of a mutable structure.)")) (defparameter *good-declspecs* '(((:int) (:signed :unsigned) (:short :long :long-long) ()) @@@ -186,6 -195,7 +195,7 @@@ ((and (eq (ds-label old) :long) (eq ds old)) (values t (gethash :long-long *declspec-map*))) (t (values nil nil)))) + (specs (values t (adjoin (ds-spec ds) old))) (t (values (not old) ds))) (if ok (let ((copy (copy-instance specs))) @@@ -195,38 -205,46 +205,46 @@@ (defun declspecs-type (specs) "Convert `declspecs' SPECS into a standalone C type object." - (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 cplx) - (when (and sign (eq (ds-label sign) :signed) - (eq (ds-label type) :int)) - (setf sign nil)) - (cond ((and (or (null type) (eq (ds-label type) :int)) - (or size sign)) - (setf type nil)) - ((null type) - (setf type (gethash :int *declspec-map*)))) - (make-simple-type (format nil "~{~@[~A~^ ~]~}" - (mapcar #'ds-name - (remove nil - (list sign cplx - size type)))) - quals)) - (t - nil)))) + (let* ((base-type (ds-type specs)) + (size (ds-size specs)) + (sign (ds-sign specs)) + (cplx (ds-complexity specs)) + (quals (mapcar #'ds-label (ds-qualifiers specs))) + (specs (ds-specs specs)) + (type (cond ((typep base-type 'c-type) + (qualify-c-type base-type quals)) + ((or base-type size sign cplx) + (when (and sign (eq (ds-label sign) :signed) + (eq (ds-label base-type) :int)) + (setf sign nil)) + (cond ((and (or (null base-type) + (eq (ds-label base-type) :int)) + (or size sign)) + (setf base-type nil)) + ((null base-type) + (setf base-type (gethash :int *declspec-map*)))) + (let* ((things (list sign cplx size base-type)) + (stripped (remove nil things)) + (names (mapcar #'ds-name stripped))) + (make-simple-type (format nil "~{~A~^ ~}" names) + quals))) + (t + nil)))) + (cond ((null type) nil) + ((null specs) type) + (t (make-storage-specifiers-type type specs))))) ;; Parsing declaration specifiers. (define-indicator :declspec "") - (defun scan-declspec + (defun scan-simple-declspec (scanner &key (predicate (constantly t)) (indicator :declspec)) - "Scan a `declspec' from SCANNER. + "Scan a simple `declspec' from SCANNER. + + Simple declspecs are the ones defined in the `*declspec-map*' or + `*module-type-map*'. This covers the remaining possibilities if the + `complex-declspec' pluggable parser didn't find anything to match. If PREDICATE is provided then only succeed if (funcall PREDICATE DECLSPEC) is true, where DECLSPEC is the raw declaration specifier or C-type object, @@@ -257,6 -275,34 +275,34 @@@ (scanner-step scanner) (values ds t t))))) + (define-pluggable-parser complex-declspec atomic-typepsec (scanner) + ;; `atomic' `(' type-name `)' + ;; `_Atomic' `(' type-name `)' + (with-parser-context (token-scanner-context :scanner scanner) + (parse (peek (seq ((nil (or "atomic" "_Atomic")) + #\( + (decls (parse-c-type scanner)) + (subtype (parse-declarator scanner decls + :kernel (parse-empty) + :abstractp t)) + #\)) + (make-atomic-type (car subtype))))))) + + (define-pluggable-parser complex-declspec alignas (scanner) + ;; `alignas' `(' fragment `)' + ;; `_Alignas' `(' fragment `)' + (with-parser-context (token-scanner-context :scanner scanner) + (parse (peek (seq ((nil (or "alignas" "_Alignas")) + (nil (lisp (values #\( + (eq (token-type scanner) #\() + nil))) + (nil (commit)) + (frag (parse-delimited-fragment scanner #\( #\)))) + (make-instance 'storespec + :spec (make-instance + 'alignas-storage-specifier + :alignment frag))))))) + (defun scan-and-merge-declspec (scanner specs) "Scan a declaration specifier and merge it with SPECS. @@@ -266,7 -312,9 +312,9 @@@ SPECS." (with-parser-context (token-scanner-context :scanner scanner) - (if-parse (:consumedp consumedp) (scan-declspec scanner) + (if-parse (:consumedp consumedp) + (or (plug complex-declspec scanner) + (scan-simple-declspec scanner)) (aif (combine-declspec specs it) (values it t consumedp) (values (list :declspec) nil consumedp))))) @@@ -302,7 -350,7 +350,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 @@@ -315,23 -363,10 +363,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))) @@@ -342,7 -377,7 +390,7 @@@ (parse (seq ((quals (list () - (scan-declspec + (scan-simple-declspec scanner :indicator :qualifier :predicate (lambda (ds) @@@ -351,18 -386,12 +399,18 @@@ '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)))))) @@@ -402,64 -431,26 +450,64 @@@ (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)) @@@ -468,26 -459,16 +516,26 @@@ (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 () @@@ -503,7 -484,6 +551,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))))))) @@@ -525,6 -505,9 +573,9 @@@ (or (postfix-lparen) (lbracket) (when nestedp (seq (#\)) (rparen #\)))))))) - (cons (funcall (car value) base-type) (cdr value)))))))) + (cons (wrap-c-type (lambda (type) + (funcall (car value) type)) + base-type) + (cdr value)))))))) ;;;----- That's all, folks -------------------------------------------------- diff --combined src/c-types-proto.lisp index 9fe6126,0c95d02..f8e7589 --- a/src/c-types-proto.lisp +++ b/src/c-types-proto.lisp @@@ -57,6 -57,16 +57,16 @@@ The qualifiers of the returned type are the union of the requested QUALIFIERS and the qualifiers already applied to TYPE.")) + (export 'c-qualifier-keyword) + (defgeneric c-qualifier-keyword (qualifier) + (:documentation "Return the C keyword for the QUALIFIER (a Lisp keyword).") + (:method ((qualifier symbol)) (string-downcase qualifier))) + + (export 'c-type-qualifier-keywords) + (defun c-type-qualifier-keywords (c-type) + "Return the type's qualifiers, as a list of C keyword names." + (mapcar #'c-qualifier-keyword (c-type-qualifiers c-type))) + (export 'c-type-subtype) (defgeneric c-type-subtype (type) (:documentation @@@ -152,13 -162,11 +162,11 @@@ (export '(expand-c-type-spec expand-c-type-form)) (eval-when (:compile-toplevel :load-toplevel :execute) (defgeneric expand-c-type-spec (spec) - (:documentation - "Expand SPEC into Lisp code to construct a C type.") + (:documentation "Expand SPEC into Lisp code to construct a C type.") (:method ((spec list)) (expand-c-type-form (car spec) (cdr spec)))) (defgeneric expand-c-type-form (head tail) - (:documentation - "Expand a C type list beginning with HEAD.") + (:documentation "Expand a C type list beginning with HEAD.") (:method ((name (eql 'lisp)) tail) `(progn ,@tail)))) @@@ -168,12 -176,12 +176,12 @@@ (expand-c-type-spec spec)) (export 'define-c-type-syntax) - (defmacro define-c-type-syntax (name bvl &rest body) + (defmacro define-c-type-syntax (name bvl &body body) "Define a C-type syntax function. A function defined by BODY and with lambda-list BVL is associated with the - NAME. When `expand-c-type' sees a list (NAME . STUFF), it will call this - function with the argument list STUFF." + NAME. When `expand-c-type-spec' sees a list (NAME . STUFF), it will call + this function with the argument list STUFF." (with-gensyms (head tail) (multiple-value-bind (doc decls body) (parse-body body) `(eval-when (:compile-toplevel :load-toplevel :execute) @@@ -201,8 -209,8 +209,8 @@@ "Define NAMES all to describe the C-type VALUE. NAMES can be a symbol (treated as a singleton list), or a list of symbols. - The VALUE is a C type S-expression, acceptable to `expand-c-type'. It - will be expanded once at run-time." + The VALUE is a C type S-expression, acceptable to `expand-c-type-spec'. + It will be expanded once at run-time." (let* ((names (if (listp names) names (list names))) (namevar (gensym "NAME")) (typevar (symbolicate 'c-type- (car names)))) @@@ -237,18 -245,103 +245,105 @@@ (error "Bad character in C name ~S." name)))))) (t name))) + ;;;-------------------------------------------------------------------------- + ;;; Storage specifier protocol. + + (export 'pprint-c-storage-specifier) + (defgeneric pprint-c-storage-specifier (spec stream) + (:documentation "Print the storage specifier SPEC to STREAM, as C syntax.") + (:method ((spec symbol) stream) (princ (string-downcase spec) stream))) + + (export 'print-c-storage-specifier) + (defgeneric print-c-storage-specifier (stream spec &optional colon atsign) + (:documentation + "Print the storage specifier SPEC to STREAM, as an S-expression. + + This function is suitable for use in `format's ~/.../ command.") + (:method (stream (spec t) &optional colon atsign) + (declare (ignore colon atsign)) + (prin1 spec stream)) + (:method (stream (spec symbol) &optional colon atsign) + (declare (ignore colon atsign)) + (princ (string-downcase spec) stream))) + + (export '(expand-c-storage-specifier expand-c-storage-specifier-form)) + (eval-when (:compile-toplevel :load-toplevel :execute) + (defgeneric expand-c-storage-specifier (spec) + (:documentation + "Expand SPEC into Lisp code to construct a storage specifier.") + (:method ((spec list)) + (expand-c-storage-specifier-form (car spec) (cdr spec))) + (:method ((spec symbol)) + (if (keywordp spec) spec + (expand-c-storage-specifier-form spec nil)))) + (defgeneric expand-c-storage-specifier-form (head tail) + (:documentation + "Expand a C storage-specifier form beginning with HEAD.") + (:method ((name (eql 'lisp)) tail) + `(progn ,@tail)))) + + (export 'define-c-storage-specifier-syntax) + (defmacro define-c-storage-specifier-syntax (name bvl &body body) + "Define a C storage-specifier syntax function. + + A function defined by BODY and with lambda-list BVL is associated wth the + NAME. When `expand-c-storage-specifier' sees a list (NAME . STUFF), it + will call this function with the argument list STUFF." + (with-gensyms (head tail) + (multiple-value-bind (doc decls body) (parse-body body) + `(eval-when (:compile-toplevel :load-toplevel :execute) + (defmethod expand-c-storage-specifier-form + ((,head (eql ',name)) ,tail) + ,@doc + (destructuring-bind ,bvl ,tail + ,@decls + (block ,name ,@body))) + ',name)))) + + ;;;-------------------------------------------------------------------------- + ;;; A type for carrying storage specifiers. + + (export '(c-storage-specifiers-type c-type-specifiers)) + (defclass c-storage-specifiers-type (c-type) + ((specifiers :initarg :specifiers :type list :reader c-type-specifiers) + (subtype :initarg :subtype :type c-type :reader c-type-subtype)) + (:documentation + "A type for carrying storage specifiers. + + Properly, storage specifiers should only appear on an outermost type. + This fake C type is a handy marker for the presence of storage specifiers, + so that they can be hoisted properly when constructing derived types.")) + + (export 'wrap-c-type) + (defun wrap-c-type (wrapper-func base-type) + "Handle storage specifiers correctly when making a derived type. + + WRAPPER-FUNC should be a function which will return some derived type of + BASE-TYPE. This function differs from `funcall' only when BASE-TYPE is + actually a `c-storage-specifiers-type', in which case it invokes + WRAPPER-FUNC on the underlying type, and re-attaches the storage + specifiers to the derived type." + (if (typep base-type 'c-storage-specifiers-type) + (let* ((unwrapped-type (c-type-subtype base-type)) + (wrapped-type (funcall wrapper-func unwrapped-type)) + (specifiers (c-type-specifiers base-type))) + (make-or-intern-c-type 'c-storage-specifiers-type unwrapped-type + :specifiers specifiers + :subtype wrapped-type)) + (funcall wrapper-func base-type))) + ;;;-------------------------------------------------------------------------- ;;; Function arguments. -(export '(argument argumentp make-argument argument-name argument-type)) -(defstruct (argument (:constructor make-argument (name type +(export '(argument argumentp make-argument + argument-name argument-type argument-default)) +(defstruct (argument (:constructor make-argument (name type &optional default &aux (%type type))) (:predicate argumentp)) "Simple structure representing a function argument." (name nil :type t :read-only t) - (%type nil :type c-type :read-only t)) + (%type nil :type c-type :read-only t) + (default nil :type t :read-only t)) (define-access-wrapper argument-type argument-%type :read-only t) (export 'commentify-argument-name) diff --combined src/module-parse.lisp index 35e07ea,e33b6d3..15a7d8f --- a/src/module-parse.lisp +++ b/src/module-parse.lisp @@@ -199,34 -199,6 +199,34 @@@ (export 'class-item) +(define-pluggable-parser class-item initfrags (scanner class pset) + ;; raw-class-item ::= frag-keyword `{' c-fragment `}' + ;; frag-keyword ::= `init' | `teardown' + (with-parser-context (token-scanner-context :scanner scanner) + (parse (seq ((make (or (seq ("init") #'make-sod-class-initfrag) + (seq ("teardown") #'make-sod-class-tearfrag))) + (frag (parse-delimited-fragment scanner #\{ #\}))) + (funcall make class frag pset scanner))))) + +(define-pluggable-parser class-item initargs (scanner class pset) + ;; initarg-item ::= `initarg' declspec+ init-declarator-list + ;; init-declarator ::= declarator [`=' initializer] + (with-parser-context (token-scanner-context :scanner scanner) + (parse (seq ("initarg" + (base-type (parse-c-type scanner)) + (nil (skip-many (:min 1) + (seq ((declarator (parse-declarator scanner + base-type)) + (init (? (parse-delimited-fragment + scanner #\= (list #\; #\,) + :keep-end t)))) + (make-sod-user-initarg class + (cdr declarator) + (car declarator) + pset init scanner)) + #\,)) + #\;))))) + (defun parse-class-body (scanner pset name supers) ;; class-body ::= `{' class-item* `}' ;; @@@ -248,7 -220,6 +248,7 @@@ ;; names. (parse-declarator scanner base-type + :keywordp t :kernel (parser () (seq ((name-a :id) (name-b (? (seq (#\. (id :id)) id)))) @@@ -281,12 -252,50 +281,12 @@@ body sub-pset scanner)))) (parse-initializer () - ;; initializer ::= `=' c-fragment | `=' `{' c-fragment `}' + ;; initializer ::= `=' c-fragment ;; - ;; Return (VALUE-KIND . VALUE-FORM), ready for passing to a - ;; `sod-initializer' constructor. - - ;; 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))))))) + ;; Return a VALUE, ready for passing to a `sod-initializer' + ;; constructor. + (parse-delimited-fragment scanner #\= (list #\, #\;) + :keep-end t)) (parse-slot-item (sub-pset base-type type name) ;; slot-item ::= @@@ -300,7 -309,8 +300,7 @@@ sub-pset scanner) (when init (make-sod-instance-initializer - class nick name (car init) (cdr init) - sub-pset scanner))) + class nick name init sub-pset scanner))) (skip-many () (seq (#\, (ds (parse-declarator scanner @@@ -310,26 -320,25 +310,26 @@@ sub-pset scanner) (when init (make-sod-instance-initializer - class nick (cdr ds) - (car init) (cdr init) + class nick (cdr ds) init sub-pset scanner)))) #\;))) - (parse-initializer-item (sub-pset constructor) + (parse-initializer-item (sub-pset must-init-p constructor) ;; initializer-item ::= ;; [`class'] -!- slot-initializer-list `;' ;; - ;; slot-initializer ::= id `.' id initializer - (parse (and (skip-many () - (seq ((name-a :id) #\. (name-b :id) - (init (parse-initializer))) - (funcall constructor class - name-a name-b - (car init) (cdr init) - sub-pset scanner)) - #\,) - #\;))) + ;; slot-initializer ::= id `.' id [initializer] + (let ((parse-init (if must-init-p + #'parse-initializer + (parser () (? (parse-initializer)))))) + (parse (and (skip-many () + (seq ((name-a :id) #\. (name-b :id) + (init (funcall parse-init))) + (funcall constructor class + name-a name-b init + sub-pset scanner)) + #\,) + #\;)))) (class-item-dispatch (sub-pset base-type type name) ;; Logically part of `parse-raw-class-item', but the @@@ -360,7 -369,6 +360,7 @@@ ;; | method-item ;; | slot-item ;; | initializer-item + ;; | initfrag-item ;; ;; Most of the above begin with declspecs and a declarator ;; (which might be dotted). So we parse that here and @@@ -369,16 -377,17 +369,17 @@@ (peek (seq ((ds (parse-c-type scanner)) (dc (parse-maybe-dotted-declarator ds)) + (nil (commit)) (nil (class-item-dispatch sub-pset ds (car dc) (cdr dc)))))) (and "class" (parse-initializer-item - sub-pset + sub-pset t #'make-sod-class-initializer)) (parse-initializer-item - sub-pset + sub-pset nil #'make-sod-instance-initializer))))) (parse (seq (#\{ diff --combined src/sod-module.5 index 66937da,1837524..949e781 --- a/src/sod-module.5 +++ b/src/sod-module.5 @@@ -542,10 -542,6 +542,10 @@@ class-definitio .| .I initializer-item .| +.I initarg-item +.| +.I fragment-item +.| .I message-item .| .I method-item @@@ -572,48 -568,29 +572,48 @@@ .I slot-initializer ::= .I dotted-name -.B = -.I initializer +.RB [ = +.IR initializer ] .br .I initializer ::= +.I c-fragment +.br +.I initarg-item +::= +.< +.B initarg +.IR declaration-specifier \*+ +.IR list [ init-declarator ] +.B ; +.br +.I fragment-item +::= +.I fragment-kind .B { .I c-fragment .B } +.br +.I fragment-kind +::= +.B init | -.I c-fragment +.B teardown .br .I message-item ::= .< .IR declaration-specifier \*+ -.I simple-declarator +.IR keyword-declarator [ identifier ] +.< .RI [ method-body ] .br .I method-item ::= .< .IR declaration-specifier \*+ -.IR declarator [ dotted-name ] +.IR keyword-declarator [ dotted-name ] +.< .I method-body .br .I method-body @@@ -735,15 -712,49 +735,49 @@@ .B _Complex .| .I qualifier + .| + .I storage-specifier + .| + .I atomic-type .br .I qualifier ::= + .I atomic + | .B const | .B volatile | .B restrict .br + .I atomic-type + ::= + .I + atomic + .B ( + .IR declaration-specifier \*+ + .I abstract-declarator + .B ) + .br + .I atomic + ::= + .B atomic + | + .B _Atomic + .br + .I storage-specifier + ::= + .I alignas + .B ( + .I c-fragment + .B ) + .br + .I alignas + ::= + .B alignas + | + .B _Alignas + .br .I type-name ::= .I identifier @@@ -765,6 -776,8 +799,8 @@@ However, not all combinations are permi A declaration specifier must consist of zero or more .IR qualifier s, + zero or more + .IR storage-specifier s, and one of the following, up to reordering. .hP \*o .I type-name @@@ -832,35 -845,35 +868,35 @@@ .BR "double complex" , .B "long double complex" .PP -.IR declarator [ k ] +.IR declarator [ k ", " a ] ::= .IR pointer \** -.IR primary-declarator [ k ] +.IR primary-declarator [ k ", " a ] .br -.IR primary-declarator [ k ] +.IR primary-declarator [ k ", " a ] ::= .I k .| .B ( -.IR primary-declarator [ k ] +.IR primary-declarator [ k ", " a ] .B ) .| -.IR primary-declarator [ k ] -.IR declarator-suffix +.IR primary-declarator [ k ", " a ] +.IR declarator-suffix [ a ] .br .I pointer ::= .B * .IR qualifier \** .br -.I declarator-suffix +.IR declarator-suffix [ a ] ::= .B [ .I c-fragment .B ] .| .B ( -.I argument-list +.I a .B ) .br .I argument-list @@@ -877,41 -890,23 +913,45 @@@ .IR declaration-specifier \*+ .I argument-declarator .br + .I abstract-declarator + ::= -.IR declarator [\*e] ++.IR declarator "[\*e, " argument-list ] + .br .I argument-declarator ::= -.IR declarator [ identifier " | \*e]" +.IR declarator [ identifier " | \*e, " argument-list ] .br .I simple-declarator ::= -.IR declarator [ identifier ] +.IR declarator [ identifier ", " argument-list ] +.br +.I keyword-argument +::= +.I argument +.RB [ = +.IR c-fragment ] +.br +.I keyword-argument-list +::= +.I argument-list +.B ?\& +.I keyword-argument-list +.br +.I method-argument-list +::= +.I argument-list +| +.I keyword-argument-list .br .I dotted-name ::= .I identifier .B .\& .I identifier +.br +.IR keyword-declarator [ k ] +::= +.IR declarator [ k ", " method-argument-list ] . .\"-------------------------------------------------------------------------- .SH SEE ALSO