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
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
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
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
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
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
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
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
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
*null-pointer* variable
*sod-ap* variable
*sod-master-ap* variable
+ banner-inst class
block-inst class
break-inst class
call-inst class
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
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
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
*sod-version* variable
exercise function
test-module function
+ test-parse-c-type function
test-parser macro
fragment-parse.lisp
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
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
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
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
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
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
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
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
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
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
compute-cpl
sod-class
compute-effective-method-body
+ basic-effective-method t t
simple-effective-method t t
compute-effective-methods
sod-class
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
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
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
(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
(eql short) t
(eql signed-char) t
(eql size-t) t
+ (eql specs) t
(eql struct) t
(eql unsigned) t
(eql unsigned-char) t
c-fragment
property
sod-class
+ sod-initarg
sod-initializer
sod-message
sod-method
module
finalize-sod-class
sod-class
+find-slot-initargs
+ sod-class sod-slot
find-slot-initializer
sod-class sod-slot
format-temporary-name
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
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
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
inst-type
function-inst
var-inst
+inst-update
+ for-inst
inst-var
set-inst
+ sod::suppliedp-struct-inst
update-inst
invoke-sequencer-items
sequencer
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
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
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)
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
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
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
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)
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)
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
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
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
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
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
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
sod:c-fragment
sod:property
sod:sod-class
+ sod:sod-initarg
sod:sod-initializer
sod:sod-message
sod:sod-method
@|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}
type specifiers among its arguments.
\end{describe}
- \begin{describe}{fun}{expand-c-type-spec @<type-spec> @> @<form>}
+ \begin{describe}{gf}{expand-c-type-spec @<type-spec> @> @<form>}
Returns the Lisp form that @|(c-type @<type-spec>)| would expand into.
+
+ If @<type-spec> is a list, then \descref{expand-c-type-form}{fun} is
+ invoked.
+ \end{describe}
+
+ \begin{describe}{gf}{expand-c-type-form @<head> @<tail> @> @<form>}
+ Returns the Lisp form that @|(c-type (@<head> . @<tail>)| would expand
+ into.
\end{describe}
\begin{describe}{gf}
\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|,
non-null then the final character of the returned string will be a space.
\end{describe}
+ \begin{describe}{gf}{c-qualifier-keyword @<qualifier> @> @<string>}
+ Return, as a string, the C keyword corresponding to the Lisp @<qualifier>.
+
+ 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 @<keyword> @> @<string>}
+ Returns the @<keyword>'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 @<c-type> @> @<list>}
+ Return the @<c-type>'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 @<subtype> is the actual
+ type, and may be any C type; the @<specifiers> are a list of
+ storage-specifier objects.
+
+ The type specifier @|(specs @<subtype> @<specifier>^*)| wraps the
+ @<subtype> in a @|c-storage-specifiers-type|, carrying the @<specifier>s,
+ which are a list of storage specifiers in S-expression notation.
+ \end{describe}
+
+ \begin{describe}{fun}{c-type-specifiers @<type> @> @<list>}
+ Returns the list of type specifiers attached to the @<type> object, which
+ must be a @|c-storage-specifiers-type|.
+ \end{describe}
+
+ \begin{describe}{mac}
+ {define-c-storage-specifier-syntax @<name> @<lambda-list> \\ \ind
+ @[[ @<declaration>^* @! @<doc-string> @]] \\
+ @<form>^* \-
+ \nlret @<name>}
+
+ Defines the symbol @<name> as a new storage-specifier operator. When a
+ list of the form @|(@<name> @<argument>^*)| is used as a storage specifier,
+ the @<argument>s are bound to fresh variables according to the
+ @<lambda-list> (a destructuring lambda-list) and the @<form>s evaluated in
+ order in the resulting lexical environment as an implicit @<progn>. The
+ value should be a Lisp form which will evaluate to the storage-specifier
+ object described by the arguments.
+
+ The @<form>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 @<spec> @> @<form>}
+ Returns the Lisp form that @<spec> expands to within @|(c-type (specs
+ @<subtype> @<spec>))|.
+
+ If @<spec> is a list, then \descref{expand-c-storage-specifier-form} is
+ invoked.
+ \end{describe}
+
+ \begin{describe}{gf}{expand-c-storage-specifier-form @<spec> @> @<form>}
+ Returns the Lisp form that @|(@<head> . @<tail>)| expands to within
+ @|(c-type (specs @<subtype> (@<head> . @<tail>)))|.
+ \end{describe}
+
+ \begin{describe}{gf}{pprint-c-storage-specifier @<spec> @<stream>}
+ \end{describe}
+
+ \begin{describe}{gf}
+ {print-c-storage-specifier @<stream> @<spec>
+ \&optional @<colon> @<atsign>}
+ \end{describe}
+
+ \begin{describe}{fun}{wrap-c-type @<func> @<base-type> @> @<c-type>}
+ Apply @<func> to the underlying C type of @<base-type> to create a new
+ `wrapped' type, and attach the storage specifiers of @<base-type> to the
+ wrapped type.
+
+ If @<base-type> is \emph{not} a @|c-storage-specifiers-type|, then return
+ @|(funcall @<func> @<base-type>)|. Otherwise, return a new
+ @|c-storage-specifiers-type|, with the same specifiers, but whose subtype
+ is the result of applying @<func> to the subtype of the original
+ @<base-type>.
+ \end{describe}
+
+ \begin{describe}{cls}{alignas-storage-specifier () \&key :alignment}
+ The class of @|_Alignas| storage specifiers; an instance denotes the
+ specifier @|_Alignas(@<alignment>)|. The @<alignment> parameter may be any
+ printable object, but is usually a string or C fragment.
+
+ The storage specifier form @|(alignas @<alignment>)| returns a storage
+ specifier @|_Alignas(@<alignment>)|, where @<alignment> is evaluated.
+ \end{describe}
+
\subsection{Leaf types} \label{sec:clang.c-types.leaf}
\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(@<subtype>)|.
+
+ The @<subtype> 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 @<type-spec> @<qualifier>^*)| returns a type
+ qualified atomic @<subtype>, where @<subtype> is the type specified by
+ @<type-spec> and the @<qualifier>s are qualifier keywords (which are
+ evaluated).
+ \end{describe}
+
+ \begin{describe}{fun}
+ {make-atomic-type @<c-type> \&optional @<qualifiers> @> @<c-atomic-type>}
+ Return an object describing the type qualified atomic @<subtype>. If
+ @<subtype> 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
not return nil.
\end{describe}
-\begin{describe}{fun}{make-argument @<name> @<c-type> @> @<argument>}
+\begin{describe}{fun}
+ {make-argument @<name> @<c-type> \&optional @<default> @> @<argument>}
Construct and a return a new @<argument> object. The argument has type
@<c-type>, which must be a @|c-type| object, and is named @<name>.
suitable for function definitions. If @<name> is not nil, then the
@<name>'s print representation, with @|*print-escape*| nil, is used as the
argument name.
+
+ A @<default> 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 @<default> 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 @<argument> @> @<name>}
- \dhead{fun}{argument-type @<argument> @> @<c-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 @<argument> @> @<c-type>}
+ \dhead{fun}{argument-default @<argument> @> @<default>}}
+ Accessor functions for @|argument| objects. They return the appropriate
+ component of the object, as set by to @|make-argument|. The @<default> is
+ nil if no default was provided to @|make-argument|.
\end{describe*}
\begin{describe}{gf}
\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 @<arguments>, and keyword arguments from the @<keywords>
+ list, and returns @<subtype>. Either or both of the @<arguments> and
+ @<keywords> 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 @<arguments> and @<keywords>
+ 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 \=@<return-type>
+ @{ (@<arg-name> @<arg-type>) @}^* \+ \\
+ @{ \=:keys @{ (@<kw-name> @<kw-type> @[@<kw-default>@]) @}^*
+ @[. @<form>@] @! \+ \\
+ . @<form> @}
+ \end{prog}
+ where either the symbol @|:keys| appears literally in the specifier, or the
+ @<form> 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
+ @<form> is handled.
+
+ The list of @<arg-name>s and @<arg-type>s describes the positional
+ arguments. The list of @<kw-name>s, @<kw-type>s and @<kw-defaults>s
+ describes the keyword arguments.
+\end{describe}
+
\begin{describe}{fun}
{make-function-type @<subtype> @<arguments> @> @<c-function-type>}
Construct and return a new function type, returning @<subtype> and
accepting the @<arguments>.
+
+ If the @<arguments> 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 @<subtype> @<arguments> @<keywords>
+ \nlret @<c-keyword-function-type>}
+ Construct and return a new keyword-function type, returning @<subtype> and
+ accepting the @<arguments> and @<keywords>.
\end{describe}
\begin{describe}{gf}
@|commentify-argument-names| to the argument list of the given type.
\end{describe}
+\begin{describe}{fun}{reify-variable-argument-tail @<arguments> @> @<list>}
+ If the @<argument> 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 @<lists> @> @<list>}
+ Merge a number of keyword-argument lists together and return the result.
+
+ The @<lists> parameter is a list consisting of a number of @|(@<args>
+ . @<origin>)| pairs: in each pair, @<args> is a list of
+ \descref{argument}{cls} objects, and @<origin> is either nil or an object
+ whose printed representation describes the origin of the corresponding
+ @<args> list, suitable for inclusion 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 from the earliest occurrence in the input @<lists> 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 @<return-type> @<stream>
+ @<print-args> @<print-kernel>}
+ Provides the top-level structure for printing C function types.
+
+ Output is written to @<stream> to describe a function type returning
+ @<return-type>, whose declarator kernel (containing the name, and any
+ further type operands) will be printed by @<print-kernel>, and whose
+ arguments, if any, will be printed by @<print-args>.
+
+ The @<print-kernel> function is a standard kernel-printing function
+ following the \descref{pprint-c-type}[protocol]{gf}.
+
+ The @<print-args> function is given a single argument, which is the
+ @<stream> to print on. It should not print the surrounding parentheses.
+
+ The output written to @<stream> looks approximately like
+ \begin{prog}
+ @<return-type> @<kernel>(@<args>)
+ \end{prog}
+\end{describe}
+
+\begin{describe}{fun}{pprint-argument-list @<args> @<stream> @> @<flag>}
+ Print an argument list to @<stream>.
+
+ The @<args> 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}
@<form>^*}
\end{describe}
+\begin{describe}{fun}
+ {format-banner-comment @<stream> @<control> \&rest @<args>}
+\end{describe}
+
\begin{table}
\begin{tabular}[C]{ll>{\codeface}l} \hlx*{hv}
\thd{Class name} &
@|set| & @<var> @<expr> & @<var> = @<expr>; \\ \hlx{v}
@|update| & @<var> @<op> @<expr> & @<var> @<op>= @<expr>;
\\ \hlx{v}
+ @|cond| & @<cond> @<conseq> @<alt> & @<cond> ? @<conseq> : @<alt>
+ \\ \hlx{v}
@|return| & @<expr> & return @[@<expr>@];
\\ \hlx{v}
@|break| & --- & break; \\ \hlx{v}
@|call| & @<func> @|\&rest| @<args>
& @<func>(@<arg>_1,
$\ldots$,
- @<arg>_n) \\ \hlx{vhv}
+ @<arg>_n) \\ \hlx{v}
+ @|banner| & @<control> @|\&rest| @<args>
+ & /* @<banner> */ \\ \hlx{vhv}
@|block| & @<decls> @<body> & \{ @[@<decls>@] @<body> \}
\\ \hlx{v}
@|if| & @<cond> @<conseq> @|\&optional| @<alt>
& if (@<cond>) @<conseq>
@[else @<alt>@] \\ \hlx{v}
+ @|for| & @<init> @<cond> @<update> @<body> &
+ for (@<init>; @<cond>; @<update>) @<body> \\ \hlx{v}
@|while| & @<cond> @<body> & while (@<cond>) @<body>
\\ \hlx{v}
@|do-while| & @<body> @<cond> & do @<body> while (@<cond>);
\\ \hlx{v}
- @|function| & @<name> @<type> @<body> &
- \vtop{\hbox{\strut @<type>_0 @<name>(@<type>_1 @<arg>_1, $\ldots$,
+ @|function| &
+ \vtop{\hbox{\strut @<name> @<type> @<body>}
+ \hbox{\strut \quad @|\&optional @<banner>|}
+ \hbox{\strut \quad @|\&rest| @<banner-args>}} &
+ \vtop{\hbox{\strut @[/* @<banner> */@]}
+ \hbox{\strut @<type>_0 @<name>(@<type>_1 @<arg>_1, $\ldots$,
@<type>_n @<arg>_n @[, \dots@])}
\hbox{\strut \quad @<body>}} \\ \hlx*{vh}
\end{tabular}
\begin{describe}{gf}{emit-decls @<codegen> @<decls>}
\end{describe}
+\begin{describe}{fun}{emit-banner @<codegen> @<control> \&rest @<args>}
+\end{describe}
+
\begin{describe}{gf}{codegen-push @<codegen>}
\end{describe}
The concrete types described in \xref{sec:structures.common} and
\ref{sec:structures.root} are declared by the header file @|<sod/sod.h>|.
-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}
\begin{figure}[tbp]
\begin{tabular}{p{10pt}p{10pt}}
- \begin{prog}
+ \begin{nprog}
struct SodObject__ilayout \{ \\ \ind
union \{ \\ \ind
struct SodObject__ichain_obj \{ \\ \ind
\} 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}
\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}
const char *name; \\
const char *nick; \\
size_t initsz; \\
+ size_t align; \\
void *(*imprint)(void *@<p>); \\
- void *(*init)(void *@<p>); \\
size_t n_supers; \\
const SodClass *const *supers; \\
size_t n_cpl; \\
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
\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 @<p> to at least
@<initsz> 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 @<p>.
- \item[init] A pointer to a function: given a pointer @<p> to at least
- @<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 @<p>.
-
\item[n_supers] The number of direct superclasses. (This is zero exactly
in the case of @|SodObject|.)
union $B$__ichainu_$i$ $i$; \\
\quad$\vdots$ \- \\
\};
- \\[\bigskipamount]
+ \\+
typedef struct $C$__ichain_$h$ $C$;
\end{prog}
\quad$\vdots$ \- \\
\} $c$; \- \\
\};
- \\[\bigskipamount]
+ \\+
extern const union $C$__vtu_$h$ $C$__vtable_$h$;
\end{prog}
\begin{prog}
@<type>_0 $m$(@<type>_1 @<arg>_1, $\ldots$, @<type>_n @<arg>_n, \dots);
\end{prog}
+or a standard message which takes keyword arguments, defined as
+\begin{prog}
+ @<type>_0 $m$(\=@<type>_1 @<arg>_1, $\ldots$, @<type>_n @<arg>_n? \+ \\
+ @<type>_{n+1} @<kw>_{n+1} @[= @<dflt>_{n+1}@], $\ldots$,
+ @<type>_m @<kw>_m @[= @<dflt>_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}
@<type>_0 $m$($C$ *me, @<type>_1 @<arg>_1, $\ldots$,
@<type>_n @<arg>_n, \dots); \\
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(@<me>, $\ldots$, @<sod__ap>)
@<me>@->_vt@->$c$.$m$__v(@<me>, $\ldots$, @<sod__ap>)
\alt "bool" | "_Bool"
\alt "imaginary" | "_Imaginary" | "complex" | "_Complex"
\alt <qualifier>
+ \alt <storage-specifier>
+ \alt <atomic-type>
- <qualifier> ::= "const" | "volatile" | "restrict"
+ <qualifier> ::= <atomic> | "const" | "volatile" | "restrict"
+
+ <atomic-type> ::=
+ <atomic> "(" @<declaration-specifier>^+ <abstract-declarator> ")"
+
+ <atomic> ::= "atomic" | "_Atomic"
+
+ <storage-specifier> ::= <alignas> "(" <c-fragment> ")"
+
+ <alignas> ::= "alignas" "_Alignas"
<type-name> ::= <identifier>
\end{grammar}
Declaration specifiers may appear in any order. However, not all
combinations are permitted. A declaration specifier must consist of zero or
- more @<qualifier>s, and one of the following, up to reordering.
+ more @<qualifier>s, zero or more @<storage-specifier>s, and one of the
+ following, up to reordering.
\begin{itemize}
\item @<type-name>
+ \item @<atomic-type>
\item @"struct" @<identifier>, @"union" @<identifier>, @"enum" @<identifier>
\item @"void"
\item @"_Bool", @"bool"
\subsubsection{Declarators}
\begin{grammar}
-<declarator>$[k]$ ::= @<pointer>^* <primary-declarator>$[k]$
+<declarator>$[k, a]$ ::= @<pointer>^* <primary-declarator>$[k, a]$
-<primary-declarator>$[k]$ ::= $k$
-\alt "(" <primary-declarator>$[k]$ ")"
-\alt <primary-declarator>$[k]$ @<declarator-suffix>
+<primary-declarator>$[k, a]$ ::= $k$
+\alt "(" <primary-declarator>$[k, a]$ ")"
+\alt <primary-declarator>$[k, a]$ @<declarator-suffix>$[a]$
<pointer> ::= "*" @<qualifier>^*
-<declarator-suffix> ::= "[" <c-fragment> "]"
-\alt "(" <arguments> ")"
+<declarator-suffix>$[a]$ ::= "[" <c-fragment> "]"
+\alt "(" $a$ ")"
<argument-list> ::= $\epsilon$ | "..."
\alt <list>$[\mbox{@<argument>}]$ @["," "..."@]
<argument> ::= @<declaration-specifier>^+ <argument-declarator>
-<abstract-declarator> ::= <declarator>$[\epsilon]$
++<abstract-declarator> ::= <declarator>$[\epsilon, \mbox{@<argument-list>}]$
+
+ <argument-declarator> ::= <declarator>$[\mbox{@<identifier> @! $\epsilon$}]$
+<argument-declarator> ::=
+ <declarator>$[\mbox{@<identifier> @! $\epsilon$}, \mbox{@<argument-list>}]$
-<simple-declarator> ::= <declarator>$[\mbox{@<identifier>}]$
-
-<dotted-name> ::= <identifier> "." <identifier>
+<simple-declarator> ::=
+ <declarator>$[\mbox{@<identifier>}, \mbox{@<argument-list>}]$
\end{grammar}
The declarator syntax is taken from C, but with some differences.
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}
+<keyword-argument> ::= <argument> @["=" <c-fragment>@]
+
+<keyword-argument-list> ::=
+ @[<list>$[\mbox{@<argument>}]$@]
+ "?" @[<list>$[\mbox{@<keyword-argument>}]$@]
+
+<method-argument-list> ::= <argument-list> @! <keyword-argument-list>
+
+<dotted-name> ::= <identifier> "." <identifier>
+
+<keyword-declarator>$[k]$ ::=
+ <declarator>$[k, \mbox{@<method-argument-list>}]$
+\end{grammar}
+
\subsection{Class definitions} \label{sec:syntax.module.class}
<class-item> ::= <slot-item>
\alt <initializer-item>
+\alt <initarg-item>
+\alt <fragment-item>
\alt <message-item>
\alt <method-item>
\end{grammar}
\begin{grammar}
<initializer-item> ::= @["class"@] <list>$[\mbox{@<slot-initializer>}]$ ";"
-<slot-initializer> ::= <dotted-name> "=" <initializer>
+<slot-initializer> ::= <dotted-name> @["=" <initializer>@]
-<initializer> :: "{" <c-fragment> "}" | <c-fragment>
+<initializer> :: <c-fragment>
\end{grammar}
An @<initializer-item> provides an initial value for one or more slots. If
class's superclasses (including itself); the second must be the name of a
slot defined in that superclass.
-The initializer has one of two forms.
-\begin{itemize}
-\item A @<c-fragment> enclosed in braces denotes an aggregate initializer.
- This is suitable for initializing structure, union or array slots.
-\item A @<c-fragment> \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-item> ::=
+ "initarg"
+ @<declaration-specifier>^+
+ <list>$[\mbox{@<init-declarator>}]$ ";"
+\end{grammar}
+
+\subsubsection{Fragment items}
+\begin{grammar}
+<fragment-item> ::= <fragment-kind> "{" <c-fragment> "}"
+
+<fragment-kind> ::= "init" | "teardown"
+\end{grammar}
\subsubsection{Message items}
\begin{grammar}
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 {
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 {
\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;
.SS The SodObject class
The
.B SodObject
-class defines no slots or messages.
+class defines no slots.
Because
.B SodObject
has no direct superclasses,
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
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
.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
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
.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
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
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.
/*----- 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.
# 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
/* 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
#include <stdarg.h>
#include <stddef.h>
+#include "keyword.h"
#include "sod-base.h"
/*----- Data structures ---------------------------------------------------*/
#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 ------------------------------------------------*/
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
(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;
(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.
(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
+ "~@<struct { ~2I~_~{unsigned ~A : 1;~^ ~_~} ~I~_} ~A;~:>"
+ 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.
(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)
(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 "~:@<SPECS ~@_~:I~/sod:print-c-type/~
+ ~{ ~_~/sod:print-c-storage-specifier/~}~:>"
+ (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.
(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)))
(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)))
(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 "~:@<ATOMIC ~@_~/sod:print-c-type/~{ ~_~S~}~:>"
+ (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.
(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.
(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.
(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))
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.
(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.
(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
`(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)
(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)
(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 --------------------------------------------------
;; 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.
(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)))
(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 ()
(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) ())
((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)))
(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 "<declaration-specifier>")
- (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,
(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.
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)))))
;;; `parse-declarator' will be of this form.
(export 'parse-declarator)
-(defun parse-declarator (scanner base-type &key kernel abstractp)
+(defun parse-declarator (scanner base-type &key kernel abstractp keywordp)
"Parse a C declarator, returning a pair (C-TYPE . NAME).
The SCANNER is a token scanner to read from. The BASE-TYPE is the type
defaults to matching a simple identifier `:id'. This might, e.g., be
(? :id) to parse an `abstract declarator' which has optional names.
+ If KEYWORDP is true, then a keyword argument list is permitted in
+ function declarations.
+
There's an annoying ambiguity in the syntax, if an empty KERNEL is
permitted. In this case, you must ensure that ABSTRACTP is true so that
the appropriate heuristic can be applied. As a convenience, if ABSTRACTP
is true then `(? :id)' is used as the default KERNEL."
+
+ ;; This is a bit confusing. This is a strangely-shaped operator grammer,
+ ;; which wouldn't be so bad, but the `values' being operated on are pairs
+ ;; of the form (FUNC . NAME). The NAME is whatever the KERNEL parser
+ ;; produces as its result, and will be passed out unchanged. The FUNC is a
+ ;; type-constructor function which will be eventually be applied to the
+ ;; input BASE-TYPE, but we can't calculate the actual result as we go along
+ ;; because of the rather annoying inside-out nature of the declarator
+ ;; syntax.
+
(with-parser-context (token-scanner-context :scanner scanner)
(let ((kernel-parser (cond (kernel kernel)
(abstractp (parser () (? :id)))
(parse
(seq ((quals (list ()
- (scan-declspec
+ (scan-simple-declspec
scanner
:indicator :qualifier
:predicate (lambda (ds)
'qualifier)))))))
(mapcar #'ds-label quals))))
+ (disallow-keyword-functions (type)
+ (when (typep type 'c-keyword-function-type)
+ (error "Functions with keyword arguments are only ~
+ allowed at top-level.")))
+
(star ()
;; Prefix: `*' qualifiers
(parse (seq (#\* (quals (qualifiers)))
(preop "*" (state 9)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
(make-pointer-type type quals)))
(cdr state))))))
(parse (seq ((name (funcall kernel-parser)))
(cons #'identity name))))
+ (arg-decl (abstractp)
+ (parse (seq ((base-type (parse-c-type scanner))
+ (dtor (parse-declarator scanner base-type
+ :abstractp abstractp)))
+ dtor)))
+
+ (argument ()
+ ;; argument ::= type abstract-declspec
+
+ (parse (seq ((dtor (arg-decl t)))
+ (make-argument (cdr dtor) (car dtor)))))
+
+ (kw-argument ()
+ ;; kw-argument ::= type declspec [= c-fragment]
+
+ (parse (seq ((dtor (arg-decl nil))
+ (dflt (? (when (eq (token-type scanner) #\=)
+ (parse-delimited-fragment
+ scanner #\= '(#\, #\))
+ :keep-end t)))))
+ (make-argument (cdr dtor) (car dtor) dflt))))
+
(argument-list ()
- ;; [argument [`,' argument]* [`,' `...']] | `...'
+ ;; argument-list ::=
+ ;; [argument [`,' argument]* [`,' argument-tail]]
+ ;; | argument-tail
+ ;;
+ ;; argument-tail ::= `...' | keyword-tail
+ ;;
+ ;; keyword-tail ::= `?' [kw-argument [`,' kw-argument]*]
+ ;;
+ ;; kw-argument ::= argument [= c-fragment]
;;
;; The possibility of a trailing `,' `...' means that we
;; can't use the standard `list' parser. Note that, unlike
;; `real' C, we allow an ellipsis even if there are no
;; explicit arguments.
- (let ((args nil))
+ (let ((args nil)
+ (keys nil)
+ (keysp nil))
(loop
(when (eq (token-type scanner) :ellipsis)
(push :ellipsis args)
(scanner-step scanner)
(return))
+ (when (and keywordp (eq (token-type scanner) #\?))
+ (setf keysp t)
+ (scanner-step scanner)
+ (multiple-value-bind (arg winp consumedp)
+ (parse (list (:min 0) (kw-argument) #\,))
+ (declare (ignore consumedp))
+ (unless winp
+ (return-from argument-list (values arg nil t)))
+ (setf keys arg)
+ (return)))
(multiple-value-bind (arg winp consumedp)
- (parse (seq ((base-type (parse-c-type scanner))
- (dtor (parse-declarator scanner
- base-type
- :abstractp t)))
- (make-argument (cdr dtor) (car dtor))))
+ (argument)
(unless winp
(if (or consumedp args)
(return-from argument-list (values arg nil t))
(unless (eq (token-type scanner) #\,)
(return))
(scanner-step scanner))
- (values (nreverse args) t args)))
+ (values (let ((rargs (nreverse args))
+ (rkeys (nreverse keys)))
+ (if keysp
+ (lambda (ret)
+ (make-keyword-function-type
+ ret rargs rkeys))
+ (lambda (ret)
+ (make-function-type ret rargs))))
+ t
+ (or args keysp))))
(postfix-lparen ()
;; Postfix: `(' argument-list `)'
- (parse (seq (#\( (args (argument-list)) #\))
+ (parse (seq (#\( (make (argument-list)) #\))
(postop "()" (state 10)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
- (make-function-type type args)))
+ (funcall make type)))
(cdr state))))))
(dimension ()
(parse (seq ((dims (list (:min 1) (dimension))))
(postop "[]" (state 10)
(cons (lambda (type)
+ (disallow-keyword-functions type)
(funcall (car state)
(make-array-type type dims)))
(cdr state)))))))
(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 --------------------------------------------------
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
(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))))
(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)
"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))))
(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)
(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* `}'
;;
;; names.
(parse-declarator
scanner base-type
+ :keywordp t
:kernel (parser ()
(seq ((name-a :id)
(name-b (? (seq (#\. (id :id)) id))))
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 ::=
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
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
;; | 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
(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 (#\{
.|
.I initializer-item
.|
+.I initarg-item
+.|
+.I fragment-item
+.|
.I message-item
.|
.I method-item
.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
.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
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
.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
.IR declaration-specifier \*+
.I argument-declarator
.br
-.IR declarator [\*e]
+ .I abstract-declarator
+ ::=
++.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