CLEANFILES += *.aux *.out *.log *.toc *.ind *.idx *.ilg
EXTRA_DIST += $(TEX_FILES)
+TEXFLAGS = --interaction=nonstopmode \
+ --output-directory=$(abs_builddir)
+
###--------------------------------------------------------------------------
### The manual.
## Main document styling and definitions.
TEX_FILES += sod.sty
+## The introduction.
+TEX_FILES += intro.tex
+
## Tutorial.
TEX_FILES += tutorial.tex
EXTRA_DIST += sod.pdf
doc_DATA += sod.pdf
sod.pdf: $(TEX_FILES)
- cd $(srcdir) && pdflatex --output-directory=$(abs_builddir) sod.tex
- cd $(srcdir) && pdflatex --output-directory=$(abs_builddir) sod.tex
+ cd $(srcdir) && pdflatex $(TEXFLAGS) sod.tex
+ cd $(srcdir) && pdflatex $(TEXFLAGS) sod.tex
makeindex sod.idx
- cd $(srcdir) && pdflatex --output-directory=$(abs_builddir) sod.tex
+ cd $(srcdir) && pdflatex $(TEXFLAGS) sod.tex
###----- That's all, folks --------------------------------------------------
effective-method-keywords
effective-method
effective-method-live-p
+ aggregating-effective-method
sod::lifecycle-effective-method
simple-effective-method
effective-method-message
\chapter{Concepts} \label{ch:concepts}
%%%--------------------------------------------------------------------------
-\section{Operational model} \label{sec:concepts.model}
-
-The Sod translator runs as a preprocessor, similar in nature to the
-traditional Unix \man{lex}{1} and \man{yacc}{1} tools. The translator reads
-a \emph{module} file containing class definitions and other information, and
-writes C~source and header files. The source files contain function
-definitions and static tables which are fed directly to a C~compiler; the
-header files contain declarations for functions and data structures, and are
-included by source files -- whether hand-written or generated by Sod -- which
-makes use of the classes defined in the module.
-
-Sod is not like \Cplusplus: it makes no attempt to `enhance' the C language
-itself. Sod module files describe classes, messages, methods, slots, and
-other kinds of object-system things, and some of these descriptions need to
-contain C code fragments, but this code is entirely uninterpreted by the Sod
-translator.\footnote{%
- As long as a code fragment broadly follows C's lexical rules, and properly
- matches parentheses, brackets, and braces, the Sod translator will copy it
- into its output unchanged. It might, in fact, be some other kind of C-like
- language, such as Objective~C or \Cplusplus. Or maybe even
- Objective~\Cplusplus, because if having an object system is good, then
- having three must be really awesome.} %
-
-The Sod translator is not a closed system. It is written in Common Lisp, and
-can load extension modules which add new input syntax, output formats, or
-altered behaviour. The interface for writing such extensions is described in
-\xref{p:lisp}. Extensions can change almost all details of the Sod object
-system, so the material in this manual must be read with this in mind: this
-manual describes the base system as provided in the distribution.
-
-%%%--------------------------------------------------------------------------
\section{Modules} \label{sec:concepts.modules}
A \emph{module} is the top-level syntactic unit of input to the Sod
\subsubsection{Slot initializers}
As well as defining slot names and types, a class can also associate an
\emph{initial value} with each slot defined by itself or one of its
-subclasses. A class $C$ provides an \emph{initialization function} (see
+subclasses. A class $C$ provides an \emph{initialization message} (see
\xref{sec:concepts.lifecycle.birth}, and \xref{sec:structures.root.sodclass})
-which sets the slots of a \emph{direct} instance of the class to the correct
-initial values. If several of $C$'s superclasses define initializers for the
-same slot then the initializer from the most specific such class is used. If
-none of $C$'s superclasses define an initializer for some slot then that slot
-will be left uninitialized.
+whose methods set the slots of a \emph{direct} instance of the class to the
+correct initial values. If several of $C$'s superclasses define initializers
+for the same slot then the initializer from the most specific such class is
+used. If none of $C$'s superclasses define an initializer for some slot then
+that slot will be left uninitialized.
The initializer for a slot with scalar type may be any C expression. The
initializer for a slot with aggregate type must contain only constant
stash them in a dynamically allocated private structure, and leave a pointer
to it in a slot. (This will also help preserve binary compatibility, because
the private structure can grow more members as needed. See
-\xref{sec:fixme.compatibility} for more details.
+\xref{sec:fixme.compatibility} for more details.)
+
+\subsubsection{Vtables}
+
\subsubsection{Class objects}
In Sod's object system, classes are objects too. Therefore classes are
slot containing a function pointer is not at all the same thing as a method.)
\subsubsection{Conversions}
-Suppose one has a value of type pointer to class type of some class~$C$, and
-wants to convert it to a pointer to class type of some other class~$B$.
+Suppose one has a value of type pointer-to-class-type for some class~$C$, and
+wants to convert it to a pointer-to-class-type for some other class~$B$.
There are three main cases to distinguish.
\begin{itemize}
\item If $B$ is a superclass of~$C$, in the same chain, then the conversion
pointer. The conversion can be performed using the appropriate generated
upcast macro (see below); the general case is handled by the macro
\descref{SOD_XCHAIN}{mac}.
-\item If $B$ is a subclass of~$C$ then the conversion is an \emph{upcast};
+\item If $B$ is a subclass of~$C$ then the conversion is a \emph{downcast};
otherwise the conversion is a~\emph{cross-cast}. In either case, the
conversion can fail: the object in question might not be an instance of~$B$
- at all. The macro \descref{SOD_CONVERT}{mac} and the function
+ after all. The macro \descref{SOD_CONVERT}{mac} and the function
\descref{sod_convert}{fun} perform general conversions. They return a null
pointer if the conversion fails. (There are therefore your analogue to the
- \Cplusplus @|dynamic_cast<>| operator.)
+ \Cplusplus\ @|dynamic_cast<>| operator.)
\end{itemize}
The Sod translator generates macros for performing both in-chain and
cross-chain upcasts. For each class~$C$, and each proper superclass~$B$
constructed: the vtables contain null pointers in place of pointers to method
entry functions.
+\begin{figure}
+ \begin{tikzpicture}
+ [>=stealth, thick,
+ order/.append style={color=green!70!black},
+ code/.append style={font=\sffamily},
+ action/.append style={font=\itshape},
+ method/.append style={rectangle, draw=black, thin, fill=blue!30,
+ text height=\ht\strutbox, text depth=\dp\strutbox,
+ minimum width=40mm}]
+
+ \def\delgstack#1#2#3{
+ \node (#10) [method, #2] {#3};
+ \node (#11) [method, above=6mm of #10] {#3};
+ \draw [->] ($(#10.north)!.5!(#10.north west) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method};
+ \draw [<-] ($(#10.north)!.5!(#10.north east) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return};
+ \draw [->] ($(#11.north)!.5!(#11.north west) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method}
+ node (ld) [above] {$\smash\vdots\mathstrut$};
+ \draw [<-] ($(#11.north)!.5!(#11.north east) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return}
+ node (rd) [above] {$\smash\vdots\mathstrut$};
+ \draw [->] ($(ld.north) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method};
+ \draw [<-] ($(rd.north) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return};
+ \node (p) at ($(ld.north)!.5!(rd.north)$) {};
+ \node (#1n) [method, above=5mm of p] {#3};
+ \draw [->, order] ($(#10.south east) + (4mm, 1mm)$) --
+ ($(#1n.north east) + (4mm, -1mm)$)
+ node [midway, right, align=left]
+ {Most to \\ least \\ specific};}
+
+ \delgstack{a}{}{Around method}
+ \draw [<-] ($(a0.south)!.5!(a0.south west) - (0mm, 1mm)$) --
+ ++(0mm, -4mm);
+ \draw [->] ($(a0.south)!.5!(a0.south east) - (0mm, 1mm)$) --
+ ++(0mm, -4mm)
+ node [action, right=4pt, midway] {return};
+
+ \draw [->] ($(an.north)!.6!(an.north west) + (0mm, 1mm)$) --
+ ++(-8mm, 8mm)
+ node [code, midway, left=3mm] {next_method}
+ node (b0) [method, above left = 1mm + 4mm and -6mm - 4mm] {};
+ \node (b1) [method] at ($(b0) - (2mm, 2mm)$) {};
+ \node (bn) [method] at ($(b1) - (2mm, 2mm)$) {Before method};
+ \draw [->, order] ($(bn.west) - (6mm, 0mm)$) -- ++(12mm, 12mm)
+ node [midway, above left, align=center] {Most to \\ least \\ specific};
+ \draw [->] ($(b0.north east) + (-10mm, 1mm)$) -- ++(8mm, 8mm)
+ node (p) {};
+
+ \delgstack{m}{above right=1mm and 0mm of an.west |- p}{Primary method}
+ \draw [->] ($(mn.north)!.5!(mn.north west) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method}
+ node [above right = 0mm and -8mm]
+ {$\vcenter{\hbox{\Huge\textcolor{red}{!}}}
+ \vcenter{\hbox{\begin{tabular}[c]{l}
+ \textsf{next_method} \\
+ pointer is null
+ \end{tabular}}}$};
+
+ \draw [->, color=blue, dotted]
+ ($(m0.south)!.2!(m0.south east) - (0mm, 1mm)$) --
+ ($(an.north)!.2!(an.north east) + (0mm, 1mm)$)
+ node [midway, sloped, below] {Return value};
+
+ \draw [<-] ($(an.north)!.6!(an.north east) + (0mm, 1mm)$) --
+ ++(8mm, 8mm)
+ node [action, midway, right=3mm] {return}
+ node (f0) [method, above right = 1mm and -6mm] {};
+ \node (f1) [method] at ($(f0) + (-2mm, 2mm)$) {};
+ \node (fn) [method] at ($(f1) + (-2mm, 2mm)$) {After method};
+ \draw [<-, order] ($(f0.east) + (6mm, 0mm)$) -- ++(-12mm, 12mm)
+ node [midway, above right, align=center]
+ {Least to \\ most \\ specific};
+ \draw [<-] ($(fn.north west) + (6mm, 1mm)$) -- ++(-8mm, 8mm);
+
+ \end{tikzpicture}
+
+ \caption{The standard method combination}
+ \label{fig:concepts.methods.stdmeth}
+\end{figure}
+
The effective method for a message with standard method combination works as
-follows.
+follows (see also~\xref{fig:concepts.methods.stdmeth}).
\begin{enumerate}
\item If any applicable methods have the @|around| role, then the most
in \xref{sec:fixme.custom-aggregating-method-combination}.
+\subsection{Sending messages in C} \label{sec:concepts.methods.c}
+
+Each instance is associated with its direct class [FIXME]
+
+The effective methods for each class are determined at translation time, by
+the Sod translator. For each effective method, one or more \emph{method
+entry functions} are constructed. A method entry function has three
+responsibilities.
+\begin{itemize}
+\item It converts the receiver pointer to the correct type. Method entry
+ functions can perform these conversions extremely efficiently: there are
+ separate method entries for each chain of each class which can receive a
+ message, so method entry functions are in the privileged situation of
+ knowing the \emph{exact} class of the receiving object.
+\item If the message accepts a variable-length argument tail, then two method
+ entry functions are created for each chain of each class: one receives a
+ variable-length argument tail, as intended, and captures it in a @|va_list|
+ object; the other accepts an argument of type @|va_list| in place of the
+ variable-length tail and arranges for it to be passed along to the direct
+ methods.
+\item It invokes the effective method with the appropriate arguments. There
+ might or might not be an actual function corresponding to the effective
+ method itself: the translator may instead open-code the effective method's
+ behaviour into each method entry function; and the machinery for handling
+ `delegation chains', such as is used for @|around| methods and primary
+ methods in the standard method combination, is necessarily scattered among
+ a number of small functions.
+\end{itemize}
+
+
\subsection{Messages with keyword arguments}
\label{sec:concepts.methods.keywords}
Details of initialization are necessarily class-specific, but typically it
involves setting the instance's slots to appropriate values, and possibly
-linking it into some larger data structure to keep track of it.
+linking it into some larger data structure to keep track of it. It is
+possible for initialization methods to attempt to allocate resources, but
+this must be done carefully: there is currently no way to report an error
+from object initialization, so the object must be marked as incompletely
+initialized, and left in a state where it will be safe to tear down later.
Initialization is performed by sending the imprinted instance an @|init|
message, defined by the @|SodObject| class. This message uses a nonstandard
fragments are executed with @|me| bound to an instance pointer of the
appropriate superclass type, immediately after that superclass's slots (if
any) have been initialized; therefore, fragments defined by a more specific
-superclass are executed after fragments defined by a more specific
+superclass are executed after fragments defined by a less specific
superclass. A class may define more than one initialization fragment: the
fragments are executed in the order in which they appear in the class
definition. It is possible for an initialization fragment to use @|return|
\descref{sod_destroy}[function]{fun}.
\subsubsection{Teardown}
-Details of initialization are necessarily class-specific, but typically it
-involves setting the instance's slots to appropriate values, and possibly
-linking it into some larger data structure to keep track of it.
+Details of teardown are necessarily class-specific, but typically it
+involves releasing resources held by the instance, and disentangling it from
+any data structures it might be linked into.
Teardown is performed by sending the instance the @|teardown| message,
defined by the @|SodObject| class. The message returns an integer, used as a
counting system, as follows.
\begin{prog}
[nick = ref] \\
- class ReferenceCountedObject \{ \\ \ind
+ class ReferenceCountedObject: SodObject \{ \\ \ind
unsigned nref = 1; \\-
void inc() \{ me@->ref.nref++; \} \\-
[role = around] \\
\}
\end{prog}
-This message uses a nonstandard method combination which works like the
-standard combination, except that the \emph{default behaviour}, if there is
-no overriding method, is to execute the superclass's teardown fragments, and
-to return zero. This default behaviour may be invoked multiple times if some
-method calls on its @|next_method| more than once, unless some other method
-takes steps to prevent this.
+The @|teardown| message uses a nonstandard method combination which works
+like the standard combination, except that the \emph{default behaviour}, if
+there is no overriding method, is to execute the superclass's teardown
+fragments, and to return zero. This default behaviour may be invoked
+multiple times if some method calls on its @|next_method| more than once,
+unless some other method takes steps to prevent this.
A class can define \emph{teardown fragments}: pieces of literal code to be
executed to shut down an instance. Each superclass's teardown fragments are
executed with @|me| bound to an instance pointer of the appropriate
superclass type; fragments defined by a more specific superclass are executed
-before fragments defined by a more specific superclass. A class may define
+before fragments defined by a less specific superclass. A class may define
more than one teardown fragment: the fragments are executed in the order in
which they appear in the class definition. It is possible for an
initialization fragment to use @|return| or @|goto| for special control-flow
%%%--------------------------------------------------------------------------
\section{Metaclasses} \label{sec:concepts.metaclasses}
+%%%--------------------------------------------------------------------------
+\section{Compatibility considerations} \label{sec:concepts.compatibility}
+
+Sod doesn't make source-level compatibility especially difficult. As long as
+classes, slots, and messages don't change names or dissappear, and slots and
+messages retain their approximate types, everything will be fine.
+
+Binary compatibility is much more difficult. Unfortunately, Sod classes have
+rather fragile binary interfaces.\footnote{%
+ Research suggestion: investigate alternative instance and vtable layouts
+ which improve binary compatibility, probably at the expense of instance
+ compactness, and efficiency of slot access and message sending. There may
+ be interesting trade-offs to be made.} %
+
+If instances are allocated [FIXME]
+
%%%----- That's all, folks --------------------------------------------------
%%% Local variables:
--- /dev/null
+%%% -*-latex-*-
+%%%
+%%% Introduction to Sod and its object system
+%%%
+%%% (c) 2015 Straylight/Edgeware
+%%%
+
+%%%----- Licensing notice ---------------------------------------------------
+%%%
+%%% This file is part of the Sensible Object Design, an object system for C.
+%%%
+%%% SOD is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation; either version 2 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% SOD is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with SOD; if not, write to the Free Software Foundation,
+%%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+\chapter{Introduction} \label{ch:intro}
+
+Sod is an object system for the C programming language.
+
+The software distribution for Sod contains two main parts:
+\begin{itemize}
+\item a translator, or preprocessor, similar in spirit to \man{lex}{1} or
+ \man{yacc}{1}, which reads input files containing class definitions, and
+ writes C source code and header files; and
+\item a very small runtime library, containing the built-in base classes and
+ some simple utility macros and functions for working with instances and
+ classes.
+\end{itemize}
+
+%%%--------------------------------------------------------------------------
+\section{About Sod's object system} \label{ch:intro.about}
+
+Sod implements a fairly sophisticated object system, with multiple
+inheritance, but only single dispatch.
+
+\subsection{Ideology}
+
+Object systems tend to come with ideology attached, so Sod is no exception;
+but Sod's ideology is different from that of most object systems.
+\begin{itemize}
+\item Sod provides an object system, not a module system. Sod provides no
+ facilities for `information hiding'; there is no equivalent to the
+ @|private| or @|protected| annotations in Java or \Cplusplus. The author
+ takes the view (a) that such facilities are propertly part of a module
+ system, and that trying to abuse classes so that they become modules is a
+ mistake; and (b) that much useful functionality is unnecessarily hidden
+ away behind abstract interfaces, and a gentle-ish nudge towards greater
+ openness is called for.
+\item Sod's objective is to provide an effective tool for the expert
+ programmer, in the classic `make easy things simple and, difficult things
+ possible' mould. It isn't intended to be useful in an environment
+ containing unassisted novice programmers. Sod tries to avoid placing
+ technically unnecessary restrictions on programmers, and is likely to
+ evolve in the direction of eliminating existing restrictions rather than
+ growing new `safety' features.
+\end{itemize}
+
+\subsection{Comparison with other object systems}
+
+Sod's object system is significantly different in flavour\footnote{%
+ The pun was unintentional, but I'm happy with it.} %
+from most popular object-ish languages. Indeed, it bears far more similarity
+to Flavors, CLOS, and Dylan than to \Cplusplus, \Csharp, or Java (and still
+less to Go or Rust).
+
+Of the popular languages, \Cplusplus's object system is probably closest to
+Sod. Both are statically compiled, statically typed, implement single
+dispatch, and have relatively simple runtime metaprogramming facilities.
+\Cplusplus\ has a rich compile-time template metalanguage; Sod instead allows
+compile-time metaprogramming in Common Lisp, which is probably less
+convenient for simple cases but rather more pleasant for doing difficult
+things. Significantly, Sod has the @|SodObject| class, of which all other
+classes\footnote{%
+ Unless you construct a new root class of your own, which you can totally
+ do, but it's hard work.} %
+are subclasses; \Cplusplus\ has no such root class.
+
+Both systems provide multiple inheritance, but go about it very differently.
+The most important difference is that \Cplusplus\ provides only \emph{static
+ delegation}: if you have a class @|B| which defines some (virtual) member
+function @|f|, and a derived class of @|D| which wants to \emph{extend} the
+behaviour of @|f| on instances of @|D|, then you must explicitly call @|B::f|
+at the appropriate point:
+\begin{prog}
+ \#include <iostream> \\+
+ %
+ class B \{ \\
+ public: \\ \ind
+ virtual void f() \{ std::cout <{}< "B@\\n"; \} \\
+ virtual @~B() \{ \} \-\\
+ \}; \\+
+ %
+ class D: public B \{ \\
+ public: \\ \ind
+ void f() \{ B::f(); std::cout <{}< "D too@\\n"; \} \-\\
+ \};
+\end{prog}
+
+This works adequately when only single inheritance is involved. But if we
+now introduce multiple inheritance, we see the problem.
+\begin{prog}
+ \#include <iostream> \\+
+ %
+ class B \{ \\
+ public: \\ \ind
+ virtual void f() \{ std::cout <{}< "B@\\n"; \} \\
+ virtual @~B() \{ \} \-\\
+ \}; \\+
+ %
+ class X: virtual public B \{ \\
+ public: \\ \ind
+ void f() \{ B::f(); std::cout <{}< "X after@\\n"; \} \-\\
+ \}; \\+
+ %
+ class Y: virtual public B \{ \\
+ public: \\ \ind
+ void f() \{ std::cout <{}< "Y before@\\n"; B::f(); \} \-\\
+ \}; \\+
+ %
+ class D: public X, public Y \{ \\
+ public: \\ \ind
+ void f() \{ X::f(); Y::f(); \} // \comment{oh, dear} \-\\
+ \};
+\end{prog}
+The above prints
+\begin{prog}
+ B \\
+ X after \\
+ Y before \\
+ B
+\end{prog}
+which is unlikely to be what was wanted: `B' prints twice, and the `before'
+and `after' actions are both in the middle.\footnote{%
+ Of course, one could have arranged to call @|Y::f| before @|X::f| -- but
+ the important point is that one would have needed to \emph{know} that this
+ was necessary.} %
+The problem is that correctly composing behaviour from a collection of
+superclasses requires knowledge of all of the superclasses involved and how
+they're supposed to work together.
+
+The obvious workaround is to separate the functionality -- here, printing the
+messages -- from the plumbing, which arranges to do everything in the right
+order. You'd end up with a @|_f| member function in each class which wanted
+print something, and then every class would have a virtual @|f| which calls
+the various @|_f| functions in the right order, like this:
+\begin{prog}
+ \#include <iostream> \\+
+ %
+ class B \{ \\
+ protected: \\ \ind
+ void _f() \{ std::cout <{}< "B@\\n"; \} \-\\
+ public: \\ \ind
+ virtual void f() \{ _f(); \} \\
+ virtual ~B() \{ \} \-\\
+ \}; \\+
+ %
+ class X: virtual public B \{ \\
+ protected: \\ \ind
+ void _f() \{ std::cout <{}< "X after@\\n"; \} \-\\
+ public: \\ \ind
+ void f() \{ B::_f(); _f(); \} \-\\
+ \}; \\+
+ %
+ class Y: virtual public B \{ \\
+ protected: \\ \ind
+ void _f() \{ std::cout <{}< "Y before@\\n"; \} \-\\
+ public: \\ \ind
+ void f() \{ _f(); B::_f(); \} \-\\
+ \}; \\+
+ %
+ class D: public X, public Y \{ \\
+ public: \\ \ind
+ void f() \{ Y::_f(); B::_f(); X::_f(); \} \-\\
+ \};
+\end{prog}
+This is clearly much more cumbersome. Most disastrously, it spreads
+knowledge about how the various classes' contributions to the behaviour of
+@|f| fit together throughout the class graph. Also, even this approach is
+only suitable for especially simple cases. Suppose @|Y| needed to add
+behaviour before \emph{and} after @|B| -- maybe @|Y| is taking out a lock,
+and then releasing it. Then this approach won't work any more; indeed, it's
+hard to see any way to make this work without spreading knowledge about
+@|Y|'s lock everywhere.
+
+Compare Sod's approach.
+\begin{prog}
+ code c: includes \{ \\
+ \#include <stdio.h> \\-
+ \#include <sod.h> \\
+ \#include "foo.h" \\
+ \} \\+
+ %
+ class B: SodObject \{ \\ \ind
+ void f() \{ puts("B"); \} \-\\
+ \} \\+
+ %
+ class X: B \{ \\ \ind
+ void b.f() \{ CALL_NEXT_METHOD; puts("X after"); \} \-\\
+ \} \\
+ %
+ class Y: B \{ \\ \ind
+ void b.f() \{ puts("Y before"); CALL_NEXT_METHOD; \} \-\\
+ \} \\+
+ %
+ class D: X, Y \{ \}
+\end{prog}
+This prints
+\begin{prog}
+ Y before \\
+ B \\
+ X after
+\end{prog}
+as (I think) you'd hope. @|CALL_NEXT_METHOD| here does the job of figuring
+out what to do next, according to some rather complicated rules
+(described in full in \xref{sec:concepts.methods.combination}).
+
+Indeed, there's an even better way to write this particular case with Sod,
+because Sod has dedicated \emph{method rôles}.
+\begin{prog}
+ code c: includes \{ \\
+ \#include <stdio.h> \\-
+ \#include <sod.h> \\
+ \#include "foo.h" \\
+ \} \\+
+ %
+ class B: SodObject \{ \\ \ind
+ void f() \{ puts("B"); \} \-\\
+ \} \\+
+ %
+ class X: B \{ \\ \ind
+ [role = after] void b.f() \{ puts("X after"); \} \-\\
+ \} \\
+ %
+ class Y: B \{ \\ \ind
+ [role = before] void b.f() \{ puts("Y before"); \} \-\\
+ \} \\+
+ %
+ class D: X, Y \{ \}
+\end{prog}
+
+%%%--------------------------------------------------------------------------
+\section{About this manual}
+
+This manual intends to provide complete documentation about Sod.
+
+%%%----- That's all, folks --------------------------------------------------
+
+%%% Local variables:
+%%% mode: LaTeX
+%%% TeX-master: "sod.tex"
+%%% TeX-PDF-mode: t
+%%% End:
--- /dev/null
+%%% -*-latex-*-
+%%%
+%%% Introduction to the reference section
+%%%
+%%% (c) 2016 Straylight/Edgeware
+%%%
+
+%%%----- Licensing notice ---------------------------------------------------
+%%%
+%%% This file is part of the Sensible Object Design, an object system for C.
+%%%
+%%% SOD is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation; either version 2 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% SOD is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with SOD; if not, write to the Free Software Foundation,
+%%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+\chapter{Introduction} \label{ch:refintro}
+
+This part describes Sod in detail, from the perspective of a developer using
+the system to write their own programs. This task is complicated by the fact
+that Sod is intrinsically open-ended and flexible: it allows programmers to
+extend and alter its behaviour arbitrarily. As a result, pretty much every
+statement made in this part must be hedged around with disclaimers that it
+might not be true if Sod has been extended in a particular way. Rather than
+mention this repeatedly, this part will describe Sod as it is provided,
+rather than as it might be when modified.
+
+\Xref{p:lisp} describes Sod's internal API and protocols. A reader
+interested in understanding the design space of object systems covered by Sod
+will have to read that in order to grasp fully what the system is (and isn't)
+capable of.
+
+The following chapters in this part will describe the Sod object model, the
+syntax read by the translator, and how the two
+
+\begin{itemize}
+
+\item The remainder of this chapter describes some notational conventions
+ which will be used throughout the present part, and describes various
+ aspects of the \emph{module files} which the Sod translator reads.
+
+\item \Xref{ch:class} describes \emph{classes}, which are the primary
+ concepts of interest in Sod.
+
+\end{itemize}
+
+%%%--------------------------------------------------------------------------
+\section{Operational model} \label{sec:refintro.model}
+
+The Sod translator runs as a preprocessor, similar in nature to the
+traditional Unix \man{lex}{1} and \man{yacc}{1} tools. The translator reads
+a \emph{module} file containing class definitions and other information, and
+writes C~source and header files. The source files contain function
+definitions and static tables which are fed directly to a C~compiler; the
+header files contain declarations for functions and data structures, and are
+included by source files -- whether hand-written or generated by Sod -- which
+makes use of the classes defined in the module.
+
+Sod is not like \Cplusplus: it makes no attempt to `enhance' the C language
+itself. Sod module files describe classes, messages, methods, slots, and
+other kinds of object-system things, and some of these descriptions need to
+contain C code fragments, but this code is entirely uninterpreted by the Sod
+translator.\footnote{%
+ As long as a code fragment broadly follows C's lexical rules, and properly
+ matches parentheses, brackets, and braces, the Sod translator will copy it
+ into its output unchanged. It might, in fact, be some other kind of C-like
+ language, such as Objective~C or \Cplusplus. Or maybe even
+ Objective~\Cplusplus, because if having an object system is good, then
+ having three must be really awesome.} %
+
+The Sod translator is not a closed system. It is written in Common Lisp, and
+can load extension modules which add new input syntax, output formats, or
+altered behaviour. The interface for writing such extensions is described in
+\xref{p:lisp}. Extensions can change almost all details of the Sod object
+system, so the material in this manual must be read with this in mind: this
+manual describes the base system as provided in the distribution.
+
+%%%--------------------------------------------------------------------------
+\section{Syntax notation} \label{sec:refintro.synnote}
+
+Fortunately, Sod is syntactically quite simple. The notation is slightly
+unusual in order to make the presentation shorter and easier to read.
+
+Anywhere a simple nonterminal name $x$ may appear in the grammar, an
+\emph{indexed} nonterminal $x[a_1, \ldots, a_n]$ may also appear. On the
+left-hand side of a production rule, the indices $a_1$, \ldots, $a_n$ are
+variables which vary over all nonterminal and terminal symbols, and the
+variables may also appear on the right-hand side in place of a nonterminal.
+Such a rule stands for a family of rules, in which each variable is replaced
+by each possible simple nonterminal or terminal symbol.
+
+The letter $\epsilon$ denotes the empty nonterminal
+\begin{quote}
+ \syntax{$\epsilon$ ::=}
+\end{quote}
+
+The following indexed productions are used throughout the grammar, some often
+enough that they deserve special notation.
+\begin{itemize}
+\item @[$x$@] abbreviates @<optional>$[x]$, denoting an optional occurrence
+ of $x$:
+ \begin{quote}
+ \syntax{@[$x$@] ::= <optional>$[x]$ ::= $\epsilon$ @! $x$}
+ \end{quote}
+\item $x^*$ abbreviates @<zero-or-more>$[x]$, denoting a sequence of zero or
+ more occurrences of $x$:
+ \begin{quote}
+ \syntax{$x^*$ ::= <zero-or-more>$[x]$ ::=
+ $\epsilon$ @! <zero-or-more>$[x]$ $x$}
+ \end{quote}
+\item $x^+$ abbreviates @<one-or-more>$[x]$, denoting a sequence of one or
+ more occurrences of $x$:
+ \begin{quote}
+ \syntax{$x^+$ ::= <one-or-more>$[x]$ ::= <zero-or-more>$[x]$ $x$}
+ \end{quote}
+\item @<list>$[x]$ denotes a sequence of one or more occurrences of $x$
+ separated by commas:
+ \begin{quote}
+ \syntax{<list>$[x]$ ::= $x$ @! <list>$[x]$ "," $x$}
+ \end{quote}
+\end{itemize}
+
+%%%--------------------------------------------------------------------------
+
+
+%% properties
+
+%%%----- That's all, folks --------------------------------------------------
instance.
\begin{describe}[SOD_XCHAIN]{mac}
- {void *SOD_CHAIN(@<chead>, const @<cls> *@<obj>);}
+ {void *SOD_XCHAIN(@<chead>, const @<cls> *@<obj>);}
Performs a `cross-chain upcast'.
Given a pointer @<obj> to an instance of a class of type @<cls> and the
\def\ind{\quad\=\+\kill}
\def\@progcr{\futurelet\@tempa\@progcr@i}
{\def\:{\gdef\@progcr@sp}\: {\@progcr}}
+\atdef~{\textasciitilde}
\def\@progcr@i{%
\ifx\@tempa\@sptoken\let\next@\@progcr@sp\else
\if1\ifx\@tempa[1\else
totoc=true, font=small]
{idxlayout}
\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{positioning}
\usepackage{syntax}
\usepackage{sverb}
\usepackage{mdwtab}
\mainmatter
+\include{intro}
+
%%%--------------------------------------------------------------------------
\part{Tutorial} \label{p:tut}
%%%--------------------------------------------------------------------------
\part{Reference} \label{p:ref}
+\include{refintro}
\include{concepts}
\include{cmdline}
\include{syntax}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="389.75183"
- height="344.37515"
- id="svg2"
- sodipodi:version="0.32"
- inkscape:version="0.46"
- sodipodi:docname="standard-method-combination.svg"
- inkscape:output_extension="org.inkscape.output.svg.inkscape"
- version="1.0">
- <defs
- id="defs4">
- <marker
- inkscape:stockid="Arrow1Mend"
- orient="auto"
- refY="0.0"
- refX="0.0"
- id="Arrow1Mend"
- style="overflow:visible;">
- <path
- id="path3268"
- d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
- style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
- transform="scale(0.4) rotate(180) translate(10,0)" />
- </marker>
- <inkscape:perspective
- sodipodi:type="inkscape:persp3d"
- inkscape:vp_x="0 : 526.18109 : 1"
- inkscape:vp_y="0 : 1000 : 0"
- inkscape:vp_z="744.09448 : 526.18109 : 1"
- inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
- id="perspective108" />
- <marker
- inkscape:stockid="Arrow2Mend"
- orient="auto"
- refY="0"
- refX="0"
- id="Arrow2Mend"
- style="overflow:visible">
- <path
- id="path3204"
- style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
- d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z"
- transform="scale(-0.6,-0.6)" />
- </marker>
- <radialGradient
- xlink:href="#linearGradient3074"
- r="21.214399"
- inkscape:collect="always"
- id="radialGradient3078"
- fy="158.17307"
- fx="61.08794"
- cy="158.17307"
- cx="61.08794"
- gradientTransform="scale(1.2180558,0.8209804)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient3074">
- <stop
- style="stop-color:#ffffff;stop-opacity:0.53370786;"
- offset="0.0000000"
- id="stop3075" />
- <stop
- style="stop-color:#000000;stop-opacity:0.69101125;"
- offset="1.0000000"
- id="stop3076" />
- </linearGradient>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- gridtolerance="10000"
- guidetolerance="10"
- objecttolerance="10"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="2.268243"
- inkscape:cx="194.87592"
- inkscape:cy="172.21547"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="1337"
- inkscape:window-height="998"
- inkscape:window-x="225"
- inkscape:window-y="17"
- showborder="false" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <cc:license
- rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
- <dc:title>SOD standard method combination diagram</dc:title>
- <dc:date>2009-09-18</dc:date>
- <dc:creator>
- <cc:Agent>
- <dc:title>Mark Wooding</dc:title>
- </cc:Agent>
- </dc:creator>
- <dc:description>A diagram showing how the applicable methods are invoked by standard method combination in the SOD object system.</dc:description>
- <dc:rights>
- <cc:Agent>
- <dc:title>Straylight/Edgeware</dc:title>
- </cc:Agent>
- </dc:rights>
- <dc:publisher>
- <cc:Agent>
- <dc:title>Straylight/Edgeware</dc:title>
- </cc:Agent>
- </dc:publisher>
- <dc:language>en-GB</dc:language>
- </cc:Work>
- <cc:License
- rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#Reproduction" />
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#Distribution" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#Notice" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#Attribution" />
- <cc:permits
- rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
- <cc:requires
- rdf:resource="http://creativecommons.org/ns#ShareAlike" />
- </cc:License>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(-76.574432,-549.18106)">
- <g
- id="g3106">
- <g
- transform="translate(-6.4194226,7.8915857)"
- id="g2941">
- <use
- transform="translate(-87.171262,-145.57702)"
- x="0"
- y="0"
- xlink:href="#rect2383"
- id="use8070"
- width="744.09448"
- height="1052.3622" />
- <use
- height="1052.3622"
- width="744.09448"
- id="use8068"
- xlink:href="#rect2383"
- y="0"
- x="0"
- transform="translate(-91.171262,-141.57702)" />
- <use
- transform="translate(-95.171262,-137.57702)"
- x="0"
- y="0"
- xlink:href="#rect2383"
- id="use8057"
- width="744.09448"
- height="1052.3622" />
- </g>
- <text
- id="text7219"
- y="743.74951"
- x="137.82718"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- id="tspan7221"
- y="743.74951"
- x="137.82718"
- sodipodi:role="line">Before method</tspan></text>
- <path
- sodipodi:nodetypes="cc"
- id="path7232"
- d="M 96.33489,740.26133 L 127.09404,709.94688"
- style="fill:none;fill-rule:evenodd;stroke:#00c800;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <flowRoot
- transform="translate(-301.49963,-67.218524)"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#00c800;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- id="flowRoot7234"
- xml:space="preserve"><flowRegion
- id="flowRegion7236"><rect
- style="text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#00c800;fill-opacity:1"
- y="756.86218"
- x="377"
- height="38.5"
- width="35.5"
- id="rect7238" /></flowRegion><flowPara
- id="flowPara7240">Most to least specific</flowPara></flowRoot> <path
- sodipodi:nodetypes="cc"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 233.72077,768.23762 L 215.68955,750.20639"
- id="path7292" />
- <text
- xml:space="preserve"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- x="149.36943"
- y="762.65088"
- id="text7294"><tspan
- sodipodi:role="line"
- id="tspan7296"
- x="149.36943"
- y="762.65088"
- style="font-style:italic">call-next-method</tspan></text>
- <path
- id="path7370"
- d="M 215.3571,720.94472 L 233.38832,702.91349"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- sodipodi:nodetypes="cc" />
- <use
- height="1052.3622"
- width="744.09448"
- transform="matrix(1,0,0,-1,193.60423,1471.9955)"
- id="use2946"
- xlink:href="#g2941"
- y="0"
- x="0" />
- <text
- xml:space="preserve"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- x="334.1524"
- y="735.396"
- id="text7428"><tspan
- sodipodi:role="line"
- x="334.1524"
- y="735.396"
- id="tspan7430">After method</tspan></text>
- <path
- style="fill:none;fill-rule:evenodd;stroke:#00c800;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 414.991,709.26598 L 445.75015,739.58043"
- id="path7432"
- sodipodi:nodetypes="cc" />
- <flowRoot
- xml:space="preserve"
- id="flowRoot7434"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#00c800;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- transform="translate(54.25206,-67.572077)"><flowRegion
- id="flowRegion7436"><rect
- id="rect7438"
- width="35.5"
- height="38.5"
- x="377"
- y="756.86218"
- style="text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#00c800;fill-opacity:1" /></flowRegion><flowPara
- id="flowPara7440">Least to most specific</flowPara></flowRoot> <path
- id="path7442"
- d="M 309.99443,702.58108 L 328.02565,720.61231"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- sodipodi:nodetypes="cc" />
- <text
- id="text7444"
- y="715.43781"
- x="326.31656"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- style="font-style:italic"
- y="715.43781"
- x="326.31656"
- id="tspan7446"
- sodipodi:role="line">return</tspan></text>
- <path
- sodipodi:nodetypes="cc"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 328.3581,749.87398 L 310.32688,767.90521"
- id="path7448" />
- <path
- id="path7460"
- d="M 286.62089,703.05143 L 286.62089,767.7517"
- style="fill:none;fill-rule:evenodd;stroke:#0000c8;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:0.75000000000000000, 0.75000000000000000;stroke-dashoffset:0;stroke-opacity:1" />
- <text
- transform="matrix(0,1,-1,0,0,0)"
- id="text7983"
- y="-289.09686"
- x="705.11279"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#0000c8;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- y="-289.09686"
- x="705.11279"
- id="tspan7985"
- sodipodi:role="line">Return value</tspan></text>
- </g>
- <g
- id="g3071">
- <g
- id="g2966">
- <rect
- y="861.61884"
- x="211.62221"
- height="16.482248"
- width="120.76241"
- id="rect2383"
- style="font-size:10px;fill:#c8c8ff;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <rect
- style="font-size:10px;fill:#c8c8ff;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="rect2883"
- width="120.76241"
- height="16.482248"
- x="211.62221"
- y="828.91174" />
- <rect
- style="font-size:10px;fill:#c8c8ff;fill-opacity:1;stroke:#000000;stroke-width:0.75;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="rect2875"
- width="120.76241"
- height="16.482248"
- x="211.62221"
- y="770.448" />
- <path
- id="path4477"
- d="M 243.59829,860.1812 L 243.59829,847.80683"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 295.09829,847.18855 L 295.09829,859.56292"
- id="path5000" />
- <text
- xml:space="preserve"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:end;line-height:125%;writing-mode:lr-tb;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- x="239.44922"
- y="856.36218"
- id="text2952"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan2954"
- x="239.44922"
- y="856.36218"
- style="font-style:italic;text-align:end;line-height:125%;writing-mode:lr-tb;text-anchor:end">call-next-method</tspan></text>
- <text
- id="text5030"
- y="855.92719"
- x="299.5"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- style="font-style:italic"
- y="855.92719"
- x="299.5"
- id="tspan5032"
- sodipodi:role="line">return</tspan></text>
- <path
- id="path5048"
- d="M 295.09829,814.18855 L 295.09829,826.56292"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <use
- x="0"
- y="0"
- xlink:href="#text5030"
- id="use2960"
- transform="translate(0,-33)"
- width="744.09448"
- height="1052.3622" />
- <path
- id="path5060"
- d="M 243.59829,801.6812 L 243.59829,789.30683"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- id="path5046"
- d="M 243.59829,827.1812 L 243.59829,814.80683"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 295.09829,788.68855 L 295.09829,801.06292"
- id="path5062" />
- <text
- id="text5072"
- y="808.65448"
- x="263"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- y="808.65448"
- x="263"
- id="tspan5074"
- sodipodi:role="line">. . .</tspan></text>
- <use
- x="0"
- y="0"
- xlink:href="#text2952"
- id="use2956"
- transform="translate(0,-33)"
- width="744.09448"
- height="1052.3622" />
- <use
- x="0"
- y="0"
- xlink:href="#text2952"
- id="use2958"
- transform="translate(0,-58.5)"
- width="744.09448"
- height="1052.3622" />
- <use
- x="0"
- y="0"
- xlink:href="#text5030"
- id="use2962"
- transform="translate(0,-58.5)"
- width="744.09448"
- height="1052.3622" />
- <flowRoot
- xml:space="preserve"
- id="flowRoot5638"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#00c800;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- transform="translate(-34.5,48.169125)"><flowRegion
- id="flowRegion5640"><rect
- id="rect5642"
- width="35.5"
- height="38.5"
- x="377"
- y="756.86218"
- style="text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#00c800;fill-opacity:1" /></flowRegion><flowPara
- id="flowPara5644">Most to least specific</flowPara></flowRoot> <path
- id="path3347"
- d="M 338,876.33367 L 338,772.83367"
- style="fill:none;fill-rule:evenodd;stroke:#00c800;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- </g>
- <path
- id="path3341"
- d="M 243.59829,892.6812 L 243.59829,880.30683"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <path
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 295.09829,880.18855 L 295.09829,892.56292"
- id="path3335" />
- <use
- height="1052.3622"
- width="744.09448"
- transform="translate(0,33)"
- id="use2964"
- xlink:href="#text5030"
- y="0"
- x="0" />
- <g
- transform="translate(35.976561,0)"
- id="g2994">
- <text
- sodipodi:linespacing="125%"
- id="text3155"
- y="873.42499"
- x="236.11349"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- xml:space="preserve"><tspan
- id="tspan3159"
- y="873.42499"
- x="236.11349"
- sodipodi:role="line">Around method</tspan></text>
- <use
- x="0"
- y="0"
- xlink:href="#text3155"
- id="use2986"
- transform="translate(-2.5939942e-6,-32.70712)"
- width="744.09448"
- height="1052.3622" />
- <use
- x="0"
- y="0"
- xlink:href="#text3155"
- id="use2988"
- transform="translate(-2.5939942e-6,-91.170866)"
- width="744.09448"
- height="1052.3622" />
- </g>
- </g>
- <g
- id="g3041">
- <use
- height="1052.3622"
- width="744.09448"
- transform="translate(0,-269.42743)"
- id="use3038"
- xlink:href="#text2952"
- y="0"
- x="0" />
- <flowRoot
- transform="translate(-24.870058,-7.68156)"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- id="flowRoot7999"
- xml:space="preserve"><flowRegion
- id="flowRegion8001"><rect
- y="558.09454"
- x="287.08536"
- height="24.395184"
- width="79.903069"
- id="rect8003" /></flowRegion><flowPara
- id="flowPara8005">‘No next method’ error</flowPara></flowRoot> <use
- height="1052.3622"
- width="744.09448"
- transform="translate(-7.6293945e-6,-178.00001)"
- id="use2990"
- xlink:href="#g2966"
- y="0"
- x="0" />
- <path
- id="path7372"
- d="M 244.26849,590.75383 L 244.26849,578.37946"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.75000000000000000;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
- <g
- id="g3138">
- <path
- d="M 252.33396,551.87647 L 253.31263,549.30103 L 253.7247,551.92798 L 255.37298,549.97064 L 255.11544,551.97949 L 258.92709,550.3312 L 256.14561,552.80363 L 259.18463,554.91549 L 255.88807,553.98833 L 258.56653,558.62413 L 255.37298,554.81247 L 255.78505,560.58146 L 254.3428,555.58511 L 251.71585,558.26357 L 253.41564,554.70946 L 250.53115,556.25472 L 252.33396,554.45191 L 248.93438,555.37907 L 250.73718,553.7823 L 248.11023,553.16419 L 251.25227,552.54608 L 250.78869,550.12517 L 252.33396,551.87647 z"
- id="path3079"
- sodipodi:nodetypes="ccccccccccccccccccccccc"
- style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M 252.53999,552.6491 L 251.35529,551.41289 L 252.07641,553.00966 L 249.55248,553.2157 L 251.92189,553.67928 L 250.68567,554.55493 L 253.10659,553.98833 L 252.64301,554.76097 L 254.08526,554.03984 L 253.51866,555.63662 L 254.49733,554.86398 L 255.16695,556.61528 L 254.85789,554.24588 L 255.99109,554.76097 L 255.01242,553.57626 L 256.6607,553.67928 L 255.37298,553.11268 L 256.24863,552.13401 L 254.54884,552.39156 L 254.65186,551.41289 L 253.57017,552.90665 L 253.1581,550.94931 L 252.53999,552.6491 z"
- id="path3080"
- sodipodi:nodetypes="ccccccccccccccccccccccc"
- style="fill:#ff3f00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:#a6a667;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.23994538;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
- sodipodi:nodetypes="ccccccccccccccc"
- id="path1817"
- d="M 247.30717,549.89628 C 246.00963,549.94176 244.84256,550.91505 244.19739,551.52591 C 243.54502,552.14928 243.51447,553.31251 243.61252,554.07733 C 243.71058,554.84216 243.96644,555.43302 243.96644,555.43302 L 245.06419,554.95313 C 245.06419,554.95313 244.87482,554.509 244.80025,553.92736 C 244.72626,553.35025 244.78543,552.72675 245.16617,552.28974 C 245.60405,551.74993 246.88452,550.98903 248.22897,551.20098 C 249.13851,551.42743 249.67447,552.72927 250.43297,553.23152 C 251.19147,553.73377 252.21663,553.97614 253.54026,553.71141 C 254.12914,553.53696 254.09465,552.46369 253.30631,552.53568 C 252.21323,552.7543 251.61772,552.57935 251.09882,552.23575 C 250.57991,551.89215 250.13911,551.30485 249.60516,550.66411 L 249.59316,550.65811 C 249.11476,550.16521 248.19393,549.88725 247.30717,549.89628 z" />
- <path
- style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- sodipodi:nodetypes="cc"
- id="path2440"
- d="M 253.49996,552.61443 C 253.49996,552.61443 252.20973,553.87567 251.25293,553.62922" />
- <path
- style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- sodipodi:nodetypes="cc"
- id="path2441"
- d="M 251.10796,552.23751 C 250.84702,552.54195 249.60028,552.13603 249.0494,551.73012" />
- <path
- style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- sodipodi:nodetypes="cc"
- id="path2442"
- d="M 247.81716,549.9035 C 247.87515,550.09197 247.1503,551.23722 246.51243,551.39669" />
- <path
- style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- sodipodi:nodetypes="cc"
- id="path2443"
- d="M 245.2367,550.70084 C 245.2367,550.70084 245.57013,551.62864 245.12073,552.33899" />
- <path
- style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.19195631pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- sodipodi:nodetypes="cc"
- id="path2444"
- d="M 243.55506,553.2523 C 243.96097,553.2378 244.88878,554.13661 244.93227,554.52803" />
- <path
- d="M 242.68783,554.71626 C 242.57768,555.20411 242.65793,556.90337 241.20017,558.32955 C 237.07117,559.4961 234.0618,562.82062 234.0618,566.78418 C 234.0618,571.7145 238.68629,575.71115 244.37945,575.71115 C 250.0726,575.71115 254.6911,571.7145 254.6911,566.78418 C 254.6911,562.61923 251.37869,559.14526 246.92287,558.15833 C 246.14765,557.08259 245.53252,555.5894 245.72314,554.65131 C 245.3779,554.17559 243.72357,553.79879 242.68783,554.71626 z"
- id="path1193"
- sodipodi:nodetypes="cccccccc"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M 103.57781,124.91055 A 30.456171,20.527729 0 1 1 42.665472,124.91055 A 30.456171,20.527729 0 1 1 103.57781,124.91055 z"
- id="path2452"
- sodipodi:cx="73.121643"
- sodipodi:cy="124.91055"
- sodipodi:rx="30.456171"
- sodipodi:ry="20.527729"
- sodipodi:type="arc"
- style="fill:url(#radialGradient3078);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- transform="matrix(0.2562315,0,0,0.2197998,225.48526,537.77762)" />
- </g>
- <g
- id="g3028"
- transform="translate(35.941561,-179.31502)">
- <text
- xml:space="preserve"
- style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L"
- x="236.11349"
- y="873.42499"
- id="text3030"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- x="236.11349"
- y="873.42499"
- id="tspan3032">Primary method</tspan></text>
- <use
- height="1052.3622"
- width="744.09448"
- transform="translate(-2.5939942e-6,-32.70712)"
- id="use3034"
- xlink:href="#text3030"
- y="0"
- x="0" />
- <use
- height="1052.3622"
- width="744.09448"
- transform="translate(-2.5939942e-6,-91.170866)"
- id="use3036"
- xlink:href="#text3030"
- y="0"
- x="0" />
- </g>
- </g>
- </g>
-</svg>
--- /dev/null
+%%% -*- mode: latex; TeX-PDF-mode: t -*-
+
+\documentclass{article}
+\usepackage[palatino, helvetica, courier, maths=cmr]{mdwfonts}
+
+\usepackage{syntax}
+\usepackage{tikz}
+\usetikzlibrary{calc}
+\usetikzlibrary{positioning}
+
+\errorcontextlines=999
+
+\begin{document}
+
+\begin{tikzpicture}
+ [>=stealth, thick,
+ order/.append style={color=green!70!black},
+ code/.append style={font=\sffamily},
+ action/.append style={font=\itshape},
+ method/.append style={rectangle, draw=black, thin, fill=blue!30,
+ text height=\ht\strutbox, text depth=\dp\strutbox,
+ minimum width=40mm}]
+
+ \def\delgstack#1#2#3{
+ \node (#10) [method, #2] {#3};
+ \node (#11) [method, above=6mm of #10] {#3};
+ \draw [->] ($(#10.north)!.5!(#10.north west) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method};
+ \draw [<-] ($(#10.north)!.5!(#10.north east) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return};
+ \draw [->] ($(#11.north)!.5!(#11.north west) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method}
+ node (ld) [above] {$\smash\vdots\mathstrut$};
+ \draw [<-] ($(#11.north)!.5!(#11.north east) + (0mm, 1mm)$) --
+ ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return}
+ node (rd) [above] {$\smash\vdots\mathstrut$};
+ \draw [->] ($(ld.north) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method};
+ \draw [<-] ($(rd.north) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [action, right=4pt, midway] {return};
+ \node (p) at ($(ld.north)!.5!(rd.north)$) {};
+ \node (#1n) [method, above=5mm of p] {#3};
+ \draw [->, order] ($(#10.south east) + (4mm, 1mm)$) --
+ ($(#1n.north east) + (4mm, -1mm)$)
+ node [midway, right, align=left] {Most to \\ least \\ specific};}
+
+ \delgstack{a}{}{Around method}
+ \draw [<-] ($(a0.south)!.5!(a0.south west) - (0mm, 1mm)$) --
+ ++(0mm, -4mm);
+ \draw [->] ($(a0.south)!.5!(a0.south east) - (0mm, 1mm)$) --
+ ++(0mm, -4mm)
+ node [action, right=4pt, midway] {return};
+
+ \draw [->] ($(an.north)!.6!(an.north west) + (0mm, 1mm)$) --
+ ++(-8mm, 8mm)
+ node [code, midway, left=3mm] {next_method}
+ node (b0) [method, above left = 1mm + 4mm and -6mm - 4mm] {};
+ \node (b1) [method] at ($(b0) - (2mm, 2mm)$) {};
+ \node (bn) [method] at ($(b1) - (2mm, 2mm)$) {Before method};
+ \draw [->, order] ($(bn.west) - (6mm, 0mm)$) -- ++(12mm, 12mm)
+ node [midway, above left, align=center] {Most to \\ least \\ specific};
+ \draw [->] ($(b0.north east) + (-10mm, 1mm)$) -- ++(8mm, 8mm)
+ node (p) {};
+
+ \delgstack{m}{above right=1mm and 0mm of an.west |- p}{Primary method}
+ \draw [->] ($(mn.north)!.5!(mn.north west) + (0mm, 1mm)$) -- ++(0mm, 4mm)
+ node [code, left=4pt, midway] {next_method}
+ node [above right = 0mm and -8mm]
+ {$\vcenter{\hbox{\Huge\textcolor{red}{!}}}
+ \vcenter{\hbox{\begin{tabular}[c]{l}
+ \textsf{next_method} \\
+ pointer is null
+ \end{tabular}}}$};
+
+ \draw [->, color=blue, dotted]
+ ($(m0.south)!.2!(m0.south east) - (0mm, 1mm)$) --
+ ($(an.north)!.2!(an.north east) + (0mm, 1mm)$)
+ node [midway, sloped, below] {Return value};
+
+ \draw [<-] ($(an.north)!.6!(an.north east) + (0mm, 1mm)$) --
+ ++(8mm, 8mm)
+ node [action, midway, right=3mm] {return}
+ node (f0) [method, above right = 1mm and -6mm] {};
+ \node (f1) [method] at ($(f0) + (-2mm, 2mm)$) {};
+ \node (fn) [method] at ($(f1) + (-2mm, 2mm)$) {After method};
+ \draw [<-, order] ($(f0.east) + (6mm, 0mm)$) -- ++(-12mm, 12mm)
+ node [midway, above right, align=center] {Least to \\ most \\ specific};
+ \draw [<-] ($(fn.north west) + (6mm, 1mm)$) -- ++(-8mm, 8mm);
+
+\end{tikzpicture}
+
+\end{document}
\begin{describe}[SodClass]{cls}
{[nick = cls, link = SodObject] \\
- class SodClass : SodObject \{ \\ \ind
+ class SodClass: SodObject \{ \\ \ind
const char *name; \\
const char *nick; \\
size_t initsz; \\
@<type>_n @<slot>_n; \-\\
\} $c$; \-\\
\} $c$; \\
- struct $H$__ichain_$h$ $h$; \\
+ struct $A$__ichain_$h$ $a$; \\
\quad$\vdots$ \-\\
\} $h$; \\
union $B$__ichainu_$i$ $i$; \\
@<type>_n @<arg>_n? \+\\
@<type>_{n+1} @<kw>_{n+1} @[= @<dflt>_{n+1}@],
$\ldots$,
- @<type>_m @<kw>_m @[= @<dflt>_m@]);
+ @<type>_{n'} @<kw>_{n'} @[= @<dflt>_{n'}@]);
\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
\chapter{Module syntax} \label{ch:syntax}
%%%--------------------------------------------------------------------------
-\section{Notation} \label{sec:syntax.notation}
-
-Fortunately, Sod is syntactically quite simple. The notation is slightly
-unusual in order to make the presentation shorter and easier to read.
-
-Anywhere a simple nonterminal name $x$ may appear in the grammar, an
-\emph{indexed} nonterminal $x[a_1, \ldots, a_n]$ may also appear. On the
-left-hand side of a production rule, the indices $a_1$, \ldots, $a_n$ are
-variables which vary over all nonterminal and terminal symbols, and the
-variables may also appear on the right-hand side in place of a nonterminal.
-Such a rule stands for a family of rules, in each variable is replaced by
-each possible simple nonterminal or terminal symbol.
-
-The letter $\epsilon$ denotes the empty nonterminal
-\begin{quote}
- \syntax{$\epsilon$ ::=}
-\end{quote}
-
-The following indexed productions are used throughout the grammar, some often
-enough that they deserve special notation.
-\begin{itemize}
-\item @[$x$@] abbreviates @<optional>$[x]$, denoting an optional occurrence
- of $x$:
- \begin{quote}
- \syntax{@[$x$@] ::= <optional>$[x]$ ::= $\epsilon$ @! $x$}
- \end{quote}
-\item $x^*$ abbreviates @<zero-or-more>$[x]$, denoting a sequence of zero or
- more occurrences of $x$:
- \begin{quote}
- \syntax{$x^*$ ::= <zero-or-more>$[x]$ ::=
- $\epsilon$ @! <zero-or-more>$[x]$ $x$}
- \end{quote}
-\item $x^+$ abbreviates @<one-or-more>$[x]$, denoting a sequence of one or
- more occurrences of $x$:
- \begin{quote}
- \syntax{$x^+$ ::= <one-or-more>$[x]$ ::= <zero-or-more>$[x]$ $x$}
- \end{quote}
-\item @<list>$[x]$ denotes a sequence of one or more occurrences of $x$
- separated by commas:
- \begin{quote}
- \syntax{<list>$[x]$ ::= $x$ @! <list>$[x]$ "," $x$}
- \end{quote}
-\end{itemize}
-
-%%%--------------------------------------------------------------------------
\section{Lexical syntax} \label{sec:syntax.lex}
Whitespace and comments are discarded. The remaining characters are
<digit-char> ::= "0" | <nonzero-digit-char>
-<nonzero-digit-char> ::= "1" | "2" $| \cdots |$ "9"
+<nonzero-digit-char> ::= "1" | "2" $| \ldots |$ "9"
\end{grammar}
The precise definition of @<alpha-char> is left to the function
<octal-integer> ::= "0" @["o"|"O"@] @<octal-digit-char>^+
-<octal-digit-char> ::= "0" | "1" $| \cdots |$ "7"
+<octal-digit-char> ::= "0" | "1" $| \ldots |$ "7"
<hex-integer> ::= "0" @("x"|"X"@) @<hex-digit-char>^+
A @<module> is the top-level syntactic item. A module consists of a sequence
of definitions.
+[FIXME]
+Properties:
+\begin{description}
+\item[@"module_class"] A symbol naming the Lisp class to use to
+ represent the module.
+\item[@"guard"] An identifier to use as the guard symbol used to prevent
+ multiple inclusion in the header file.
+\end{description}
+
+
\subsection{Simple definitions} \label{sec:syntax.module.simple}
\subsubsection{Importing modules}
<abstract-declarator> ::= <declarator>$[\epsilon, \mbox{@<argument-list>}]$
-<argument-declarator> ::= <declarator>$[\mbox{@<identifier> @! $\epsilon$}]$
-
<argument-declarator> ::=
<declarator>$[\mbox{@<identifier> @! $\epsilon$}, \mbox{@<argument-list>}]$
\begin{prog}
class Sub; \\+
-class Super : SodObject \{ \\ \ind
+class Super: SodObject \{ \\ \ind
Sub *sub; \-\\
\}; \\+
-class Sub : Super \{ \\ \ind
+class Sub: Super \{ \\ \ind
/* \dots\ */ \-\\
\};
\end{prog}
which messages it will respond to, and what its behaviour will be when it
receives them. The property value must be an identifier naming a defined
subclass of @"SodClass". The default metaclass is @"SodClass".
- %%% FIXME xref to theory
+ See \xref{sec:concepts.metaclasses} for more details.
\item[@"nick"] A nickname for the class, to be used to distinguish it from
other classes in various limited contexts. The property value must be an
identifier; the default is constructed by forcing the class name to
interpreted as being a @<message-item> or @<method-item>. Pointers to
functions are fine.
+Properties:
+\begin{description}
+\item[@"slot_class"] A symbol naming the Lisp class to use to represent the
+ direct slot.
+\item[@"initarg"] An identifier naming an initialization argument which can
+ be used to provide a value for the slot. See
+ \xref{sec:concepts.lifecycle.birth} for the details.
+\end{description}
+
An @<initializer>, if present, is treated as if a separate
@<initializer-item> containing the slot name and initializer were present.
For example,
\begin{prog}
[nick = eg] \\
-class Example : Super \{ \\ \ind
+class Example: Super \{ \\ \ind
int foo = 17; \-\\
\};
\end{prog}
means the same as
\begin{prog}
[nick = eg] \\
-class Example : Super \{ \\ \ind
+class Example: Super \{ \\ \ind
int foo; \\
eg.foo = 17; \-\\
\};
class's superclasses (including itself); the second must be the name of a
slot defined in that superclass.
-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.
+Properties:
+\begin{description}
+\item[@"initializer_class"] A symbol naming the Lisp class to use to
+ represent the initializer.
+\item[@"initarg"] An identifier naming an initialization argument which can
+ be used to provide a value for the slot. See
+ \xref{sec:concepts.lifecycle.birth} for the details. An initializer item
+ must have either an @|initarg| property, or an initializer expression, or
+ both.
+\item[@"initarg_class"] A symbol naming the Lisp class to use to represent
+ the initarg. Only permitted if @"initarg" is also set.
+\end{description}
Each class may define at most one initializer item with an explicit
initializer expression for a given slot.
@<declaration-specifier>^+
<list>$[\mbox{@<init-declarator>}]$ ";"
\end{grammar}
+Properties:
+\begin{description}
+\item[@"initarg_class"] A symbol naming the Lisp class to use to represent
+ the initarg.
+\end{description}
\subsubsection{Fragment items}
\begin{grammar}
<keyword-declarator>$[\mbox{@<identifier>}]$
@[<method-body>@]
\end{grammar}
+Properties:
+\begin{description}
+\item[@"message_class"] A symbol naming the Lisp class to use to represent
+ the message.
+\item[@"combination"] A keyword naming the aggregating method combination to
+ use.
+\item[@"most_specific"] A keyword, either @`first' or @`last', according to
+ whether the most specific applicable method should be invoked first or
+ last.
+\end{description}
+
+Properties for the @|custom| aggregating method combination:
+\begin{description}
+\item[@"retvar"] An identifier for the return value from the effective
+ method. The default is @|sod__ret|. Only permitted if the message return
+ type is not @|void|.
+\item[@"valvar"] An identifier holding each return value from a direct method
+ in the effective method. The default is @|sod__val|. Only permitted if
+ the method return type (see @"methty" below) is not @|void|.
+\item[@"methty"] A C type, which is the return type for direct methods of
+ this message.
+\item[@"decls"] A code fragment containing declarations to be inserted at the
+ head of the effective method body. The default is to insert nothing.
+\item[@"before"] A code fragment containing initialization to be performed at
+ the beginning of the effective method body. The default is to insert
+ nothing.
+\item[@"empty"] A code fragment executed if there are no primary methods;
+ it should usually store a suitable (identity) value in @<retvar>. The
+ default is not to emit an effective method at all if there are no primary
+ methods.
+\item[@"first"] A code fragment to set the return value after calling the
+ first applicable direct method. The default is to use the @"each"
+ fragment.
+\item[@"each"] A code fragment to set the return value after calling a direct
+ method. If @"first" is also set, then it is used after the first direct
+ method instead of this. The default is to insert nothing, which is
+ probably not what you want.
+\item[@"after"] A code fragment inserted at the end of the effective method
+ body. The default is to insert nothing.
+\item[@"count"] An identifier naming a variable to be declared in the
+ effective method body, of type @|size_t|, holding the number of applicable
+ methods. The default is not to provide such a variable.
+\end{description}
\subsubsection{Method items}
\begin{grammar}
<method-body> ::= "{" <c-fragment> "}" | "extern" ";"
\end{grammar}
+Properties:
+\begin{description}
+\item[@"method_class"] A symbol naming the Lisp class to use to represent
+ the direct method.
+\item[@"role"] A keyword naming the direct method's rôle. For the built-in
+ `simple' message classes, the acceptable rôle names are @|before|,
+ @|after|, and @|around|. By default, a primary method is constructed.
+\end{description}
%%%----- That's all, folks --------------------------------------------------
\begin{prog}
/* -*-sod-*- */ \\+
- code c : includes \{ \\
+ code c: includes \{ \\
\#include "greeter.h" \\
\} \\+
- code h : includes \{ \\
+ code h: includes \{ \\
\#include <stdio.h> \\
\#include <sod/sod.h> \\
\} \\+
- class Greeter : SodObject \{ \\ \ind
+ class Greeter: SodObject \{ \\ \ind
void greet(FILE *fp) \{ \\ \ind
fputs("Hello, world!\textbackslash n", fp); \-\\
\} \-\\
The basic syntax for @"code" stanzas is
\begin{prog}
- code @<file-label> : @<section> \{ \\ \ind
+ code @<file-label>: @<section> \{ \\ \ind
@<code> \-\\
\}
\end{prog}
set##_KWSET(KWSET__SUPPLIEDP) \
set##_KWSET(KWSET__STRUCTMEM) \
}
-#define KWSET__SUPPLIEDP(type, name, dflt) unsigned name##_suppliedp : 1;
+#define KWSET__SUPPLIEDP(type, name, dflt) unsigned name##_suppliedp: 1;
#define KWSET__STRUCTMEM(type, name, dflt) type name;
/* --- @KWSET_PARSEFN@ --- *
\h'6n'} \fIc\fB;
\h'4n'} \fIc\fB;
\h'4n'\fR...\fB
-\h'4n'struct \fIH\fB__ichain_\fIh\fB \fIh\fB;
+\h'4n'struct \fIA\fB__ichain_\fIh\fB \fIa\fB;
\h'2n'} \fIh\fB;
\h'2n'union \fIB\fB__ichainu_\fIi\fB \fIi\fB;
\h'2n'\fR...\fB
(definst suppliedp-struct (stream) (flags var)
(format stream
- "~@<struct { ~2I~_~{unsigned ~A : 1;~^ ~_~} ~I~_} ~A;~:>"
+ "~@<struct { ~2I~_~{unsigned ~A: 1;~^ ~_~} ~I~_} ~A;~:>"
flags var))
;; Initialization.
(defmethod make-sod-user-initarg
((class sod-class) name type pset &optional default location)
- (declare (ignore pset))
(with-slots (initargs) class
- (push (make-instance 'sod-user-initarg :location (file-location location)
+ (push (make-instance (get-property pset :initarg-class :symbol
+ 'sod-user-initarg)
+ :location (file-location location)
:class class :name name :type type :default default)
initargs)))
(defmethod make-sod-slot-initarg-using-slot
((class sod-class) name (slot sod-slot) pset &optional location)
- (declare (ignore pset))
(with-slots (initargs) class
(with-slots ((type %type)) slot
- (push (make-instance 'sod-slot-initarg
+ (push (make-instance (get-property pset :initarg-class :symbol
+ 'sod-slot-initarg)
:location (file-location location)
:class class :name name :type type :slot slot)
initargs))))
(when varargsp
(format stream "#ifdef SOD__HAVE_VARARGS_MACROS~%"))
(format stream "#define ~A(~{~A~^, ~}) ~
- ~A->_vt->~A.~A(~{~A~^, ~})~%"
+ (~A)->_vt->~A.~A(~{~A~^, ~})~%"
(message-macro-name class entry)
(nreverse in-names)
me
(c-function-keywords type))))
(when keys
(format stream "struct ~A {~%~
- ~{ unsigned ~A : 1;~%~}~
+ ~{ unsigned ~A: 1;~%~}~
};~2%"
(direct-method-suppliedp-struct-tag method)
(mapcar #'argument-name keys))))))))
class)
(format stream "~&struct ~A {~%"
(effective-method-keyword-struct-tag method))
- (format stream "~{ unsigned ~A__suppliedp : 1;~%~}"
+ (format stream "~{ unsigned ~A__suppliedp: 1;~%~}"
(mapcar #'argument-name keys))
(dolist (key keys)
(write-string " " stream)
(defclass aggregating-effective-method (simple-effective-method) ()
(:documentation "Effective method counterpart to `aggregating-message'."))
+(defgeneric aggregating-message-always-live-p (message combination)
+ (:documentation
+ "Return whether the method combination can work without primary methods.
+
+ Return non-nil if the corresponding effective method should be considered
+ live even if it doesn't have any methods.")
+ (:method ((message aggregating-message) (combination t)) nil))
+
+(defmethod effective-method-live-p ((method aggregating-effective-method))
+ (or (let* ((message (effective-method-message method))
+ (comb (sod-message-combination message)))
+ (aggregating-message-always-live-p message comb))
+ (call-next-method)))
+
;;;--------------------------------------------------------------------------
;;; Implementation.
(methods (gensym "METHODS-")))
&key properties return-type
((:around around-func) '#'funcall)
+ ((:empty empty-func) nil emptyp)
((:first-method first-method-func) nil firstp)
((:methods methods-func) '#'funcall))
"Utility macro for definining aggregating method combinations.
on `check-aggregating-message-type' to check the that the message's return
type matches RETURN-TYPE.
+ If an EMPTY function is given, then (a) it's OK if there are no primary
+ methods, because (b) the EMPTY function is called to set the return
+ value variable in this case. Note that EMPTY is only called when there
+ are no primary methods.
+
The AROUND, FIRST-METHOD, and METHODS are function designators (probably
`lambda' forms) providing pieces of the aggregating behaviour.
(with-gensyms (type msg combvar target arg-names args want-type
meth targ func call-methfunc
- aroundfunc fmethfunc methfunc)
+ aroundfunc fmethfunc methfunc bodyfunc)
`(progn
;; If properties are listed, arrange for them to be collected.
,combvar ,want-type)))
(call-next-method))))
+ ;; If there is an EMPTY function then the effective method is always
+ ;; live.
+ ,@(and emptyp
+ `((defmethod aggregating-message-always-live-p
+ ((,msg aggregating-message)
+ (,combvar (eql ',comb)))
+ t)))
+
;; Define the main kernel-compuation method.
(defmethod compute-aggregating-message-kernel
((,msg aggregating-message) (,combvar (eql ',comb))
;; Declare the necessary variables and give names to the functions
;; supplied by the caller.
(let* (,@(and vars
- `((,type (c-type-subtype (sod-message-type ,msg)))))
+ `((,type (c-type-subtype (sod-message-type ,msg)))
+ (,(car vars) (temporary-var ,codegen ,type))))
,@(mapcar (lambda (var)
- (list var `(temporary-var ,codegen ,type)))
- vars)
+ (list var `(and ,methods
+ (temporary-var ,codegen ,type))))
+ (cdr vars))
(,aroundfunc ,around-func)
(,methfunc ,methods-func)
(,fmethfunc ,(if firstp first-method-func methfunc)))
- ;; Arrange to release the temporaries when we're finished with
- ;; them.
- (unwind-protect
- (progn
-
- ;; Wrap the AROUND function around most of the work.
- (funcall ,aroundfunc
- (lambda (&rest ,args)
- (flet ((,call-methfunc (,func ,meth)
- ;; Call FUNC, passing it an INVOKE
- ;; function which will generate a call
- ;; to METH.
- (apply ,func
- (lambda
- (&optional (,targ :void))
- (invoke-method ,codegen
- ,targ
- ,arg-names
- ,meth))
- ,args)))
-
- ;; The first method might need special
- ;; handling.
- (,call-methfunc ,fmethfunc (car ,methods))
-
- ;; Call the remaining methods in the right
- ;; order.
- (dolist (,meth (cdr ,methods))
- (,call-methfunc ,methfunc ,meth)))))
+ (flet ((,bodyfunc ()
+ (funcall ,aroundfunc
+ (lambda (&rest ,args)
+ (flet ((,call-methfunc (,func ,meth)
+ ;; Call FUNC, passing it an INVOKE
+ ;; function which will generate a
+ ;; call to METH.
+ (apply ,func
+ (lambda
+ (&optional (,targ :void))
+ (invoke-method ,codegen
+ ,targ
+ ,arg-names
+ ,meth))
+ ,args)))
+
+ ;; The first method might need special
+ ;; handling.
+ (,call-methfunc ,fmethfunc (car ,methods))
+
+ ;; Call the remaining methods in the right
+ ;; order.
+ (dolist (,meth (cdr ,methods))
+ (,call-methfunc ,methfunc ,meth)))))))
+
+ ;; Arrange to release the temporaries when we're finished with
+ ;; them.
+ (unwind-protect
+ (progn
+
+ ;; If there are no direct methods, then just do the
+ ;; empty-effective-method thing to set the return
+ ;; variable. Otherwise, wrap AROUND round the main body.
+ ,(if emptyp
+ `(if (null ,methods)
+ (funcall ,empty-func)
+ (,bodyfunc))
+ `(,bodyfunc))
;; Outside the AROUND function now, deliver the final
;; result to the right place.
(deliver-expr ,codegen ,target ,(car vars)))
- ;; Finally, release the temporary variables.
- ,@(mapcar (lambda (var) `(setf (var-in-use-p ,var) nil))
- vars))))
+ ;; Finally, release the temporary variables.
+ ,@(mapcar (lambda (var)
+ `(when ,var (setf (var-in-use-p ,var) nil)))
+ vars)))))
',comb)))
;;; Fixed aggregating method combinations.
(define-aggregating-method-combination :progn (nil)
- :return-type void)
+ :return-type void
+ :empty (lambda () nil))
(define-aggregating-method-combination :sum ((acc val) :codegen codegen)
+ :empty (lambda () (emit-inst codegen (make-set-inst acc 0)))
:first-method (lambda (invoke)
(funcall invoke val)
(emit-inst codegen (make-set-inst acc val)))
(emit-inst codegen (make-update-inst acc #\+ val))))
(define-aggregating-method-combination :product ((acc val) :codegen codegen)
+ :empty (lambda () (emit-inst codegen (make-set-inst acc 1)))
:first-method (lambda (invoke)
(funcall invoke val)
(emit-inst codegen (make-set-inst acc val)))
(make-set-inst acc val)))))
(define-aggregating-method-combination :and ((ret) :codegen codegen)
+ :empty (lambda () (emit-inst codegen (make-set-inst ret 1)))
:around (lambda (body)
(codegen-push codegen)
(funcall body)
(make-break-inst)))))
(define-aggregating-method-combination :or ((ret) :codegen codegen)
+ :empty (lambda () (emit-inst codegen (make-set-inst ret 0)))
:around (lambda (body)
(codegen-push codegen)
(funcall body)
'(:retvar :id
:valvar :id
:methty :type
+ :empty :fragment
:decls :fragment
:before :fragment
:first :fragment
(getf (sod-message-plist message) :methty
(c-type-subtype (sod-message-type message))))
+(defmethod aggregating-message-always-live-p
+ ((message aggregating-message) (combination (eql :custom)))
+ (getf (sod-message-plist message) :empty))
+
(defmethod compute-aggregating-message-kernel
((message aggregating-message) (combination (eql :custom))
codegen target methods arg-names
&key (retvar "sod_ret") (valvar "sod_val") (methty nil methtyp)
- decls before each (first each) after count)
+ empty decls before each (first each) after count)
(let* ((type (c-type-subtype (sod-message-type message)))
(methty (if methtyp methty type)))
(unless (eq type c-type-void)
(ensure-var codegen retvar type))
- (unless (eq methty c-type-void)
+ (unless (or (null methods)
+ (eq methty c-type-void))
(ensure-var codegen valvar methty))
- (when count
+ (when (and methods count)
(ensure-var codegen count c-type-size-t (length methods)))
- (when decls
+ (when (and methods decls)
(emit-decl codegen decls))
(labels ((maybe-emit (fragment)
(when fragment (emit-inst codegen fragment)))
(if (eq methty c-type-void) :void valvar)
arg-names method)
(maybe-emit fragment)))
- (maybe-emit before)
- (invoke (car methods) first)
- (dolist (method (cdr methods)) (invoke method each))
- (maybe-emit after)
+ (cond ((and empty (null methods))
+ (emit-inst codegen empty))
+ (t
+ (maybe-emit before)
+ (invoke (car methods) first)
+ (dolist (method (cdr methods)) (invoke method each))
+ (maybe-emit after)))
(deliver-expr codegen target retvar))))
;;;----- That's all, folks --------------------------------------------------
(codegen-push codegen)
(ensure-var codegen "sod__obj" ilayout-type
(make-convert-to-ilayout-inst class
- head "me"))))
+ head "me"))
+ (deliver-call codegen :void "SOD__IGNORE" "sod__obj")))
(finish-entry (tail)
(let* ((head (sod-class-chain-head tail))
(role (if parm-n :valist nil))
(*keyword-struct-disposition* :local))
(ensure-var codegen *sod-keywords* (c-type (struct tag)))
(make-keyword-parser-function codegen method tag set keywords)
+ (emit-insts codegen
+ (mapcar (lambda (keyword)
+ (make-set-inst
+ (format nil "~A.~A__suppliedp"
+ *sod-keywords*
+ (argument-name keyword))
+ 0))
+ keywords))
(parse-keywords (lambda ()
(call :void name kw-addr ap-addr
*null-pointer* 0)))
collect (ext:argv i))))
(error "Unsupported Lisp"))))))
- *program-name* (pathname-name (car *command-line*))))
+ *program-name* (pathname-name (car *command-line*))))
;;;--------------------------------------------------------------------------
;;; Fancy conditionals.
(opt-long-name o)
(opt-arg-optional-p o)
(opt-arg-name o)
- (opt-documentation o)))))
+ (opt-%documentation o)))))
(:constructor %make-option
(&key long-name tag negated-tag short-name
arg-name arg-optional-p documentation
and the variables may also appear on the right-hand side
in place of a nonterminal.
Such a rule stands for a family of rules,
-in each variable is replaced by
+in which each variable is replaced by
each possible simple nonterminal or terminal symbol.
.PP
The letter \*e denotes the empty nonterminal
./kwtest >kwtest.out
diff -u $(srcdir)/kwtest.ref kwtest.out
+check_PROGRAMS += rat
+
+EXTRA_DIST += rat.sod rat.ref
+nodist_rat_SOURCES = rat.c rat.h
+BUILT_SOURCES += $(nodist_rat_SOURCES)
+check-local:: rat rat.ref
+ ./rat >rat.out
+ diff -u $(srcdir)/rat.ref rat.out
+
###----- That's all, folks --------------------------------------------------
* A simple SOD module for testing.
*/
-code c : includes {
+code c: includes {
#include <stdio.h>
#include "chimaera.h"
}
-code h : includes {
+code h: includes {
#include "sod.h"
}
[nick = nml, link = SodObject]
-class Animal : SodObject {
+class Animal: SodObject {
int tickles = 0;
[combination = progn] void tickle();
[role = before] void nml.tickle() { me->nml.tickles++; }
}
-class Lion : Animal {
+class Lion: Animal {
void bite() { puts("Munch!"); }
void nml.tickle() { Lion_bite(me); }
}
-class Goat : Animal {
+class Goat: Animal {
void butt() { puts("Bonk!"); }
void nml.tickle() { Goat_butt(me); }
}
-class Serpent : Animal {
+class Serpent: Animal {
int limit = 2;
void hiss() { puts("Sssss!"); }
}
[nick = sir, link = Animal]
-class Chimaera : Lion, Goat, Serpent {
+class Chimaera: Lion, Goat, Serpent {
serpent.limit = 1;
}
-code c : user {
+code c: user {
/*----- Main driver code --------------------------------------------------*/
static void tickle_animal(Animal *a)
--- /dev/null
+0/1
+6/1
+2/3
--- /dev/null
+/* -*-sod-*- */
+
+code c: includes {
+#include <assert.h>
+#include <stdio.h>
+
+#include "rat.h"
+}
+
+code h: includes {
+#include "sod.h"
+}
+
+typename FILE;
+
+code c: early_user {
+ static int gcd(int x, int y)
+ {
+ int t;
+ if (x < 0) x = -x;
+ if (y < 0) y = -y;
+ while (y) { t = x%y; x = y; y = t; }
+ return (x);
+ }
+}
+
+[nick = rat, link = SodObject]
+class Rational: SodObject {
+ int num, den;
+ initarg int num = 0, den = 1;
+ init {
+ int g;
+ assert(den);
+ g = gcd(num, den);
+ me->rat.num = num/g;
+ me->rat.den = den/g;
+ }
+
+ void print(FILE *fp) { fprintf(fp, "%d/%d\n", me->rat.num, me->rat.den); }
+}
+
+code c: user {
+ int main(void)
+ {
+ SOD_DECL(Rational, r0, KWARGS(K(den, 9)));
+ SOD_DECL(Rational, r1, KWARGS(K(num, 6)));
+ SOD_DECL(Rational, r2, KWARGS(K(num, 6) K(den, 9)));
+ Rational_print(r0, stdout);
+ Rational_print(r1, stdout);
+ Rational_print(r2, stdout);
+ return (0);
+ }
+}
/* -*-sod-*- */
-code h : includes {
+code h: includes {
#include "sod.h"
}
-code c : includes {
+code c: includes {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"
}
-code c : early_user {
+code c: early_user {
/*----- Preliminary definitions -------------------------------------------*/
/* Confuse the fragment scanner... */
}
-code c : (tests head)
+code c: (tests head)
[user (tests head) tests (tests tail) main (user end)]
{
/*----- Test machinery ----------------------------------------------------*/
static void tests(void)
LBRACE
}
-code c : (tests tail) {
+code c: (tests tail) {
RBRACE
}
-code c : main {
+code c: main {
/*----- Main program ------------------------------------------------------*/
int main(void)
/*----- Various kinds of method combinations ------------------------------*/
-code h : early_user {
+code h: early_user {
struct item {
struct item *next;
const char *p;
}
-code c : early_user {
+code c: early_user {
static void *xmalloc(size_t n)
{
void *p = malloc(n);
}
-[link = SodObject, nick = t1base]
-class T1Base : SodObject {
- [combination = progn] void aprogn() { STEP(1); }
- [combination = sum] int asum() { return 1; }
- [combination = and] int aand() { return 8; }
- [combination = max] int amax() { return 12; }
+[link = SodObject, nick = base]
+class T1Base: SodObject {
+ [combination = progn] void aprogn();
+ [combination = sum] int asum();
+ [combination = and] int aand();
+ [combination = max] int amax();
[combination = custom,
+ empty = { sod_ret = 0; },
decls = { struct item **head = &sod_ret; },
each = { *head = sod_val; head = &sod_val->next; },
after = { *head = 0; }]
- struct item *alist() { return make_item("base"); }
+ struct item *alist();
[combination = custom,
decls = { int *v; size_t i = 0; }, methty = <int>, count = n,
+ empty = { sod_ret.v = 0; sod_ret.n = 0; },
before = { v = xmalloc(n*sizeof(int)); },
each = { v[i++] = sod_val; },
after = { sod_ret.v = v; sod_ret.n = n; }]
struct vec avec();
- int t1base.avec() { return 19; }
}
-[link = T1Base, nick = t1sub]
-class T1Sub : T1Base {
- void t1base.aprogn() { STEP(0); }
- int t1base.asum() { return 2; }
- int t1base.aand() { return 6; }
- int t1base.amax() { return 17; }
- struct item *t1base.alist() { return make_item("sub"); }
- int t1base.avec() { return 4; }
+[link = T1Base, nick = mid]
+class T1Mid: T1Base {
+ void base.aprogn() { STEP(1); }
+ int base.asum() { return 1; }
+ int base.aand() { return 8; }
+ int base.amax() { return 12; }
+ struct item *base.alist() { return make_item("mid"); }
+ int base.avec() { return 19; }
+}
+
+[link = T1Mid, nick = sub]
+class T1Sub: T1Mid {
+ void base.aprogn() { STEP(0); }
+ int base.asum() { return 2; }
+ int base.aand() { return 6; }
+ int base.amax() { return 17; }
+ struct item *base.alist() { return make_item("sub"); }
+ int base.avec() { return 4; }
}
-code c : tests {
+code c: tests {
prepare("aggregate, base");
{ SOD_DECL(T1Base, t1, NO_KWARGS);
struct item *l;
struct vec v;
+ STEP(0); T1Base_aprogn(t1); STEP(1);
+ if (T1Base_asum(t1) == 0) STEP(2);
+ if (T1Base_aand(t1) == 1) STEP(3);
+ if (!t1->_vt->base.amax) STEP(4);
+ l = T1Base_alist(t1);
+ if (!l) STEP(5);
+ v = T1Base_avec(t1);
+ if (!v.n) STEP(6);
+ DONE(7);
+ }
+ prepare("aggregate, mid");
+ { SOD_DECL(T1Mid, t1, NO_KWARGS);
+ struct item *l;
+ struct vec v;
STEP(0); T1Base_aprogn(t1); /* 1 */
if (T1Base_asum(t1) == 1) STEP(2);
if (T1Base_aand(t1) == 8) STEP(3);
if (T1Base_amax(t1) == 12) STEP(4);
l = T1Base_alist(t1);
- if (!check_list(l, "base", (const char *)0)) STEP(5);
+ if (!check_list(l, "mid", (const char *)0)) STEP(5);
free_list(l);
v = T1Base_avec(t1);
if (!check_vec(&v, 19, -1)) STEP(6);
if (T1Base_aand(t1) == 8) STEP(3);
if (T1Base_amax(t1) == 17) STEP(4);
l = T1Base_alist(t1);
- if (!check_list(l, "sub", "base", (const char *)0)) STEP(5);
+ if (!check_list(l, "sub", "mid", (const char *)0)) STEP(5);
free_list(l);
v = T1Base_avec(t1);
if (!check_vec(&v, 4, 19, -1)) STEP(6);
/*----- Slot and user initargs --------------------------------------------*/
[link = SodObject, nick = t2]
-class T2 : SodObject {
+class T2: SodObject {
[initarg = x] int x = 0;
initarg int y = 1;
init { if (!y) STEP(0); }
}
-code c : tests {
+code c: tests {
prepare("initargs, defaults");
{ SOD_DECL(T2, t, NO_KWARGS);
if (t->t2.x == 0) STEP(0);
/*----- Keyword argument propagation --------------------------------------*/
[link = SodObject, nick = base]
-class T3Base : SodObject {
+class T3Base: SodObject {
void m0(?int x) { STEP(x); }
void m1(?) { }
}
[link = T3Base, nick = mid]
-class T3Mid : T3Base {
+class T3Mid: T3Base {
void base.m0(?int y) { STEP(y); CALL_NEXT_METHOD; }
void base.m1(?) { STEP(4); CALL_NEXT_METHOD; }
}
[link = T3Mid, nick = sub]
-class T3Sub : T3Mid {
+class T3Sub: T3Mid {
void base.m0(?int z) { STEP(z); CALL_NEXT_METHOD; }
void base.m1(?int z) { STEP(z); CALL_NEXT_METHOD; }
}
-code c : tests {
+code c: tests {
prepare("kwargs");
{ SOD_DECL(T3Sub, t, NO_KWARGS);
T3Base_m0(t, KWARGS(K(z, 0) K(y, 1) K(x, 2)));