Changeset e7f8119


Ignore:
Timestamp:
Jun 10, 2019, 10:52:03 AM (5 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
6355ba7
Parents:
9856ca9 (diff), 61c7239 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

Files:
14 added
24 edited

Legend:

Unmodified
Added
Removed
  • doc/LaTeXmacros/common.tex

    r9856ca9 re7f8119  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Mon Jul  9 08:28:05 2018
    14 %% Update Count     : 380
     13%% Last Modified On : Fri May 24 07:59:54 2019
     14%% Update Count     : 382
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    6161\newlength{\gcolumnposn}                                % temporary hack because lstlisting does not handle tabs correctly
    6262\newlength{\columnposn}
    63 \setlength{\gcolumnposn}{2.5in}
     63\setlength{\gcolumnposn}{2.75in}
    6464\setlength{\columnposn}{\gcolumnposn}
    6565\newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@basicstyle{\LstCommentStyle{#2}}}}
  • doc/bibliography/pl.bib

    r9856ca9 re7f8119  
    25852585
    25862586@article{Tarjan75,
    2587  keywords = {union-find},
    2588  contributer = {a3moss@uwaterloo.ca},
    2589  author = {Tarjan, Robert Endre},
    2590  title = {Efficiency of a Good But Not Linear Set Union Algorithm},
    2591  journal = {J. ACM},
    2592  issue_date = {April 1975},
    2593  volume = {22},
    2594  number = {2},
    2595  month = apr,
    2596  year = {1975},
    2597  issn = {0004-5411},
    2598  pages = {215--225},
    2599  numpages = {11},
    2600  url = {http://doi.acm.org/10.1145/321879.321884},
    2601  doi = {10.1145/321879.321884},
    2602  acmid = {321884},
    2603  publisher = {ACM},
    2604  address = {New York, NY, USA},
     2587    keywords    = {union-find},
     2588    contributer = {a3moss@uwaterloo.ca},
     2589    author      = {Tarjan, Robert Endre},
     2590    title       = {Efficiency of a Good But Not Linear Set Union Algorithm},
     2591    journal     = {J. ACM},
     2592    issue_date  = {April 1975},
     2593    volume      = {22},
     2594    number      = {2},
     2595    month       = apr,
     2596    year        = {1975},
     2597    issn        = {0004-5411},
     2598    pages       = {215--225},
     2599    numpages    = {11},
     2600    url         = {http://doi.acm.org/10.1145/321879.321884},
     2601    doi         = {10.1145/321879.321884},
     2602    acmid       = {321884},
     2603    publisher   = {ACM},
     2604    address     = {New York, NY, USA},
    26052605}
    26062606
     
    26232623    journal     = ipl,
    26242624    year        = 1980,
    2625     month       = apr, volume = 10, number = 3, pages = {120-123},
     2625    month       = apr,
     2626    volume      = 10,
     2627    number      = 3,
     2628    pages        = {120-123},
    26262629    comment     = {
    26272630        The ``two-pass'' algorithm.  An upward pass over a parse tree
     
    26572660}
    26582661
    2659 @InProceedings{chambers89a,
     2662@inproceedings{chambers89a,
    26602663    keywords    = {maps, delegation},
    26612664    author      = "Craig Chambers and David Ungar and Elgin Lee",
    2662     title       = "An Efficient Implementation of {SELF}, a Dynamically-Typed
    2663                  Object-Oriented Language Based on Prototypes",
     2665    title       = "An Efficient Implementation of {SELF}, a Dynamically-Typed Object-Oriented Language Based on Prototypes",
    26642666    crossref    = "OOPSLA89",
    26652667    pages       = {49-70}
    26662668}
    26672669
     2670@misc{Turley99,
     2671    keywords    = {embedded system, micrprocessor},
     2672    contributer = {pabuhr@plg},
     2673    author      = {Jim Turley},
     2674    title       = {Embedded Processors by the Numbers},
     2675    year        = 1999,
     2676    month       = may,
     2677    note        = {Electronic Engineering Times},
     2678    howpublished= {\href{https://www.eetimes.com/author.asp?sectionid=36&doc_id=1287712}
     2679                  {https://\-www.eetimes.com/\-author.asp?sectionid=\-36&doc_id=1287712}},
     2680}
     2681
    26682682@article{oop:encapsulation,
    26692683    keywords    = {Encapsulation, Inheritance, Subclasses, Multiple Inheritance},
    26702684    contributer = {gjditchfield@plg},
    26712685    author      = {Alan Snyder},
    2672     title       = {Encapsulation and Inheritance in Object-Oriented Programming
    2673         Languages},
     2686    title       = {Encapsulation and Inheritance in Object-Oriented Programming Languages},
    26742687    journal     = sigplan,
    26752688    volume      = {21},    number = {11},
     
    27062719    title       = {Encapsulators: A New Software Paradigm in Smalltalk-80},
    27072720    journal     = sigplan,
    2708     volume      = {21},    number       = {11},
     2721    volume      = {21},
     2722    number      = {11},
    27092723    pages       = {341-346},
    2710     month       = nov, year = 1986,
     2724    month       = nov,
     2725    year        = 1986,
    27112726    comment     = {
    27122727        Encapsulators are objects that surround other objects.
  • doc/papers/concurrency/Makefile

    r9856ca9 re7f8119  
    2424
    2525PICTURES = ${addsuffix .pstex, \
     26FullProdConsStack \
     27FullCoroutinePhases \
     28corlayout \
    2629monitor \
    2730ext_monitor \
  • doc/papers/concurrency/Paper.tex

    r9856ca9 re7f8119  
    153153                auto, _Bool, catch, catchResume, choose, _Complex, __complex, __complex__, __const, __const__,
    154154                coroutine, disable, dtype, enable, exception, __extension__, fallthrough, fallthru, finally,
    155                 __float80, float80, __float128, float128, forall, ftype, _Generic, _Imaginary, __imag, __imag__,
     155                __float80, float80, __float128, float128, forall, ftype, generator, _Generic, _Imaginary, __imag, __imag__,
    156156                inline, __inline, __inline__, __int128, int128, __label__, monitor, mutex, _Noreturn, one_t, or,
    157                 otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, thread,
     157                otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, suspend, thread,
    158158                _Thread_local, throw, throwResume, timeout, trait, try, ttype, typeof, __typeof, __typeof__,
    159159                virtual, __volatile, __volatile__, waitfor, when, with, zero_t},
     
    231231}
    232232
     233\newbox\myboxA
     234\newbox\myboxB
     235\newbox\myboxC
     236\newbox\myboxD
     237
    233238\title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}}
    234239
     
    247252This paper discusses the design philosophy and implementation of its advanced control-flow and concurrent/parallel features, along with the supporting runtime.
    248253These features are created from scratch as ISO C has only low-level and/or unimplemented concurrency, so C programmers continue to rely on library features like C pthreads.
    249 \CFA introduces modern language-level control-flow mechanisms, like coroutines, user-level threading, and monitors for mutual exclusion and synchronization.
     254\CFA introduces modern language-level control-flow mechanisms, like generators, coroutines, user-level threading, and monitors for mutual exclusion and synchronization.
    250255Library extension for executors, futures, and actors are built on these basic mechanisms.
    251 The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and reducing monitor barging.
     256The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and optional monitor barging.
    252257The runtime also ensures multiple monitors can be safely acquired \emph{simultaneously} (deadlock free), and this feature is fully integrated with all monitor synchronization mechanisms.
    253 All language features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers.
     258All control-flow features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers.
    254259Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming-languages.
    255260}%
    256261
    257 \keywords{coroutines, concurrency, parallelism, threads, monitors, runtime, C, \CFA (Cforall)}
     262\keywords{generator, coroutine, concurrency, parallelism, thread, monitor, runtime, C, \CFA (Cforall)}
    258263
    259264
     
    285290As a result, languages like Java, Scala~\cite{Scala}, Objective-C~\cite{obj-c-book}, \CCeleven~\cite{C11}, and C\#~\cite{Csharp} adopt the 1:1 kernel-threading model, with a variety of presentation mechanisms.
    286291From 2000 onwards, languages like Go~\cite{Go}, Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, D~\cite{D}, and \uC~\cite{uC++,uC++book} have championed the M:N user-threading model, and many user-threading libraries have appeared~\cite{Qthreads,MPC,BoostThreads}, including putting green threads back into Java~\cite{Quasar}.
    287 The main argument for user-level threading is that they are lighter weight than kernel threads (locking and context switching do not cross the kernel boundary), so there is less restriction on programming styles that encourage large numbers of threads performing smaller work-units to facilitate load balancing by the runtime~\cite{Verch12}.
     292The main argument for user-level threading is that they are lighter weight than kernel threads (locking and context switching do not cross the kernel boundary), so there is less restriction on programming styles that encourage large numbers of threads performing medium work-units to facilitate load balancing by the runtime~\cite{Verch12}.
    288293As well, user-threading facilitates a simpler concurrency approach using thread objects that leverage sequential patterns versus events with call-backs~\cite{vonBehren03}.
    289294Finally, performant user-threading implementations (both time and space) are largely competitive with direct kernel-threading implementations, while achieving the programming advantages of high concurrency levels and safety.
     
    300305
    301306Finally, it is important for a language to provide safety over performance \emph{as the default}, allowing careful reduction of safety for performance when necessary.
    302 Two concurrency violations of this philosophy are \emph{spurious wakeup} and \emph{barging}, i.e., random wakeup~\cite[\S~8]{Buhr05a} and signalling-as-hints~\cite[\S~8]{Buhr05a}, where one begats the other.
    303 If you believe spurious wakeup is a foundational concurrency property, than unblocking (signalling) a thread is always a hint.
    304 If you \emph{do not} believe spurious wakeup is foundational, than signalling-as-hints is a performance decision.
    305 Most importantly, removing spurious wakeup and signals-as-hints makes concurrent programming significantly safer because it removes local non-determinism.
    306 Clawing back performance where the local non-determinism is unimportant, should be an option not the default.
     307Two concurrency violations of this philosophy are \emph{spurious wakeup} and \emph{barging}, i.e., random wakeup~\cite[\S~8]{Buhr05a} and signals-as-hints~\cite[\S~8]{Buhr05a}, where one is a consequence of the other, i.e., once there is spurious wakeup, signals-as-hints follows.
     308However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~8]{Buhr05a}, it is a performance design choice.
     309Similarly, signals-as-hints is also a performance decision.
     310We argue removing spurious wakeup and signals-as-hints makes concurrent programming significantly safer because it removes local non-determinism and matches with programmer expectation.
     311(Experience by the authors teaching concurrency is that students are highly confused by these semantics.)
     312Clawing back performance, when local non-determinism is unimportant, should be an option not the default.
    307313
    308314\begin{comment}
     
    327333
    328334\CFA embraces user-level threading, language extensions for advanced control-flow, and safety as the default.
    329 We present comparative examples so the reader can judge if the \CFA control-flow extensions are better and safer than those in or proposed for \Celeven, \CC and other concurrent, imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms.
     335We present comparative examples so the reader can judge if the \CFA control-flow extensions are better and safer than those in or proposed for \Celeven, \CC, and other concurrent, imperative programming-languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms.
    330336The main contributions of this work are:
    331337\begin{itemize}
    332338\item
    333 expressive language-level coroutines and user-level threading, which respect the expectations of C programmers.
     339language-level generators, coroutines and user-level threading, which respect the expectations of C programmers.
    334340\item
    335 monitor synchronization without barging.
    336 \item
    337 safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating this capability with all monitor synchronization mechanisms.
     341monitor synchronization without barging, and the ability to safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating these capability with all monitor synchronization mechanisms.
    338342\item
    339343providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features.
     
    343347a runtime system with no spurious wakeup.
    344348\item
    345 experimental results showing comparable performance of the new features with similar mechanisms in other concurrent programming-languages.
     349a dynamic partitioning mechanism to segregate the execution environment for specialized requirements.
     350\item
     351a non-blocking I/O library
     352\item
     353experimental results showing comparable performance of the new features with similar mechanisms in other programming languages.
    346354\end{itemize}
    347355
     356
     357\section{Stateful Function}
     358
     359The generator/coroutine provides a stateful function, which is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}.
     360A stateful function allows execution to be temporarily suspended and later resumed, e.g., plugin, device driver, finite-state machine.
     361Hence, a stateful function may not end when it returns to its caller, allowing it to be restarted with the data and execution location present at the point of suspension.
     362This capability is accomplished by retaining a data/execution \emph{closure} between invocations.
     363If the closure is fixed size, we call it a \emph{generator} (or \emph{stackless}), and its control flow is restricted, e.g., suspending outside the generator is prohibited.
     364If the closure is variable sized, we call it a \emph{coroutine} (or \emph{stackful}), and as the names implies, often implemented with a separate stack with no programming restrictions.
     365Hence, refactoring a stackless coroutine may require changing it to stackful.
     366A foundational property of all \emph{stateful functions} is that resume/suspend \emph{do not} cause incremental stack growth, i.e., resume/suspend operations are remembered through the closure not the stack.
     367As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles).
     368A fixed closure activated by modified call/return is faster than a variable closure activated by context switching.
     369Additionally, any storage management for the closure (especially in unmanaged languages, i.e., no garbage collection) must also be factored into design and performance.
     370Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general.
     371Note, creation cost is amortized across usage, so activation cost is usually the dominant factor.
     372
     373
     374\begin{figure}
     375\centering
     376\begin{lrbox}{\myboxA}
     377\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     378typedef struct {
     379        int fn1, fn;
     380} Fib;
     381#define FibCtor { 1, 0 }
     382int fib( Fib * f ) {
     383
     384
     385
     386        int fn = f->fn; f->fn = f->fn1;
     387                f->fn1 = f->fn + fn;
     388        return fn;
     389
     390}
     391int main() {
     392        Fib f1 = FibCtor, f2 = FibCtor;
     393        for ( int i = 0; i < 10; i += 1 )
     394                printf( "%d %d\n",
     395                           fib( &f1 ), fib( &f2 ) );
     396}
     397\end{cfa}
     398\end{lrbox}
     399
     400\begin{lrbox}{\myboxB}
     401\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     402`generator` Fib {
     403        int fn1, fn;
     404};
     405
     406void `main(Fib & fib)` with(fib) {
     407
     408        [fn1, fn] = [1, 0];
     409        for () {
     410                `suspend;`
     411                [fn1, fn] = [fn, fn + fn1];
     412
     413        }
     414}
     415int main() {
     416        Fib f1, f2;
     417        for ( 10 )
     418                sout | `resume( f1 )`.fn
     419                         | `resume( f2 )`.fn;
     420}
     421\end{cfa}
     422\end{lrbox}
     423
     424\begin{lrbox}{\myboxC}
     425\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     426typedef struct {
     427        int fn1, fn;  void * `next`;
     428} Fib;
     429#define FibCtor { 1, 0, NULL }
     430Fib * comain( Fib * f ) {
     431        if ( f->next ) goto *f->next;
     432        f->next = &&s1;
     433        for ( ;; ) {
     434                return f;
     435          s1:; int fn = f->fn + f->fn1;
     436                        f->fn1 = f->fn; f->fn = fn;
     437        }
     438}
     439int main() {
     440        Fib f1 = FibCtor, f2 = FibCtor;
     441        for ( int i = 0; i < 10; i += 1 )
     442                printf("%d %d\n",comain(&f1)->fn,
     443                                 comain(&f2)->fn);
     444}
     445\end{cfa}
     446\end{lrbox}
     447
     448\subfloat[C asymmetric generator]{\label{f:CFibonacci}\usebox\myboxA}
     449\hspace{3pt}
     450\vrule
     451\hspace{3pt}
     452\subfloat[\CFA asymmetric generator]{\label{f:CFAFibonacciGen}\usebox\myboxB}
     453\hspace{3pt}
     454\vrule
     455\hspace{3pt}
     456\subfloat[C generator implementation]{\label{f:CFibonacciSim}\usebox\myboxC}
     457\caption{Fibonacci (output) Asymmetric Generator}
     458\label{f:FibonacciAsymmetricGenerator}
     459
     460\bigskip
     461
     462\begin{lrbox}{\myboxA}
     463\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     464`generator Fmt` {
     465        char ch;
     466        int g, b;
     467};
     468void ?{}( Fmt & fmt ) { `resume(fmt);` } // constructor
     469void ^?{}( Fmt & f ) with(f) { $\C[1.75in]{// destructor}$
     470        if ( g != 0 || b != 0 ) sout | nl; }
     471void `main( Fmt & f )` with(f) {
     472        for () { $\C{// until destructor call}$
     473                for ( ; g < 5; g += 1 ) { $\C{// groups}$
     474                        for ( ; b < 4; b += 1 ) { $\C{// blocks}$
     475                                `suspend;` $\C{// wait for character}$
     476                                while ( ch == '\n' ) `suspend;` // ignore
     477                                sout | ch;                                              // newline
     478                        } sout | " ";  // block spacer
     479                } sout | nl; // group newline
     480        }
     481}
     482int main() {
     483        Fmt fmt; $\C{// fmt constructor called}$
     484        for () {
     485                sin | fmt.ch; $\C{// read into generator}$
     486          if ( eof( sin ) ) break;
     487                `resume( fmt );`
     488        }
     489
     490} $\C{// fmt destructor called}\CRT$
     491\end{cfa}
     492\end{lrbox}
     493
     494\begin{lrbox}{\myboxB}
     495\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     496typedef struct {
     497        void * next;
     498        char ch;
     499        int g, b;
     500} Fmt;
     501void comain( Fmt * f ) {
     502        if ( f->next ) goto *f->next;
     503        f->next = &&s1;
     504        for ( ;; ) {
     505                for ( f->g = 0; f->g < 5; f->g += 1 ) {
     506                        for ( f->b = 0; f->b < 4; f->b += 1 ) {
     507                                return;
     508                          s1:;  while ( f->ch == '\n' ) return;
     509                                printf( "%c", f->ch );
     510                        } printf( " " );
     511                } printf( "\n" );
     512        }
     513}
     514int main() {
     515        Fmt fmt = { NULL };  comain( &fmt ); // prime
     516        for ( ;; ) {
     517                scanf( "%c", &fmt.ch );
     518          if ( feof( stdin ) ) break;
     519                comain( &fmt );
     520        }
     521        if ( fmt.g != 0 || fmt.b != 0 ) printf( "\n" );
     522}
     523\end{cfa}
     524\end{lrbox}
     525
     526\subfloat[\CFA asymmetric generator]{\label{f:CFAFormatGen}\usebox\myboxA}
     527\hspace{3pt}
     528\vrule
     529\hspace{3pt}
     530\subfloat[C generator simulation]{\label{f:CFormatSim}\usebox\myboxB}
     531\hspace{3pt}
     532\caption{Formatter (input) Asymmetric Generator}
     533\label{f:FormatterAsymmetricGenerator}
     534\end{figure}
     535
     536
     537\subsection{Generator}
     538
     539Stackless generators have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution.
     540The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity.
     541How this goal is accomplished is done through a series of different kinds of generators and their implementation.
     542
     543Figure~\ref{f:FibonacciAsymmetricGenerator} shows an unbounded asymmetric generator for an infinite sequence of Fibonacci numbers written in C and \CFA, with a simple C implementation for the \CFA version.
     544This kind of generator is an \emph{output generator}, producing a new result on each resumption.
     545To compute Fibonacci, the previous two values in the sequence are retained to generate the next value, \ie @fn1@ and @fn@, plus the execution location where control restarts when the generator is resumed, \ie top or middle.
     546An additional requirement is the ability to create an arbitrary number of generators (of any kind), \ie retaining state in global variables is insufficient;
     547hence, state is retained in a closure between calls.
     548Figure~\ref{f:CFibonacci} shows the C approach of manually creating the closure in structure @Fib@, and multiple instances of this closure provide multiple Fibonacci generators.
     549The C version only has the middle execution state because the top execution state becomes declaration initialization.
     550Figure~\ref{f:CFAFibonacciGen} shows the \CFA approach, which also has a manual closure, but replaces the structure with a special \CFA @generator@ type.
     551This generator type is then connected to a function named @main@ that takes as its only parameter a reference to the generator type, called a \emph{generator main}.
     552The generator main contains @suspend@ statements that suspend execution without ending the generator versus @return@.
     553For the Fibonacci generator-main,\footnote{
     554The \CFA \lstinline|with| opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics.
     555Multiple aggregates may be opened.}
     556the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@.
     557Any local variables in @main@ \emph{are not retained} between calls;
     558hence local variable are only for temporary computations \emph{between} suspends.
     559All retained state \emph{must} appear in the generator's type.
     560As well, generator code containing a @suspend@ cannot be refactored into a helper function called by the generator, because @suspend@ is implemented via @return@, so a return from the helper function goes back to the current generator not the resumer.
     561The generator is started by calling function @resume@ with a generator instance, which begins execution at the top of the generator main, and subsequent @resume@ calls restart the generator at its point of last suspension.
     562Resuming an ended (returned) generator is undefined.
     563Function @resume@ returns its argument generator so it can be cascaded in an expression, in this case to print the next Fibonacci value @fn@ computed in the generator instance.
     564Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA generator only needs one additional field, @next@, to handle retention of execution state.
     565The computed @goto@ at the start of the generator main, which branches after the previous suspend, adds very little cost to the resume call.
     566Finally, an explicit generator type provides both design and performance benefits, such as multiple type-safe interface functions taking and returning arbitrary types.
     567\begin{cfa}
     568int ?()( Fib & fib ) with( fib ) { return `resume( fib )`.fn; }   // function-call interface
     569int ?()( Fib & fib, int N ) with( fib ) { for ( N - 1 ) `fib()`; return `fib()`; }   // use simple interface
     570double ?()( Fib & fib ) with( fib ) { return (int)`fib()` / 3.14159; } // cast prevents recursive call
     571sout | (int)f1() | (double)f1() | f2( 2 );   // simple interface, cast selects call based on return type, step 2 values
     572\end{cfa}
     573Now, the generator can be a separately-compiled opaque-type only accessed through its interface functions.
     574For contrast, Figure~\ref{f:PythonFibonacci} shows the equivalent Python Fibonacci generator, which does not use a generator type, and hence only has a single interface, but an implicit closure.
     575
     576Having to manually create the generator closure by moving local-state variables into the generator type is an additional programmer burden and possible point of error.
     577(This restriction is removed by the coroutine, see Section~\ref{s:Coroutine}.)
     578Our concern is that static analysis to discriminate local state from temporary variables is complex and beyond the current scope of the \CFA project.
     579As well, supporting variable-size local-state, like variable-length arrays, requires dynamic allocation of the local state, which significantly increases the cost of generator creation/destruction, and is a show-stopper for embedded programming.
     580But more importantly, the size of the generator type is tied to the local state in the generator main, which precludes separate compilation of the generator main, \ie a generator must be inlined or local state must be dynamically allocated.
     581Our current experience is that most generator problems have simple data state, including local state, but complex execution state.
     582As well, C programmers are not afraid with this kind of semantic programming requirement, if it results in very small, fast generators.
     583
     584Figure~\ref{f:CFAFormatGen} shows an asymmetric \newterm{input generator}, @Fmt@, for restructuring text into groups of characters of fixed-size blocks, \ie the input on the left is reformatted into the output on the right, where newlines are ignored.
     585\begin{center}
     586\tt
     587\begin{tabular}{@{}l|l@{}}
     588\multicolumn{1}{c|}{\textbf{\textrm{input}}} & \multicolumn{1}{c}{\textbf{\textrm{output}}} \\
     589\begin{tabular}[t]{@{}ll@{}}
     590abcdefghijklmnopqrstuvwxyz \\
     591abcdefghijklmnopqrstuvwxyz
     592\end{tabular}
     593&
     594\begin{tabular}[t]{@{}lllll@{}}
     595abcd    & efgh  & ijkl  & mnop  & qrst  \\
     596uvwx    & yzab  & cdef  & ghij  & klmn  \\
     597opqr    & stuv  & wxyz  &               &
     598\end{tabular}
     599\end{tabular}
     600\end{center}
     601The example takes advantage of resuming a generator in the constructor to prime the loops so the first character sent for formatting appears inside the nested loops.
     602The destructor provides a newline, if formatted text ends with a full line.
     603Figure~\ref{f:CFormatSim} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@.
     604For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the Fibonacci generator.
     605
     606Figure~\ref{f:DeviceDriverGen} shows a \emph{killer} asymmetric generator, a device-driver, because device drivers caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}.
     607Device drives follow the pattern of simple data state but complex execution state, \ie finite state-machine (FSM) parsing a protocol.
     608For example, the following protocol:
     609\begin{center}
     610\ldots\, STX \ldots\, message \ldots\, ESC ETX \ldots\, message \ldots\, ETX 2-byte crc \ldots
     611\end{center}
     612is a network message beginning with the control character STX, ending with an ETX, and followed by a 2-byte cyclic-redundancy check.
     613Control characters may appear in a message if preceded by an ESC.
     614When a message byte arrives, it triggers an interrupt, and the operating system services the interrupt by calling the device driver with the byte read from a hardware register.
     615The device driver returns a status code of its current state, and when a complete message is obtained, the operating system knows the message is in the message buffer.
     616Hence, the device driver is an input/output generator.
     617
     618Note, the cost of creating and resuming the device-driver generator, @Driver@, is virtually identical to call/return, so performance in an operating-system kernel is excellent.
     619As well, the data state is small, where variables @byte@ and @msg@ are communication variables for passing in message bytes and returning the message, and variables @lnth@, @crc@, and @sum@ are local variable that must be retained between calls and are hoisted into the generator type.
     620Manually, detecting and hoisting local-state variables is easy when the number is small.
     621Finally, the execution state is large, with one @resume@ and seven @suspend@s.
     622Hence, the key benefits of the generator are correctness, safety, and maintenance because the execution states of the FSM are transcribed directly into the programming language rather than using a table-driven approach.
     623Because FSMs can be complex and occur frequently in important domains, direct support of the generator is crucial in a systems programming-language.
     624
     625\begin{figure}
     626\centering
     627\newbox\myboxA
     628\begin{lrbox}{\myboxA}
     629\begin{python}[aboveskip=0pt,belowskip=0pt]
     630def Fib():
     631    fn1, fn = 0, 1
     632    while True:
     633        `yield fn1`
     634        fn1, fn = fn, fn1 + fn
     635f1 = Fib()
     636f2 = Fib()
     637for i in range( 10 ):
     638        print( next( f1 ), next( f2 ) )
     639
     640
     641
     642
     643
     644
     645\end{python}
     646\end{lrbox}
     647
     648\newbox\myboxB
     649\begin{lrbox}{\myboxB}
     650\begin{python}[aboveskip=0pt,belowskip=0pt]
     651def Fmt():
     652        try:
     653                while True:
     654                        for g in range( 5 ):
     655                                for b in range( 4 ):
     656                                        print( `(yield)`, end='' )
     657                                print( '  ', end='' )
     658                        print()
     659        except GeneratorExit:
     660                if g != 0 | b != 0:
     661                        print()
     662fmt = Fmt()
     663`next( fmt )`                    # prime, next prewritten
     664for i in range( 41 ):
     665        `fmt.send( 'a' );`      # send to yield
     666\end{python}
     667\end{lrbox}
     668\subfloat[Fibonacci]{\label{f:PythonFibonacci}\usebox\myboxA}
     669\hspace{3pt}
     670\vrule
     671\hspace{3pt}
     672\subfloat[Formatter]{\label{f:PythonFormatter}\usebox\myboxB}
     673\caption{Python Generator}
     674\label{f:PythonGenerator}
     675
     676\bigskip
     677
     678\begin{tabular}{@{}l|l@{}}
     679\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     680enum Status { CONT, MSG, ESTX,
     681                                ELNTH, ECRC };
     682`generator` Driver {
     683        Status status;
     684        unsigned char byte, * msg; // communication
     685        unsigned int lnth, sum;      // local state
     686        unsigned short int crc;
     687};
     688void ?{}( Driver & d, char * m ) { d.msg = m; }
     689Status next( Driver & d, char b ) with( d ) {
     690        byte = b; `resume( d );` return status;
     691}
     692void main( Driver & d ) with( d ) {
     693        enum { STX = '\002', ESC = '\033',
     694                        ETX = '\003', MaxMsg = 64 };
     695  msg: for () { // parse message
     696                status = CONT;
     697                lnth = 0; sum = 0;
     698                while ( byte != STX ) `suspend;`
     699          emsg: for () {
     700                        `suspend;` // process byte
     701\end{cfa}
     702&
     703\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     704                        choose ( byte ) { // switch with implicit break
     705                          case STX:
     706                                status = ESTX; `suspend;` continue msg;
     707                          case ETX:
     708                                break emsg;
     709                          case ESC:
     710                                `suspend;`
     711                        }
     712                        if ( lnth >= MaxMsg ) { // buffer full ?
     713                                status = ELNTH; `suspend;` continue msg; }
     714                        msg[lnth++] = byte;
     715                        sum += byte;
     716                }
     717                msg[lnth] = '\0'; // terminate string
     718                `suspend;`
     719                crc = byte << 8;
     720                `suspend;`
     721                status = (crc | byte) == sum ? MSG : ECRC;
     722                `suspend;`
     723        }
     724}
     725\end{cfa}
     726\end{tabular}
     727\caption{Device-driver generator for communication protocol}
     728\label{f:DeviceDriverGen}
     729\end{figure}
     730
     731Figure~\ref{f:CFAPingPongGen} shows a symmetric generator, where the generator resumes another generator, forming a resume/resume cycle.
     732(The trivial cycle is a generator resuming itself.)
     733This control flow is similar to recursion for functions, but without stack growth.
     734The steps for symmetric control-flow are creating, executing, and terminating the cycle.
     735Constructing the cycle must deal with definition-before-use to close the cycle, \ie, the first generator must know about the last generator, which is not within scope.
     736(This issues occurs for any cyclic data-structure.)
     737% The example creates all the generators and then assigns the partners that form the cycle.
     738% Alternatively, the constructor can assign the partners as they are declared, except the first, and the first-generator partner is set after the last generator declaration to close the cycle.
     739Once the cycle is formed, the program main resumes one of the generators, and the generators can then traverse an arbitrary cycle using @resume@ to activate partner generator(s).
     740Terminating the cycle is accomplished by @suspend@ or @return@, both of which go back to the stack frame that started the cycle (program main in the example).
     741The starting stack-frame is below the last active generator because the resume/resume cycle does not grow the stack.
     742Also, since local variables are not retained in the generator function, it does not contain an objects with destructors that must be called, so the  cost is the same as a function return.
     743Destructor cost occurs when the generator instance is deallocated, which is easily controlled by the programmer.
     744
     745Figure~\ref{f:CPingPongSim} shows the implementation of the symmetric generator, where the complexity is the @resume@, which needs an extension to the calling convention to perform a forward rather than backward jump.
     746This jump starts at the top of the next generator main to re-execute the normal calling convention to make space on the stack for its local variables.
     747However, before the jump, the caller must reset its stack (and any registers) equivalent to a @return@, but subsequently jump forward not backwards.
     748This semantics is basically a tail-call optimization, which compilers already perform.
     749Hence, assembly code is manually insert in the example to undo the generator's entry code before the direct jump.
     750This assembly code is fragile as it depends on what entry code is generated, specifically if there are local variables and the level of optimization.
     751To provide this new calling convention requires a mechanism built into the compiler, which is beyond the scope of \CFA at this time.
     752Nevertheless, it is possible to hand generate any symmetric generators for proof of concept and performance testing.
     753A compiler could also eliminate other artifacts in the generator simulation to further increase performance.
     754
     755\begin{figure}
     756\centering
     757\begin{lrbox}{\myboxA}
     758\begin{cfa}[aboveskip=0pt,belowskip=0pt]
     759`generator PingPong` {
     760        const char * name;
     761        PingPong & partner; // rebindable reference
     762        int N, i;
     763};
     764void ?{}(PingPong & pp, char * nm, int N) with(pp) {
     765        name = nm;  partner = 0p;  pp.N = N;  i = 0; }
     766void `main( PingPong & pp )` with(pp) {
     767        for ( ; i < N; i += 1 ) {
     768                sout | name | i;
     769                `resume( partner );`
     770        }
     771}
     772int main() {
     773        enum { N = 5 };
     774        PingPong ping = {"ping", N}, pong = {"pong", N};
     775        &ping.partner = &pong;  &pong.partner = &ping;
     776        `resume( ping );`
     777}
     778\end{cfa}
     779\end{lrbox}
     780
     781\begin{lrbox}{\myboxB}
     782\begin{cfa}[escapechar={},aboveskip=0pt,belowskip=0pt]
     783typedef struct PingPong {
     784        const char * name;
     785        struct PingPong * partner;
     786        int N, i;
     787        void * next;
     788} PingPong;
     789#define PPCtor(name, N) { name, NULL, N, 0, NULL }
     790void comain( PingPong * pp ) {
     791        if ( pp->next ) goto *pp->next;
     792        pp->next = &&cycle;
     793        for ( ; pp->i < pp->N; pp->i += 1 ) {
     794                printf( "%s %d\n", pp->name, pp->i );
     795                asm( "mov  %0,%%rdi" : "=m" (pp->partner) );
     796                asm( "mov  %rdi,%rax" );
     797                asm( "popq %rbx" );
     798                asm( "jmp  comain" );
     799          cycle: ;
     800        }
     801}
     802\end{cfa}
     803\end{lrbox}
     804
     805\subfloat[\CFA symmetric generator]{\label{f:CFAPingPongGen}\usebox\myboxA}
     806\hspace{3pt}
     807\vrule
     808\hspace{3pt}
     809\subfloat[C generator simulation]{\label{f:CPingPongSim}\usebox\myboxB}
     810\hspace{3pt}
     811\caption{Ping-Pong Symmetric Generator}
     812\label{f:PingPongSymmetricGenerator}
     813\end{figure}
     814
     815Finally, part of this generator work was inspired by the recent \CCtwenty generator proposal~\cite{C++20Coroutine19} (which they call coroutines).
     816Our work provides the same high-performance asymmetric-generators as \CCtwenty, and extends their work with symmetric generators.
     817An additional \CCtwenty generator feature allows @suspend@ and @resume@ to be followed by a restricted compound-statement that is executed after the current generator has reset its stack but before calling the next generator, specified with the following \CFA syntax.
     818\begin{cfa}
     819... suspend`{ ... }`;
     820... resume( C )`{ ... }` ...
     821\end{cfa}
     822Since the current generator's stack is released before calling the compound statement, the compound statement can only reference variables in the generator's type.
     823This feature is useful when a generator is used in a concurrent context to ensure it is stopped before releasing a lock in the compound statement, which might immediately allow another thread to resume the generator.
     824Hence, this mechanism provides a general and safe handoff of the generator among competing threads.
     825
     826
     827\subsection{Coroutine}
     828\label{s:Coroutine}
     829
     830Stackful coroutines extend generator semantics, \ie there is an implicit closure and @suspend@ may appear in a helper function called from the coroutine main.
     831A coroutine is specified by replacing @generator@ with @coroutine@ for the type.
     832This generality results in higher cost for creation, due to dynamic stack allocation, execution, due to context switching among stacks, and terminating, due to possible stack unwinding and dynamic stack deallocation.
     833How coroutines differ from generators is done through a series of examples.
     834
     835First, the previous generator examples are converted to their coroutine counterparts, allowing local-state variables to be moved from the generator type into the coroutine main.
     836\begin{description}
     837\item[Fibonacci]
     838Move the declaration of @fn1@ to the start of coroutine main.
     839\begin{cfa}[xleftmargin=0pt]
     840void main( Fib & fib ) with(fib) {
     841        `int fn1;`
     842\end{cfa}
     843\item[Formatter]
     844Move the declaration of @g@ and @b@ to the for loops in the coroutine main.
     845\begin{cfa}[xleftmargin=0pt]
     846for ( `g`; 5 ) {
     847        for ( `b`; 4 ) {
     848\end{cfa}
     849\item[Device Driver]
     850Move the declaration of @lnth@ and @sum@ to their points of initialization.
     851\begin{cfa}[xleftmargin=0pt]
     852        status = CONT;
     853        `unsigned int lnth = 0, sum = 0;`
     854        ...
     855        `unsigned short int crc = byte << 8;`
     856\end{cfa}
     857\item[PingPong]
     858Move the declaration of @i@ to the for loop in the coroutine main.
     859\begin{cfa}[xleftmargin=0pt]
     860void main( PingPong & pp ) with(pp) {
     861        for ( `i`; N ) {
     862\end{cfa}
     863\end{description}
     864It is also possible to refactor code containing local-state and @suspend@ statements into a helper routine, like the computation of the CRC for the device driver.
     865\begin{cfa}
     866unsigned int Crc() {
     867        `suspend;`
     868        unsigned short int crc = byte << 8;
     869        `suspend;`
     870        status = (crc | byte) == sum ? MSG : ECRC;
     871        return crc;
     872}
     873\end{cfa}
     874A call to this function is placed at the end of the driver's coroutine-main.
     875For complex finite-state machines, refactoring is part of normal program abstraction, especially when code is used in multiple places.
     876Again, this complexity is usually associated with execution state rather than data state.
     877
    348878\begin{comment}
    349 This paper provides a minimal concurrency \newterm{Application Program Interface} (API) that is simple, efficient and can be used to build other concurrency features.
    350 While the simplest concurrency system is a thread and a lock, this low-level approach is hard to master.
    351 An easier approach for programmers is to support higher-level constructs as the basis of concurrency.
    352 Indeed, for highly-productive concurrent-programming, high-level approaches are much more popular~\cite{Hochstein05}.
    353 Examples of high-level approaches are jobs (thread pool)~\cite{TBB}, implicit threading~\cite{OpenMP}, monitors~\cite{Java}, channels~\cite{CSP,Go}, and message passing~\cite{Erlang,MPI}.
    354 
    355 The following terminology is used.
    356 A \newterm{thread} is a fundamental unit of execution that runs a sequence of code and requires a stack to maintain state.
    357 Multiple simultaneous threads give rise to \newterm{concurrency}, which requires locking to ensure access to shared data and safe communication.
    358 \newterm{Locking}, and by extension \newterm{locks}, are defined as a mechanism to prevent progress of threads to provide safety.
    359 \newterm{Parallelism} is running multiple threads simultaneously.
    360 Parallelism implies \emph{actual} simultaneous execution, where concurrency only requires \emph{apparent} simultaneous execution.
    361 As such, parallelism only affects performance, which is observed through differences in space and/or time at runtime.
    362 Hence, there are two problems to be solved: concurrency and parallelism.
    363 While these two concepts are often combined, they are distinct, requiring different tools~\cite[\S~2]{Buhr05a}.
    364 Concurrency tools handle mutual exclusion and synchronization, while parallelism tools handle performance, cost, and resource utilization.
    365 
    366 The proposed concurrency API is implemented in a dialect of C, called \CFA (pronounced C-for-all).
    367 The paper discusses how the language features are added to the \CFA translator with respect to parsing, semantics, and type checking, and the corresponding high-performance runtime-library to implement the concurrent features.
    368 \end{comment}
    369 
    370 
    371 \begin{comment}
    372 \section{\CFA Overview}
    373 
    374 The following is a quick introduction to the \CFA language, specifically tailored to the features needed to support concurrency.
    375 Extended versions and explanation of the following code examples are available at the \CFA website~\cite{Cforall} or in Moss~\etal~\cite{Moss18}.
    376 
    377 \CFA is a non-object-oriented extension of ISO-C, and hence, supports all C paradigms.
    378 Like C, the building blocks of \CFA are structures and routines.
    379 Virtually all of the code generated by the \CFA translator respects C memory layouts and calling conventions.
    380 While \CFA is not object oriented, lacking the concept of a receiver (\eg @this@) and nominal inheritance-relationships, C has a notion of objects: ``region of data storage in the execution environment, the contents of which can represent values''~\cite[3.15]{C11}.
    381 While some object-oriented features appear in \CFA, they are independent capabilities, allowing \CFA to adopt them while maintaining a procedural paradigm.
    382 
    383 
    384 \subsection{References}
    385 
    386 \CFA provides multi-level rebindable references, as an alternative to pointers, which significantly reduces syntactic noise.
    387 \begin{cfa}
    388 int x = 1, y = 2, z = 3;
    389 int * p1 = &x, ** p2 = &p1,  *** p3 = &p2,      $\C{// pointers to x}$
    390     `&` r1 = x,   `&&` r2 = r1,   `&&&` r3 = r2;        $\C{// references to x}$
    391 int * p4 = &z, `&` r4 = z;
    392 
    393 *p1 = 3; **p2 = 3; ***p3 = 3;       // change x
    394 r1 =  3;     r2 = 3;      r3 = 3;        // change x: implicit dereferences *r1, **r2, ***r3
    395 **p3 = &y; *p3 = &p4;                // change p1, p2
    396 `&`r3 = &y; `&&`r3 = &`&`r4;             // change r1, r2: cancel implicit dereferences (&*)**r3, (&(&*)*)*r3, &(&*)r4
    397 \end{cfa}
    398 A reference is a handle to an object, like a pointer, but is automatically dereferenced the specified number of levels.
    399 Referencing (address-of @&@) a reference variable cancels one of the implicit dereferences, until there are no more implicit references, after which normal expression behaviour applies.
    400 
    401 
    402 \subsection{\texorpdfstring{\protect\lstinline{with} Statement}{with Statement}}
    403 \label{s:WithStatement}
    404 
    405 Heterogeneous data is aggregated into a structure/union.
    406 To reduce syntactic noise, \CFA provides a @with@ statement (see Pascal~\cite[\S~4.F]{Pascal}) to elide aggregate field-qualification by opening a scope containing the field identifiers.
    407 \begin{cquote}
    408 \vspace*{-\baselineskip}%???
    409 \lstDeleteShortInline@%
    410 \begin{cfa}
    411 struct S { char c; int i; double d; };
    412 struct T { double m, n; };
    413 // multiple aggregate parameters
    414 \end{cfa}
    415 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}|@{\hspace{2\parindentlnth}}l@{}}
    416 \begin{cfa}
    417 void f( S & s, T & t ) {
    418         `s.`c; `s.`i; `s.`d;
    419         `t.`m; `t.`n;
    420 }
    421 \end{cfa}
    422 &
    423 \begin{cfa}
    424 void f( S & s, T & t ) `with ( s, t )` {
    425         c; i; d;                // no qualification
    426         m; n;
    427 }
    428 \end{cfa}
    429 \end{tabular}
    430 \lstMakeShortInline@%
    431 \end{cquote}
    432 Object-oriented programming languages only provide implicit qualification for the receiver.
    433 
    434 In detail, the @with@-statement syntax is:
    435 \begin{cfa}
    436 $\emph{with-statement}$:
    437         'with' '(' $\emph{expression-list}$ ')' $\emph{compound-statement}$
    438 \end{cfa}
    439 and may appear as the body of a routine or nested within a routine body.
    440 Each expression in the expression-list provides a type and object.
    441 The type must be an aggregate type.
    442 (Enumerations are already opened.)
    443 The object is the implicit qualifier for the open structure-fields.
    444 All expressions in the expression list are opened in parallel within the compound statement, which is different from Pascal, which nests the openings from left to right.
    445 
    446 
    447 \subsection{Overloading}
    448 
    449 \CFA maximizes the ability to reuse names via overloading to aggressively address the naming problem.
    450 Both variables and routines may be overloaded, where selection is based on number and types of returns and arguments (as in Ada~\cite{Ada}).
    451 \newpage
    452 \vspace*{-2\baselineskip}%???
    453 \begin{cquote}
    454 \begin{cfa}
    455 // selection based on type
    456 \end{cfa}
    457 \lstDeleteShortInline@%
    458 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}|@{\hspace{2\parindentlnth}}l@{}}
    459 \begin{cfa}
    460 const short int `MIN` = -32768;
    461 const int `MIN` = -2147483648;
    462 const long int `MIN` = -9223372036854775808L;
    463 \end{cfa}
    464 &
    465 \begin{cfa}
    466 short int si = `MIN`;
    467 int i = `MIN`;
    468 long int li = `MIN`;
    469 \end{cfa}
    470 \end{tabular}
    471 \begin{cfa}
    472 // selection based on type and number of parameters
    473 \end{cfa}
    474 \begin{tabular}{@{}l@{\hspace{2.7\parindentlnth}}|@{\hspace{2\parindentlnth}}l@{}}
    475 \begin{cfa}
    476 void `f`( void );
    477 void `f`( char );
    478 void `f`( int, double );
    479 \end{cfa}
    480 &
    481 \begin{cfa}
    482 `f`();
    483 `f`( 'a' );
    484 `f`( 3, 5.2 );
    485 \end{cfa}
    486 \end{tabular}
    487 \begin{cfa}
    488 // selection based on type and number of returns
    489 \end{cfa}
    490 \begin{tabular}{@{}l@{\hspace{2\parindentlnth}}|@{\hspace{2\parindentlnth}}l@{}}
    491 \begin{cfa}
    492 char `f`( int );
    493 double `f`( int );
    494 [char, double] `f`( int );
    495 \end{cfa}
    496 &
    497 \begin{cfa}
    498 char c = `f`( 3 );
    499 double d = `f`( 3 );
    500 [d, c] = `f`( 3 );
    501 \end{cfa}
    502 \end{tabular}
    503 \lstMakeShortInline@%
    504 \end{cquote}
    505 Overloading is important for \CFA concurrency since the runtime system relies on creating different types to represent concurrency objects.
    506 Therefore, overloading eliminates long prefixes and other naming conventions to prevent name clashes.
    507 As seen in Section~\ref{s:Concurrency}, routine @main@ is heavily overloaded.
    508 As another example, variable overloading is useful in the parallel semantics of the @with@ statement for fields with the same name:
    509 \begin{cfa}
    510 struct S { int `i`; int j; double m; } s;
    511 struct T { int `i`; int k; int m; } t;
    512 with ( s, t ) {
    513         j + k;                                                                  $\C{// unambiguous, s.j + t.k}$
    514         m = 5.0;                                                                $\C{// unambiguous, s.m = 5.0}$
    515         m = 1;                                                                  $\C{// unambiguous, t.m = 1}$
    516         int a = m;                                                              $\C{// unambiguous, a = t.m }$
    517         double b = m;                                                   $\C{// unambiguous, b = s.m}$
    518         int c = `s.i` + `t.i`;                                  $\C{// unambiguous, qualification}$
    519         (double)m;                                                              $\C{// unambiguous, cast s.m}$
    520 }
    521 \end{cfa}
    522 For parallel semantics, both @s.i@ and @t.i@ are visible with the same type, so only @i@ is ambiguous without qualification.
    523 
    524 
    525 \subsection{Operators}
    526 
    527 Overloading also extends to operators.
    528 Operator-overloading syntax creates a routine name with an operator symbol and question marks for the operands:
    529 \begin{cquote}
    530 \lstDeleteShortInline@%
    531 \begin{tabular}{@{}ll@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}}
    532 \begin{cfa}
    533 int ++?(int op);
    534 int ?++(int op);
    535 int `?+?`(int op1, int op2);
    536 int ?<=?(int op1, int op2);
    537 int ?=? (int & op1, int op2);
    538 int ?+=?(int & op1, int op2);
    539 \end{cfa}
    540 &
    541 \begin{cfa}
    542 // unary prefix increment
    543 // unary postfix increment
    544 // binary plus
    545 // binary less than
    546 // binary assignment
    547 // binary plus-assignment
    548 \end{cfa}
    549 &
    550 \begin{cfa}
    551 struct S { int i, j; };
    552 S `?+?`( S op1, S op2) { // add two structures
    553         return (S){op1.i + op2.i, op1.j + op2.j};
    554 }
    555 S s1 = {1, 2}, s2 = {2, 3}, s3;
    556 s3 = s1 `+` s2;         // compute sum: s3 == {2, 5}
    557 \end{cfa}
    558 \end{tabular}
    559 \lstMakeShortInline@%
    560 \end{cquote}
    561 
    562 
    563 \subsection{Constructors / Destructors}
    564 
    565 Object lifetime is a challenge in non-managed programming languages.
    566 \CFA responds with \CC-like constructors and destructors, using a different operator-overloading syntax.
    567 \begin{cfa}
    568 struct VLA { int len, * data; };                        $\C{// variable length array of integers}$
    569 void ?{}( VLA & vla ) with ( vla ) { len = 10;  data = alloc( len ); }  $\C{// default constructor}$
    570 void ?{}( VLA & vla, int size, char fill ) with ( vla ) { len = size;  data = alloc( len, fill ); } // initialization
    571 void ?{}( VLA & vla, VLA other ) { vla.len = other.len;  vla.data = other.data; } $\C{// copy, shallow}$
    572 void ^?{}( VLA & vla ) with ( vla ) { free( data ); } $\C{// destructor}$
    573 {
    574         VLA  x,            y = { 20, 0x01 },     z = y; $\C{// z points to y}$
    575         // $\LstCommentStyle{\color{red}\ \ \ x\{\};\ \ \ \ \ \ \ \ \ y\{ 20, 0x01 \};\ \ \ \ \ \ \ \ \ \ z\{ z, y \};\ \ \ \ \ \ \ implicit calls}$
    576         ^x{};                                                                   $\C{// deallocate x}$
    577         x{};                                                                    $\C{// reallocate x}$
    578         z{ 5, 0xff };                                                   $\C{// reallocate z, not pointing to y}$
    579         ^y{};                                                                   $\C{// deallocate y}$
    580         y{ x };                                                                 $\C{// reallocate y, points to x}$
    581         x{};                                                                    $\C{// reallocate x, not pointing to y}$
    582 }       //  $\LstCommentStyle{\color{red}\^{}z\{\};\ \ \^{}y\{\};\ \ \^{}x\{\};\ \ \ implicit calls}$
    583 \end{cfa}
    584 Like \CC, construction is implicit on allocation (stack/heap) and destruction is implicit on deallocation.
    585 The object and all their fields are constructed/destructed.
    586 \CFA also provides @new@ and @delete@ as library routines, which behave like @malloc@ and @free@, in addition to constructing and destructing objects:
    587 \begin{cfa}
    588 {
    589         ... struct S s = {10}; ...                              $\C{// allocation, call constructor}$
    590 }                                                                                       $\C{// deallocation, call destructor}$
    591 struct S * s = new();                                           $\C{// allocation, call constructor}$
    592 ...
    593 delete( s );                                                            $\C{// deallocation, call destructor}$
    594 \end{cfa}
    595 \CFA concurrency uses object lifetime as a means of mutual exclusion and/or synchronization.
    596 
    597 
    598 \subsection{Parametric Polymorphism}
    599 \label{s:ParametricPolymorphism}
    600 
    601 The signature feature of \CFA is parametric-polymorphic routines~\cite{Cforall} with routines generalized using a @forall@ clause (giving the language its name), which allow separately compiled routines to support generic usage over multiple types.
    602 For example, the following sum routine works for any type that supports construction from 0 and addition:
    603 \begin{cfa}
    604 forall( otype T | { void `?{}`( T *, zero_t ); T `?+?`( T, T ); } ) // constraint type, 0 and +
    605 T sum( T a[$\,$], size_t size ) {
    606         `T` total = { `0` };                                    $\C{// initialize by 0 constructor}$
    607         for ( size_t i = 0; i < size; i += 1 )
    608                 total = total `+` a[i];                         $\C{// select appropriate +}$
    609         return total;
    610 }
    611 S sa[5];
    612 int i = sum( sa, 5 );                                           $\C{// use S's 0 construction and +}$
    613 \end{cfa}
    614 Type variables can be @otype@ or @dtype@.
    615 @otype@ refers to a \emph{complete type}, \ie, a type with size, alignment, default constructor, copy constructor, destructor, and assignment operator.
    616 @dtype@ refers to an \emph{incomplete type}, \eg, void or a forward-declared type.
    617 The builtin types @zero_t@ and @one_t@ overload constant 0 and 1 for a new types, where both 0 and 1 have special meaning in C.
    618 
    619 \CFA provides \newterm{traits} to name a group of type assertions, where the trait name allows specifying the same set of assertions in multiple locations, preventing repetition mistakes at each routine declaration:
    620 \begin{cfa}
    621 trait `sumable`( otype T ) {
    622         void `?{}`( T &, zero_t );                              $\C{// 0 literal constructor}$
    623         T `?+?`( T, T );                                                $\C{// assortment of additions}$
    624         T ?+=?( T &, T );
    625         T ++?( T & );
    626         T ?++( T & );
    627 };
    628 forall( otype T `| sumable( T )` )                      $\C{// use trait}$
    629 T sum( T a[$\,$], size_t size );
    630 \end{cfa}
    631 
    632 Using the return type for overload discrimination, it is possible to write a type-safe @alloc@ based on the C @malloc@:
    633 \begin{cfa}
    634 forall( dtype T | sized(T) ) T * alloc( void ) { return (T *)malloc( sizeof(T) ); }
    635 int * ip = alloc();                                                     $\C{// select type and size from left-hand side}$
    636 double * dp = alloc();
    637 struct S {...} * sp = alloc();
    638 \end{cfa}
    639 where the return type supplies the type/size of the allocation, which is impossible in most type systems.
    640 \end{comment}
    641 
    642 
    643 \section{Coroutines: Stepping Stone}
    644 \label{coroutine}
    645 
    646 Coroutines are generalized routines allowing execution to be temporarily suspended and later resumed.
    647 Hence, unlike a normal routine, a coroutine may not terminate when it returns to its caller, allowing it to be restarted with the values and execution location present at the point of suspension.
    648 This capability is accomplished via the coroutine's stack, where suspend/resume context switch among stacks.
    649 Because threading design-challenges are present in coroutines, their design effort is relevant, and this effort can be easily exposed to programmers giving them a useful new programming paradigm because a coroutine handles the class of problems that need to retain state between calls, \eg plugins, device drivers, and finite-state machines.
    650 Therefore, the two fundamental features of the core \CFA coroutine-API are independent call-stacks and @suspend@/@resume@ operations.
    651 
    652 For example, a problem made easier with coroutines is unbounded generators, \eg generating an infinite sequence of Fibonacci numbers
    653 \begin{displaymath}
    654 \mathsf{fib}(n) = \left \{
    655 \begin{array}{ll}
    656 0                                       & n = 0         \\
    657 1                                       & n = 1         \\
    658 \mathsf{fib}(n-1) + \mathsf{fib}(n-2)   & n \ge 2       \\
    659 \end{array}
    660 \right.
    661 \end{displaymath}
    662 where Figure~\ref{f:C-fibonacci} shows conventional approaches for writing a Fibonacci generator in C.
    663 Figure~\ref{f:GlobalVariables} illustrates the following problems: unique unencapsulated global variables necessary to retain state between calls, only one Fibonacci generator, and execution state must be explicitly retained via explicit state variables.
    664 Figure~\ref{f:ExternalState} addresses these issues: unencapsulated program global variables become encapsulated structure variables, unique global variables are replaced by multiple Fibonacci objects, and explicit execution state is removed by precomputing the first two Fibonacci numbers and returning $\mathsf{fib}(n-2)$.
     879Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface routines, \eg @next@.
     880Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the (overloaded) coroutine main.
     881The coroutine main's stack holds the state for the next generation, @f1@ and @f2@, and the code represents the three states in the Fibonacci formula via the three suspend points, to context switch back to the caller's @resume@.
     882The interface routine @next@, takes a Fibonacci instance and context switches to it using @resume@;
     883on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned.
     884The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack;
     885when the coroutine main returns, its stack is deallocated.
     886Hence, @Fib@ is an object at creation, transitions to a coroutine on its first resume, and transitions back to an object when the coroutine main finishes.
     887Figure~\ref{f:Coroutine1State} shows the coroutine version of the C version in Figure~\ref{f:ExternalState}.
     888Coroutine generators are called \newterm{output coroutines} because values are only returned.
    665889
    666890\begin{figure}
     
    689913\begin{lrbox}{\myboxA}
    690914\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    691 #define FIB_INIT { 0, 1 }
     915#define FibCtor { 0, 1 }
    692916typedef struct { int fn1, fn; } Fib;
    693917int fib( Fib * f ) {
     
    702926
    703927int main() {
    704         Fib f1 = FIB_INIT, f2 = FIB_INIT;
     928        Fib f1 = FibCtor, f2 = FibCtor;
    705929        for ( int i = 0; i < 10; i += 1 ) {
    706930                printf( "%d %d\n",
     
    719943        [fn1, fn] = [0, 1];
    720944        for () {
    721                 `suspend();`
     945                `suspend;`
    722946                [fn1, fn] = [fn, fn1 + fn];
    723947        }
    724948}
    725949int ?()( Fib & fib ) with( fib ) {
    726         `resume( fib );`  return fn1;
     950        return `resume( fib )`.fn1;
    727951}
    728952int main() {
     
    772996\caption{Fibonacci Generator}
    773997\label{f:C-fibonacci}
    774 
    775 % \bigskip
    776 %
    777 % \newbox\myboxA
    778 % \begin{lrbox}{\myboxA}
    779 % \begin{cfa}[aboveskip=0pt,belowskip=0pt]
    780 % `coroutine` Fib { int fn; };
    781 % void main( Fib & fib ) with( fib ) {
    782 %       fn = 0;  int fn1 = fn; `suspend()`;
    783 %       fn = 1;  int fn2 = fn1;  fn1 = fn; `suspend()`;
    784 %       for () {
    785 %               fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `suspend()`; }
    786 % }
    787 % int next( Fib & fib ) with( fib ) { `resume( fib );` return fn; }
    788 % int main() {
    789 %       Fib f1, f2;
    790 %       for ( 10 )
    791 %               sout | next( f1 ) | next( f2 );
    792 % }
    793 % \end{cfa}
    794 % \end{lrbox}
    795 % \newbox\myboxB
    796 % \begin{lrbox}{\myboxB}
    797 % \begin{python}[aboveskip=0pt,belowskip=0pt]
    798 %
    799 % def Fibonacci():
    800 %       fn = 0; fn1 = fn; `yield fn`  # suspend
    801 %       fn = 1; fn2 = fn1; fn1 = fn; `yield fn`
    802 %       while True:
    803 %               fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `yield fn`
    804 %
    805 %
    806 % f1 = Fibonacci()
    807 % f2 = Fibonacci()
    808 % for i in range( 10 ):
    809 %       print( `next( f1 )`, `next( f2 )` ) # resume
    810 %
    811 % \end{python}
    812 % \end{lrbox}
    813 % \subfloat[\CFA]{\label{f:Coroutine3States}\usebox\myboxA}
    814 % \qquad
    815 % \subfloat[Python]{\label{f:Coroutine1State}\usebox\myboxB}
    816 % \caption{Fibonacci input coroutine, 3 states, internal variables}
    817 % \label{f:cfa-fibonacci}
    818998\end{figure}
    819999
    820 Using a coroutine, it is possible to express the Fibonacci formula directly without any of the C problems.
    821 Figure~\ref{f:Coroutine3States} creates a @coroutine@ type, @`coroutine` Fib { int fn; }@, which provides communication, @fn@, for the \newterm{coroutine main}, @main@, which runs on the coroutine stack, and possibly multiple interface routines, \eg @next@.
    822 Like the structure in Figure~\ref{f:ExternalState}, the coroutine type allows multiple instances, where instances of this type are passed to the (overloaded) coroutine main.
    823 The coroutine main's stack holds the state for the next generation, @f1@ and @f2@, and the code represents the three states in the Fibonacci formula via the three suspend points, to context switch back to the caller's @resume@.
    824 The interface routine @next@, takes a Fibonacci instance and context switches to it using @resume@;
    825 on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned.
    826 The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack;
    827 when the coroutine main returns, its stack is deallocated.
    828 Hence, @Fib@ is an object at creation, transitions to a coroutine on its first resume, and transitions back to an object when the coroutine main finishes.
    829 Figure~\ref{f:Coroutine1State} shows the coroutine version of the C version in Figure~\ref{f:ExternalState}.
    830 Coroutine generators are called \newterm{output coroutines} because values are only returned.
    831 
    832 Figure~\ref{f:CFAFmt} shows an \newterm{input coroutine}, @Format@, for restructuring text into groups of characters of fixed-size blocks.
    833 For example, the input of the left is reformatted into the output on the right.
    834 \begin{quote}
    835 \tt
    836 \begin{tabular}{@{}l|l@{}}
    837 \multicolumn{1}{c|}{\textbf{\textrm{input}}} & \multicolumn{1}{c}{\textbf{\textrm{output}}} \\
    838 abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
    839 &
    840 \begin{tabular}[t]{@{}lllll@{}}
    841 abcd    & efgh  & ijkl  & mnop  & qrst  \\
    842 uvwx    & yzab  & cdef  & ghij  & klmn  \\
    843 opqr    & stuv  & wxyz  &               &
    844 \end{tabular}
    845 \end{tabular}
    846 \end{quote}
    847 The example takes advantage of resuming a coroutine in the constructor to prime the loops so the first character sent for formatting appears inside the nested loops.
    848 The destructor provides a newline, if formatted text ends with a full line.
    849 Figure~\ref{f:CFmt} shows the C equivalent formatter, where the loops of the coroutine are flattened (linearized) and rechecked on each call because execution location is not retained between calls.
    850 (Linearized code is the bane of device drivers.)
    851 
    852 \begin{figure}
    853 \centering
     1000\bigskip
     1001
    8541002\newbox\myboxA
    8551003\begin{lrbox}{\myboxA}
    8561004\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    857 `coroutine` Fmt {
    858         char ch;   // communication variables
    859         int g, b;   // needed in destructor
    860 };
    861 void main( Fmt & fmt ) with( fmt ) {
     1005`coroutine` Fib { int fn; };
     1006void main( Fib & fib ) with( fib ) {
     1007        fn = 0;  int fn1 = fn; `suspend`;
     1008        fn = 1;  int fn2 = fn1;  fn1 = fn; `suspend`;
    8621009        for () {
    863                 for ( g = 0; g < 5; g += 1 ) { // groups
    864                         for ( b = 0; b < 4; b += 1 ) { // blocks
    865                                 `suspend();`
    866                                 sout | ch; } // print character
    867                         sout | "  "; } // block separator
    868                 sout | nl; }  // group separator
    869 }
    870 void ?{}( Fmt & fmt ) { `resume( fmt );` } // prime
    871 void ^?{}( Fmt & fmt ) with( fmt ) { // destructor
    872         if ( g != 0 || b != 0 ) // special case
    873                 sout | nl; }
    874 void send( Fmt & fmt, char c ) { fmt.ch = c; `resume( fmt )`; }
     1010                fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `suspend`; }
     1011}
     1012int next( Fib & fib ) with( fib ) { `resume( fib );` return fn; }
    8751013int main() {
    876         Fmt fmt;
    877         sout | nlOff;   // turn off auto newline
    878         for ( 41 )
    879                 send( fmt, 'a' );
     1014        Fib f1, f2;
     1015        for ( 10 )
     1016                sout | next( f1 ) | next( f2 );
    8801017}
    8811018\end{cfa}
    8821019\end{lrbox}
    883 
    8841020\newbox\myboxB
    8851021\begin{lrbox}{\myboxB}
    8861022\begin{python}[aboveskip=0pt,belowskip=0pt]
    8871023
    888 
    889 
    890 def Fmt():
    891         try:
    892                 while True:
    893                         for g in range( 5 ):
    894                                 for b in range( 4 ):
    895 
    896                                         print( `(yield)`, end='' )
    897                                 print( '  ', end='' )
    898                         print()
    899 
    900 
    901         except GeneratorExit:
    902                 if g != 0 | b != 0:
    903                         print()
    904 
    905 
    906 fmt = Fmt()
    907 `next( fmt )`                    # prime
    908 for i in range( 41 ):
    909         `fmt.send( 'a' );`      # send to yield
     1024def Fibonacci():
     1025        fn = 0; fn1 = fn; `yield fn`  # suspend
     1026        fn = 1; fn2 = fn1; fn1 = fn; `yield fn`
     1027        while True:
     1028                fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `yield fn`
     1029
     1030
     1031f1 = Fibonacci()
     1032f2 = Fibonacci()
     1033for i in range( 10 ):
     1034        print( `next( f1 )`, `next( f2 )` ) # resume
    9101035
    9111036\end{python}
    9121037\end{lrbox}
    913 \subfloat[\CFA]{\label{f:CFAFmt}\usebox\myboxA}
     1038\subfloat[\CFA]{\label{f:Coroutine3States}\usebox\myboxA}
    9141039\qquad
    915 \subfloat[Python]{\label{f:CFmt}\usebox\myboxB}
    916 \caption{Output formatting text}
    917 \label{f:fmt-line}
     1040\subfloat[Python]{\label{f:Coroutine1State}\usebox\myboxB}
     1041\caption{Fibonacci input coroutine, 3 states, internal variables}
     1042\label{f:cfa-fibonacci}
    9181043\end{figure}
    919 
    920 The previous examples are \newterm{asymmetric (semi) coroutine}s because one coroutine always calls a resuming routine for another coroutine, and the resumed coroutine always suspends back to its last resumer, similar to call/return for normal routines.
    921 However, @resume@ and @suspend@ context switch among existing stack-frames, rather than create new ones so there is no stack growth.
    922 \newterm{Symmetric (full) coroutine}s have a coroutine call to a resuming routine for another coroutine, and its coroutine main calls another resuming routine, which eventually forms a resuming-call cycle.
    923 (The trivial cycle is a coroutine resuming itself.)
    924 This control flow is similar to recursion for normal routines, but again there is no stack growth from the context switch.
     1044\end{comment}
    9251045
    9261046\begin{figure}
     
    9301050\begin{cfa}
    9311051`coroutine` Prod {
    932         Cons & c;
     1052        Cons & c;                       // communication
    9331053        int N, money, receipt;
    9341054};
     
    9511071}
    9521072void start( Prod & prod, int N, Cons &c ) {
    953         &prod.c = &c; // reassignable reference
     1073        &prod.c = &c;
    9541074        prod.[N, receipt] = [N, 0];
    9551075        `resume( prod );`
     
    9641084\begin{cfa}
    9651085`coroutine` Cons {
    966         Prod & p;
     1086        Prod & p;                       // communication
    9671087        int p1, p2, status;
    9681088        bool done;
     
    9721092        cons.[status, done ] = [0, false];
    9731093}
    974 void ^?{}( Cons & cons ) {}
    9751094void main( Cons & cons ) with( cons ) {
    9761095        // 1st resume starts here
     
    9941113        `resume( cons );`
    9951114}
     1115
    9961116\end{cfa}
    9971117\end{tabular}
    9981118\caption{Producer / consumer: resume-resume cycle, bi-directional communication}
    9991119\label{f:ProdCons}
     1120
     1121\medskip
     1122
     1123\begin{center}
     1124\input{FullProdConsStack.pstex_t}
     1125\end{center}
     1126\vspace*{-10pt}
     1127\caption{Producer / consumer runtime stacks}
     1128\label{f:ProdConsRuntimeStacks}
     1129
     1130\medskip
     1131
     1132\begin{center}
     1133\input{FullCoroutinePhases.pstex_t}
     1134\end{center}
     1135\vspace*{-10pt}
     1136\caption{Ping / Pong coroutine steps}
     1137\label{f:PingPongFullCoroutineSteps}
    10001138\end{figure}
    10011139
    1002 Figure~\ref{f:ProdCons} shows a producer/consumer symmetric-coroutine performing bi-directional communication.
    1003 Since the solution involves a full-coroutining cycle, the program main creates one coroutine in isolation, passes this coroutine to its partner, and closes the cycle at the call to @start@.
    1004 The @start@ routine communicates both the number of elements to be produced and the consumer into the producer's coroutine-structure.
    1005 Then the @resume@ to @prod@ creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it.
    1006 @prod@'s coroutine main starts, creates local variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer to deliver the values, and printing the status returned from the consumer.
     1140Figure~\ref{f:ProdCons} shows the ping-pong example in Figure~\ref{f:CFAPingPongGen} extended into a producer/consumer symmetric-coroutine performing bidirectional communication.
     1141This example is illustrative because both producer/consumer have two interface functions with @resume@s that suspend execution in these interface (helper) functions.
     1142The program main creates the producer coroutine, passes it to the consumer coroutine in its initialization, and closes the cycle at the call to @start@ along with the number of items to be produced.
     1143The first @resume@ of @prod@ creates @prod@'s stack with a frame for @prod@'s coroutine main at the top, and context switches to it.
     1144@prod@'s coroutine main starts, creates local-state variables that are retained between coroutine activations, and executes $N$ iterations, each generating two random values, calling the consumer to deliver the values, and printing the status returned from the consumer.
    10071145
    10081146The producer call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status.
    1009 For the first resume, @cons@'s stack is initialized, creating local variables retained between subsequent activations of the coroutine.
     1147On the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine.
    10101148The consumer iterates until the @done@ flag is set, prints the values delivered by the producer, increments status, and calls back to the producer via @payment@, and on return from @payment@, prints the receipt from the producer and increments @money@ (inflation).
    10111149The call from the consumer to @payment@ introduces the cycle between producer and consumer.
    10121150When @payment@ is called, the consumer copies values into the producer's communication variable and a resume is executed.
    10131151The context switch restarts the producer at the point where it last context switched, so it continues in @delivery@ after the resume.
    1014 
    10151152@delivery@ returns the status value in @prod@'s coroutine main, where the status is printed.
    10161153The loop then repeats calling @delivery@, where each call resumes the consumer coroutine.
     
    10181155The consumer increments and returns the receipt to the call in @cons@'s coroutine main.
    10191156The loop then repeats calling @payment@, where each call resumes the producer coroutine.
    1020 
    1021 After iterating $N$ times, the producer calls @stop@.
    1022 The @done@ flag is set to stop the consumer's execution and a resume is executed.
    1023 The context switch restarts @cons@ in @payment@ and it returns with the last receipt.
    1024 The consumer terminates its loops because @done@ is true, its @main@ terminates, so @cons@ transitions from a coroutine back to an object, and @prod@ reactivates after the resume in @stop@.
    1025 @stop@ returns and @prod@'s coroutine main terminates.
    1026 The program main restarts after the resume in @start@.
    1027 @start@ returns and the program main terminates.
    1028 
    1029 One \emph{killer} application for a coroutine is device drivers, which at one time caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}.
    1030 Many device drivers are a finite state-machine parsing a protocol, e.g.:
    1031 \begin{tabbing}
    1032 \ldots STX \= \ldots message \ldots \= ESC \= ETX \= \ldots message \ldots  \= ETX \= 2-byte crc \= \ldots      \kill
    1033 \ldots STX \> \ldots message \ldots \> ESC \> ETX \> \ldots message \ldots  \> ETX \> 2-byte crc \> \ldots
    1034 \end{tabbing}
    1035 where a network message begins with the control character STX and ends with an ETX, followed by a 2-byte cyclic-redundancy check.
    1036 Control characters may appear in a message if preceded by an ESC.
    1037 Because FSMs can be complex and occur frequently in important domains, direct support of the coroutine is crucial in a systems programminglanguage.
    1038 
    1039 \begin{figure}
    1040 \begin{cfa}
    1041 enum Status { CONT, MSG, ESTX, ELNTH, ECRC };
    1042 `coroutine` Driver {
    1043         Status status;
    1044         char * msg, byte;
    1045 };
    1046 void ?{}( Driver & d, char * m ) { d.msg = m; }         $\C[3.0in]{// constructor}$
    1047 Status next( Driver & d, char b ) with( d ) {           $\C{// 'with' opens scope}$
    1048         byte = b; `resume( d );` return status;
    1049 }
    1050 void main( Driver & d ) with( d ) {
    1051         enum { STX = '\002', ESC = '\033', ETX = '\003', MaxMsg = 64 };
    1052         unsigned short int crc;                                                 $\C{// error checking}$
    1053   msg: for () {                                                                         $\C{// parse message}$
    1054                 status = CONT;
    1055                 unsigned int lnth = 0, sum = 0;
    1056                 while ( byte != STX ) `suspend();`
    1057           emsg: for () {
    1058                         `suspend();`                                                    $\C{// process byte}$
    1059                         choose ( byte ) {                                               $\C{// switch with default break}$
    1060                           case STX:
    1061                                 status = ESTX; `suspend();` continue msg;
    1062                           case ETX:
    1063                                 break emsg;
    1064                           case ESC:
    1065                                 suspend();
    1066                         } // choose
    1067                         if ( lnth >= MaxMsg ) {                                 $\C{// buffer full ?}$
    1068                                 status = ELNTH; `suspend();` continue msg; }
    1069                         msg[lnth++] = byte;
    1070                         sum += byte;
    1071                 } // for
    1072                 msg[lnth] = '\0';                                                       $\C{// terminate string}\CRT$
    1073                 `suspend();`
    1074                 crc = (unsigned char)byte << 8; // prevent sign extension for signed char
    1075                 `suspend();`
    1076                 status = (crc | (unsigned char)byte) == sum ? MSG : ECRC;
    1077                 `suspend();`
    1078         } // for
    1079 }
    1080 \end{cfa}
    1081 \caption{Device driver for simple communication protocol}
    1082 \end{figure}
     1157Figure~\ref{f:ProdConsRuntimeStacks} shows the runtime stacks of the program main, and the coroutine mains for @prod@ and @cons@ during the cycling.
     1158
     1159Terminating a coroutine cycle is more complex than a generator cycle, because it requires context switching to the program main's \emph{stack} to shutdown the program, whereas generators started by the program main run on its stack.
     1160Furthermore, each deallocated coroutine must guarantee all destructors are run for object allocated in the coroutine type \emph{and} allocated on the coroutine's stack at the point of suspension, which can be arbitrarily deep.
     1161When a coroutine's main ends, its stack is already unwound so any stack allocated objects with destructors have been finalized.
     1162The na\"{i}ve semantics for coroutine-cycle termination is context switch to the last resumer, like executing a @suspend@/@return@ in a generator.
     1163However, for coroutines, the last resumer is \emph{not} implicitly below the current stack frame, as for generators, because each coroutine's stack is independent.
     1164Unfortunately, it is impossible to determine statically if a coroutine is in a cycle and unrealistic to check dynamically (graph-cycle problem).
     1165Hence, a compromise solution is necessary that works for asymmetric (acyclic) and symmetric (cyclic) coroutines.
     1166
     1167Our solution for coroutine termination works well for the most common asymmetric and symmetric coroutine usage-patterns.
     1168For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer.
     1169All previous generators converted to coroutines have this property.
     1170For symmetric coroutines, it is common for the cycle creator to persist for the life-time of the cycle.
     1171Hence, the starter coroutine is remembered on the first resume and ending the coroutine resumes the starter.
     1172Figure~\ref{f:ProdConsRuntimeStacks} shows this semantic by the dashed lines from the end of the coroutine mains: @prod@ starts @cons@ so @cons@ resumes @prod@ at the end, and the program main starts @prod@ so @prod@ resumes the program main at the end.
     1173For other scenarios, it is always possible to devise a solution with additional programming effort.
     1174
     1175The producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first.
     1176Assume generator @PingPong@ is converted to a coroutine.
     1177Figure~\ref{f:PingPongFullCoroutineSteps} shows the creation, starter, and cyclic execution steps of the coroutine version.
     1178The program main creates (declares) coroutine instances @ping@ and @pong@.
     1179Next, program main resumes @ping@, making it @ping@'s starter, and @ping@'s main resumes @pong@'s main, making it @pong@'s starter.
     1180Execution forms a cycle when @pong@ resumes @ping@, and cycles $N$ times.
     1181By adjusting $N$ for either @ping@/@pong@, it is possible to have either one finish first, instead of @pong@ always ending first.
     1182If @pong@ ends first, it resumes its starter @ping@ in its coroutine main, then @ping@ ends and resumes its starter the program main in function @start@.
     1183If @ping@ ends first, it resumes its starter the program main in function @start@.
     1184Regardless of the cycle complexity, the starter stack always leads back to the program main, but the stack can be entered at an arbitrary point.
     1185Once back at the program main, coroutines @ping@ and @pong@ are deallocated.
     1186For generators, deallocation runs the destructors for all objects in the generator type.
     1187For coroutines, deallocation deals with objects in the coroutine type and must also run the destructors for any objects pending on the coroutine's stack for any unterminated coroutine.
     1188Hence, if a coroutine's destructor detects the coroutine is not ended, it implicitly raises a cancellation exception (uncatchable exception) at the coroutine and resumes it so the cancellation exception can propagate to the root of the coroutine's stack destroying all local variable on the stack.
     1189So the \CFA semantics for the generator and coroutine, ensure both can be safely deallocated at any time, regardless of their current state, like any other aggregate object.
     1190Explicitly raising normal exceptions at another coroutine can replace flag variables, like @stop@, \eg @prod@ raises a @stop@ exception at @cons@ after it finishes generating values and resumes @cons@, which catches the @stop@exception to terminate its loop.
     1191
     1192Finally, there is an interesting effect for @suspend@ with symmetric coroutines.
     1193A coroutine must retain its last resumer to suspend back because the resumer is on a different stack.
     1194These reverse pointers allow @suspend@ to cycle \emph{backwards}, which may be useful in certain cases.
     1195However, there is an anomaly if a coroutine resumes itself, because it overwrites its last resumer with itself, losing the ability to resume the last external resumer.
     1196To prevent losing this information, a self-resume does not overwrite the last resumer.
    10831197
    10841198
     
    10861200
    10871201A significant implementation challenge for coroutines (and threads, see Section~\ref{threads}) is adding extra fields and executing code after/before the coroutine constructor/destructor and coroutine main to create/initialize/de-initialize/destroy extra fields and the stack.
    1088 There are several solutions to this problem and the chosen option forced the \CFA coroutine design.
    1089 
    1090 Object-oriented inheritance provides extra fields and code in a restricted context, but it requires programmers to explicitly perform the inheritance:
     1202There are several solutions to this problem and the chosen option directed the \CFA coroutine design.
     1203
     1204For object-oriented languages, inheritance can be used to provide extra fields and code, but it requires explicitly writing the inheritance:
    10911205\begin{cfa}[morekeywords={class,inherits}]
    10921206class mycoroutine inherits baseCoroutine { ... }
    10931207\end{cfa}
    1094 and the programming language (and possibly its tool set, \eg debugger) may need to understand @baseCoroutine@ because of the stack.
     1208In addition, the programming language and possibly its tool set, \eg debugger, @valgrind@ need to understand @baseCoroutine@ because of special stack property of coroutines.
    10951209Furthermore, the execution of constructors/destructors is in the wrong order for certain operations.
    10961210For example, for threads if the thread is implicitly started, it must start \emph{after} all constructors, because the thread relies on a completely initialized object, but the inherited constructor runs \emph{before} the derived.
     
    11031217}
    11041218\end{cfa}
    1105 which also requires an explicit declaration that must be the last one to ensure correct initialization order.
    1106 However, there is nothing preventing wrong placement or multiple declarations.
    1107 
    1108 For coroutines as for threads, many implementations are based on routine pointers or routine objects~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}.
     1219which also requires an explicit declaration that must be last to ensure correct initialization order.
     1220However, there is nothing preventing wrong placement or multiple declarations of @baseCoroutine@.
     1221
     1222For coroutines, as for threads, many implementations are based on function pointers or function objects~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}.
    11091223For example, Boost implements coroutines in terms of four functor object-types:
    11101224\begin{cfa}
     
    11141228symmetric_coroutine<>::yield_type
    11151229\end{cfa}
    1116 Similarly, the canonical threading paradigm is often based on routine pointers, \eg @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}.
    1117 However, the generic thread-handle (identifier) is limited (few operations), unless it is wrapped in a custom type.
     1230
     1231Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a task is similar).
     1232The coroutine handle is the @coroutine@ instance containing programmer specified global/communication variables.
     1233The coroutine descriptor contains all implicit declarations needed by the runtime, \eg @suspend@/@resume@, and can be part of the coroutine handle or separate.\footnote{
     1234We are examining variable-sized structures (VLS), where fields can be variable-sized structures or arrays.
     1235Once allocated, a VLS is fixed sized.}
     1236The coroutine stack can appear in a number of locations and forms, fixed or variable sized.
     1237Hence, the coroutine's stack could be a VLS on the allocating stack, provided the allocating stack is large enough.
     1238For stack allocation, allocation/deallocation only requires a few arithmetic operation to compute the size and adjust the stack point, modulo any constructor costs.
     1239For heap allocation, allocation/deallocation is an expensive heap operation (where the heap can be a shared resource), modulo any constructor costs.
     1240For heap stack allocation, it is also possible to use a split (segmented) stack calling-convention, available with gcc and clang, so the stack is variable sized.
     1241Currently, \CFA supports stack/heap allocated descriptors but only heap allocated stacks;
     1242split-stack allocation is under development.
     1243In \CFA debug-mode, a fixed-sized stack is terminated with a write-only page, which catches most stack overflows.
     1244
     1245\begin{figure}
     1246\centering
     1247\input{corlayout.pstex_t}
     1248\caption{Coroutine memory layout}
     1249\label{f:CoroutineMemoryLayout}
     1250\end{figure}
     1251
     1252Similarly, the canonical threading paradigm is often based on function pointers, \eg @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}.
     1253However, the generic thread-handle (identifier) is limited (few operations), unless it is wrapped in a custom type, as in the pthreads approach.
    11181254\begin{cfa}
    11191255void mycor( coroutine_t cid, void * arg ) {
     
    11271263}
    11281264\end{cfa}
    1129 Since the custom type is simple to write in \CFA and solves several issues, added support for routine/lambda-based coroutines adds very little.
     1265Since the custom type is simple to write in \CFA and solves several issues, added support for function/lambda-based coroutines adds very little.
    11301266
    11311267Note, the type @coroutine_t@ must be an abstract handle to the coroutine, because the coroutine descriptor and its stack are non-copyable.
  • doc/papers/concurrency/examples/Fib.c

    r9856ca9 re7f8119  
    11#include <stdio.h>
    22
    3 #define FIB_INIT { 0, 1 }
    4 typedef struct { int fn1, fn; } Fib;
     3typedef struct {
     4        int fn1, fn;
     5} Fib;
     6
     7#define FibCtor { 1, 0 }
     8
    59int fib( Fib * f ) {
    6 
    7         int ret = f->fn1;
    8         f->fn1 = f->fn;
    9         f->fn = ret + f->fn;
    10 
    11         return ret;
     10        int fn = f->fn; f->fn = f->fn1;
     11                f->fn1 = f->fn + fn;
     12        return fn;
    1213}
    1314int main() {
    14         Fib f1 = FIB_INIT, f2 = FIB_INIT;
     15        Fib f1 = FibCtor, f2 = FibCtor;
    1516        for ( int i = 0; i < 10; i += 1 ) {
    1617                printf( "%d %d\n", fib( &f1 ), fib( &f2 ) );
  • doc/papers/concurrency/examples/Fib.cfa

    r9856ca9 re7f8119  
    1313}
    1414
    15 #define FIB_INIT { 0, 1 }
    16 typedef struct { int fn1, fn2; } Fib;
     15#define FibCtor { 0, 1 }
     16typedef struct { int fn, fn1; } Fib;
    1717int fib_state( Fib & f ) with( f ) {
    18         int ret = fn2;
    19         int fn = fn1 + fn2;
    20         fn2 = fn1; fn1 = fn;
    21         return ret;
     18        int fn0 = fn1 + fn2;  fn2 = fn1;  fn = fn0;
     19        return fn1;
    2220}
    2321
     
    3028        }
    3129}
    32 int next( Fib1 & fib ) with( fib ) { resume( fib ); return fn; }
     30int ?()( Fib1 & fib ) with( fib ) { return resume( fib ).fn; }
    3331
    34 coroutine Fib2 { int ret; };                                    // used for communication
     32coroutine Fib2 { int fn; };                                             // used for communication
    3533void main( Fib2 & fib ) with( fib ) {                   // called on first resume
    36         int fn, fn1 = 1, fn2 = 0;                                       // precompute first two states
     34        int fn1 = 1, fn2 = 0;                                           // precompute first two states
    3735        for () {
    38                 ret = fn2;
    3936                fn = fn1 + fn2;  fn2 = fn1;  fn1 = fn;  // general case
    4037                suspend();                                                              // restart last resume
    4138        }
    4239}
    43 int next( Fib2 & fib ) with( fib ) {
    44         resume( fib );                                                          // restart last suspend
    45         return ret;
     40int ?()( Fib2 & fib ) with( fib ) {
     41        return resume( fib ).fn;                                        // restart last suspend
     42}
     43int ?()( Fib2 & fib, int N ) with( fib ) {
     44        for ( N - 1 ) fib();
     45        return fib();
     46}
     47double ?()( Fib2 & fib ) with( fib ) {
     48        return (int)(fib()) / 3.14159;                                          // restart last suspend
    4649}
    4750
     
    5154        sout | nl;
    5255
    53         Fib f1 = FIB_INIT, f2 = FIB_INIT;
     56        Fib f1 = FibCtor, f2 = FibCtor;
    5457        for ( 10 )
    5558                sout | fib_state( f1 ) | fib_state( f2 );
     
    5861        Fib1 f1, f2;
    5962        for ( 10 )
    60                 sout | next( f1 ) | next( f2 );
     63                sout | f1() | f2();
    6164        sout | nl;
    6265
    63         Fib2 f1, f2;
     66        Fib2 f12, f22;
    6467        for ( 10 )
    65                 sout | next( (Fib2 &)f1 ) | next( (Fib2 &)f2 );
     68                sout | (int)f12() | (double)f12() | f22( 2 );
    6669}
    6770
  • doc/papers/concurrency/examples/Fib2.cfa

    r9856ca9 re7f8119  
    1010// Created On       : Thu Apr 26 23:20:08 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Mar 22 17:26:41 2019
    13 // Update Count     : 28
     12// Last Modified On : Sat May 18 08:55:59 2019
     13// Update Count     : 36
    1414//
    1515
     
    1717#include <coroutine.hfa>
    1818
    19 coroutine Fibonacci { int fn1; };                                               // used for communication
     19coroutine Fibonacci { int fn1, fn; };                                   // used for communication
    2020
    2121void main( Fibonacci & fib ) with( fib ) {                              // called on first resume
    22         int fn;
    23         [fn1, fn] = [0, 1];                                                                     // precompute first two states
     22        [fn1, fn] = [1, 0];                                                                     // precompute first two states
    2423        for () {
    2524                suspend();                                                                              // restart last resume
     
    2928
    3029int ?()( Fibonacci & fib ) with( fib ) {                                // function call operator
    31         resume( fib );                                                                          // restart last suspend
    32         return fn1;
     30        return resume( fib ).fn1;                                                       // restart last suspend
    3331}
    3432
     
    3634        Fibonacci f1, f2;
    3735        for ( 10 ) {                                                                            // print N Fibonacci values
    38                 sout | f1() | f2();
     36                sout | resume( f1 ).fn | resume( f2 ).fn;
    3937        } // for
    4038}
     
    4240// Local Variables: //
    4341// tab-width: 4 //
    44 // compile-command: "cfa fibonacci_1.cfa" //
     42// compile-command: "cfa Fib2.cfa" //
    4543// End: //
  • libcfa/prelude/prototypes.awk

    r9856ca9 re7f8119  
    1010# Created On       : Sat May 16 07:57:37 2015
    1111# Last Modified By : Peter A. Buhr
    12 # Last Modified On : Tue Jul  5 14:32:52 2016
    13 # Update Count     : 32
     12# Last Modified On : Thu Jun  6 20:46:28 2019
     13# Update Count     : 34
    1414#
    1515
     
    1818BEGIN {
    1919  FS = "[( )]"
    20     # order so string search is longest string
    21     i=-1
    22     types[i+=1] = "BOOL";                                                         vtypes[i] = "_Bool"
    23     types[i+=1] = "UINTMAX";                                            vtypes[i] = "unsigned long int"
    24     types[i+=1] = "UINT16";                                               vtypes[i] = "short int"
    25     types[i+=1] = "UINT32";                                               vtypes[i] = "int"
    26     types[i+=1] = "UINT64";                                               vtypes[i] = "long long int"
    27     types[i+=1] = "UINT";                                                         vtypes[i] = "unsigned int"
    28     types[i+=1] = "INTMAX";                                               vtypes[i] = "long int"
    29     types[i+=1] = "INTPTR";                                               vtypes[i] = "int *"
    30     types[i+=1] = "WINT";                                                         vtypes[i] = "unsigned int"
    31     types[i+=1] = "INT";                                                          vtypes[i] = "int"
    32     types[i+=1] = "ULONGLONG";                                  vtypes[i] = "unsigned long long"
    33     types[i+=1] = "ULONG";                                                vtypes[i] = "unsigned long"
    34     types[i+=1] = "UNSIGNED";                                           vtypes[i] = "unsigned"
    35     types[i+=1] = "COMPLEX_LONGDOUBLE";             vtypes[i] = "_Complex long double"
    36     types[i+=1] = "COMPLEX_DOUBLE";                           vtypes[i] = "_Complex double"
    37     types[i+=1] = "COMPLEX_FLOAT";                            vtypes[i] = "_Complex float"
    38     types[i+=1] = "LONGDOUBLEPTR";                            vtypes[i] = "long double *"
    39     types[i+=1] = "LONGDOUBLE";                                 vtypes[i] = "long double"
    40     types[i+=1] = "LONGLONG";                                           vtypes[i] = "long long"
    41     types[i+=1] = "LONG";                                                         vtypes[i] = "long"
    42     types[i+=1] = "DFLOAT32";                                           vtypes[i] = "__Unsupported"
    43     types[i+=1] = "DFLOAT64";                                           vtypes[i] = "__Unsupported"
    44     types[i+=1] = "DFLOAT128";                                  vtypes[i] = "__Unsupported"
    45     types[i+=1] = "DOUBLEPTR";                              vtypes[i] = "double *"
    46     types[i+=1] = "DOUBLE";                                               vtypes[i] = "double"
    47     types[i+=1] = "FLOATPTR";                                           vtypes[i] = "float *"
    48     types[i+=1] = "FLOAT128X";                                              vtypes[i] = "__Unsupported"
    49     types[i+=1] = "FLOAT128";                                                 vtypes[i] = "__Unsupported"
    50     types[i+=1] = "FLOAT64X";                                                 vtypes[i] = "__Unsupported"
    51     types[i+=1] = "FLOAT64";                                                  vtypes[i] = "__Unsupported"
    52     types[i+=1] = "FLOAT32X";                                                 vtypes[i] = "__Unsupported"
    53     types[i+=1] = "FLOAT32";                                                  vtypes[i] = "__Unsupported"
    54     types[i+=1] = "FLOAT16";                                                  vtypes[i] = "__Unsupported"
    55     types[i+=1] = "FLOAT";                                                vtypes[i] = "float"
    56     types[i+=1] = "CONST_VPTR";                                       vtypes[i] = "const volatile void *"
    57     types[i+=1] = "CONST_PTR";                                  vtypes[i] = "const void *"
    58     types[i+=1] = "CONST_STRING";                                     vtypes[i] = "const char *"
    59     types[i+=1] = "CONST_TM_PTR";                               vtypes[i] = "const struct tm *"
    60     types[i+=1] = "PTR_FN_VOID_VAR_PTR_SIZE";   vtypes[i] = ""
    61     types[i+=1] = "PTR_CONST_STRING";                       vtypes[i] = "char *const"
    62     types[i+=1] = "PTRMODE_PTR";                                      vtypes[i] = ""
    63     types[i+=1] = "PTRPTR";                                               vtypes[i] = "void **"
    64     types[i+=1] = "VPTR";                                                   vtypes[i] = "volatile void *"
    65     types[i+=1] = "PTR";                                                          vtypes[i] = "void *"
    66     types[i+=1] = "VOID";                                                         vtypes[i] = "void"
    67     types[i+=1] = "STRING";                                               vtypes[i] = "char *"
    68     types[i+=1] = "FILEPTR";                                            vtypes[i] = "struct _IO_FILE *"
    69     types[i+=1] = "SIZE";                                                         vtypes[i] = "unsigned long"
    70     types[i+=1] = "VAR";                                                          vtypes[i] = "..."
    71     types[i+=1] = "VALIST_ARG";                                 vtypes[i] = "__builtin_va_list"
    72     types[i+=1] = "VALIST_REF";                                 vtypes[i] = "__builtin_va_list"
    73     types[i+=1] = "UNWINDWORD";                                 vtypes[i] = "void *"
    74     types[i+=1] = "WORD";                                                         vtypes[i] = ""
    75     types[i+=1] = "SSIZE";                                                vtypes[i] = "long int"
    76     types[i+=1] = "PID";                                                          vtypes[i] = "int"
    77     types[i+=1] = "I16";                                                          vtypes[i] = "__int128"
    78     types[i+=1] = "I8";                                                     vtypes[i] = "long long int"
    79     types[i+=1] = "I4";                                                     vtypes[i] = "int"
    80     types[i+=1] = "I2";                                                     vtypes[i] = "short"
    81     types[i+=1] = "I1";                                                     vtypes[i] = "char"
    82     N = i + 1
     20        # order so string search is longest string
     21        i=-1
     22        types[i+=1] = "BOOL";                                           vtypes[i] = "_Bool"
     23        types[i+=1] = "UINTMAX";                                        vtypes[i] = "unsigned long int"
     24        types[i+=1] = "UINT16";                                         vtypes[i] = "short int"
     25        types[i+=1] = "UINT32";                                         vtypes[i] = "int"
     26        types[i+=1] = "UINT64";                                         vtypes[i] = "long long int"
     27        types[i+=1] = "UINT";                                           vtypes[i] = "unsigned int"
     28        types[i+=1] = "INTMAX";                                         vtypes[i] = "long int"
     29        types[i+=1] = "INTPTR";                                         vtypes[i] = "int *"
     30        types[i+=1] = "WINT";                                           vtypes[i] = "unsigned int"
     31        types[i+=1] = "INT";                                            vtypes[i] = "int"
     32        types[i+=1] = "ULONGLONG";                                      vtypes[i] = "unsigned long long"
     33        types[i+=1] = "ULONG";                                          vtypes[i] = "unsigned long"
     34        types[i+=1] = "UNSIGNED";                                       vtypes[i] = "unsigned"
     35        types[i+=1] = "COMPLEX_LONGDOUBLE";                     vtypes[i] = "_Complex long double"
     36        types[i+=1] = "COMPLEX_DOUBLE";                         vtypes[i] = "_Complex double"
     37        types[i+=1] = "COMPLEX_FLOAT";                          vtypes[i] = "_Complex float"
     38        types[i+=1] = "LONGDOUBLEPTR";                          vtypes[i] = "long double *"
     39        types[i+=1] = "LONGDOUBLE";                                     vtypes[i] = "long double"
     40        types[i+=1] = "LONGLONG";                                       vtypes[i] = "long long"
     41        types[i+=1] = "LONG";                                           vtypes[i] = "long"
     42        types[i+=1] = "DFLOAT32";                                       vtypes[i] = "__Unsupported"
     43        types[i+=1] = "DFLOAT64";                                       vtypes[i] = "__Unsupported"
     44        types[i+=1] = "DFLOAT128";                                      vtypes[i] = "__Unsupported"
     45        types[i+=1] = "DOUBLEPTR";                                      vtypes[i] = "double *"
     46        types[i+=1] = "DOUBLE";                                         vtypes[i] = "double"
     47        types[i+=1] = "FLOATPTR";                                       vtypes[i] = "float *"
     48        types[i+=1] = "FLOAT128X";                                      vtypes[i] = "__Unsupported"
     49        types[i+=1] = "FLOAT128";                                       vtypes[i] = "__Unsupported"
     50        types[i+=1] = "FLOAT64X";                                       vtypes[i] = "__Unsupported"
     51        types[i+=1] = "FLOAT64";                                        vtypes[i] = "__Unsupported"
     52        types[i+=1] = "FLOAT32X";                                       vtypes[i] = "__Unsupported"
     53        types[i+=1] = "FLOAT32";                                        vtypes[i] = "__Unsupported"
     54        types[i+=1] = "FLOAT16";                                        vtypes[i] = "__Unsupported"
     55        types[i+=1] = "FLOAT";                                          vtypes[i] = "float"
     56        types[i+=1] = "CONST_VPTR";                                     vtypes[i] = "const volatile void *"
     57        types[i+=1] = "CONST_PTR";                                      vtypes[i] = "const void *"
     58        types[i+=1] = "CONST_STRING";                           vtypes[i] = "const char *"
     59        types[i+=1] = "CONST_TM_PTR";                           vtypes[i] = "const struct tm *"
     60        types[i+=1] = "PTR_FN_VOID_VAR_PTR_SIZE";       vtypes[i] = ""
     61        types[i+=1] = "PTR_CONST_STRING";                       vtypes[i] = "char *const"
     62        types[i+=1] = "PTRMODE_PTR";                            vtypes[i] = ""
     63        types[i+=1] = "PTRPTR";                                         vtypes[i] = "void **"
     64        types[i+=1] = "VPTR";                                           vtypes[i] = "volatile void *"
     65        types[i+=1] = "PTR";                                            vtypes[i] = "void *"
     66        types[i+=1] = "VOID";                                           vtypes[i] = "void"
     67        types[i+=1] = "STRING";                                         vtypes[i] = "char *"
     68        types[i+=1] = "FILEPTR";                                        vtypes[i] = "struct _IO_FILE *"
     69        types[i+=1] = "SIZE";                                           vtypes[i] = "unsigned long"
     70        types[i+=1] = "VAR";                                            vtypes[i] = "..."
     71        types[i+=1] = "VALIST_ARG";                                     vtypes[i] = "__builtin_va_list"
     72        types[i+=1] = "VALIST_REF";                                     vtypes[i] = "__builtin_va_list"
     73        types[i+=1] = "UNWINDWORD";                                     vtypes[i] = "void *"
     74        types[i+=1] = "WORD";                                           vtypes[i] = ""
     75        types[i+=1] = "SSIZE";                                          vtypes[i] = "long int"
     76        types[i+=1] = "PID";                                            vtypes[i] = "int"
     77        types[i+=1] = "I16";                                            vtypes[i] = "__int128"
     78        types[i+=1] = "I8";                                                     vtypes[i] = "long long int"
     79        types[i+=1] = "I4";                                                     vtypes[i] = "int"
     80        types[i+=1] = "I2";                                                     vtypes[i] = "short"
     81        types[i+=1] = "I1";                                                     vtypes[i] = "char"
     82        N = i + 1
    8383} # BEGIN
    8484
    8585/BT_FN/ {
    86     for (i = 1; i <= NF; i++) {
    87       if( match($i, "BT_FN") != 0 ) {
    88         prototypes[$i] = $i
    89       }
    90     }
     86        for (i = 1; i <= NF; i++) {
     87          if( match($i, "BT_FN") != 0 ) {
     88                prototypes[$i] = $i
     89          }
     90        }
    9191  }
    9292
    9393END {
    94     printf( "#define DEF_BUILTIN(ENUM, NAME, CLASS, TYPE, LIBTYPE, BOTH_P, FALLBACK_P, NONANSI_P, ATTRS, IMPLICIT, COND) TYPE(NAME)\n" );
    95     printf( "#define FUNC_SIMPLE(RETURN, NAME, ARGS...) RETURN NAME(ARGS);\n" );
    96     printf( "#define BT_LAST(NAME) FUNC_SIMPLE(void, NAME)\n\n" );
     94        printf( "#define DEF_BUILTIN(ENUM, NAME, CLASS, TYPE, LIBTYPE, BOTH_P, FALLBACK_P, NONANSI_P, ATTRS, IMPLICIT, COND) TYPE(NAME)\n" );
     95        printf( "#define FUNC_SIMPLE(RETURN, NAME, ARGS...) RETURN NAME(ARGS);\n" );
     96        printf( "#define BT_LAST(NAME) FUNC_SIMPLE(void, NAME)\n\n" );
    9797
    98     # generate C types for macros names
    99     for ( i = 0; i < N; i += 1 ) {
     98        # generate C types for macros names
     99        for ( i = 0; i < N; i += 1 ) {
    100100                printf( "#define BT_%s %s\n", types[i], vtypes[i] )
    101     } # for
    102     printf( "\n" )
     101        } # for
     102        printf( "\n" )
    103103
    104     for ( prototype in prototypes ) {
    105       # printf( "//\"%s\"\n", prototype )
    106       if ( index( "BT_LAST", prototype ) == 1 ) {
    107         continue
    108       } # if
     104        for ( prototype in prototypes ) {
     105          # printf( "//\"%s\"\n", prototype )
     106          if ( index( "BT_LAST", prototype ) == 1 ) {
     107                continue
     108          } # if
    109109
    110       printf( "#define %s(NAME) FUNC_SIMPLE(", prototype )
     110          printf( "#define %s(NAME) FUNC_SIMPLE(", prototype )
    111111
    112       if ( sub( "BT_FN_", "", prototype ) == 0 ) {
    113         printf( "\n********** BAD MACRO NAME \"%s\" **********\n", prototype )
    114         exit 0
    115       } # if
     112          if ( sub( "BT_FN_", "", prototype ) == 0 ) {
     113                printf( "\n********** BAD MACRO NAME \"%s\" **********\n", prototype )
     114                exit 0
     115          } # if
    116116
    117       # generate function return type as macro
    118       for ( t = 0; t < N; t += 1 ) {                                    # find longest match
    119         type = types[t];
    120         if ( index( prototype, type ) == 1 ) {          # found match
    121           printf( "BT_%s, NAME", type )
    122           sub( type, "", prototype )
    123           break;
    124         } # if
    125       } # for
     117          # generate function return type as macro
     118          for ( t = 0; t < N; t += 1 ) {                                        # find longest match
     119                type = types[t];
     120                if ( index( prototype, type ) == 1 ) {          # found match
     121                  printf( "BT_%s, NAME", type )
     122                  sub( type, "", prototype )
     123                  break;
     124                } # if
     125          } # for
    126126
    127       # generate function parameter types as macro
    128       if ( index( prototype, "VAR" ) != 2 ) {                   # C-style empty parameters ?
    129         for ( p = 0; length( prototype ) > 0; p += 1 ) { # until all parameters types are removed
    130           sub( "_", "", prototype)                              # remove "_"
    131           printf( ", ", type )
    132           temp = prototype
    133           for ( t = 0; t < N; t += 1 ) {                        # find longest match
    134             type = types[t];
    135             if ( index( prototype, type ) == 1 ) { # found match
    136               printf( "BT_%s", type )
    137               sub( type, "", prototype )
    138               break;
    139             } # if
    140           } # for
    141           if ( temp == prototype ) {                            # no match found for parameter in macro table
    142             printf( "\n********** MISSING TYPE \"%s\" **********\n", prototype )
    143             exit 0
    144           } # if
    145         } # for
    146       } # if
    147       printf( ")\n" )
    148     } # for
     127          # generate function parameter types as macro
     128          if ( index( prototype, "VAR" ) != 2 ) {                       # C-style empty parameters ?
     129                for ( p = 0; length( prototype ) > 0; p += 1 ) { # until all parameters types are removed
     130                  sub( "_", "", prototype)                              # remove "_"
     131                  printf( ", ", type )
     132                  temp = prototype
     133                  for ( t = 0; t < N; t += 1 ) {                        # find longest match
     134                        type = types[t];
     135                        if ( index( prototype, type ) == 1 ) { # found match
     136                          printf( "BT_%s", type )
     137                          sub( type, "", prototype )
     138                          break;
     139                        } # if
     140                  } # for
     141                  if ( temp == prototype ) {                            # no match found for parameter in macro table
     142                        printf( "\n********** MISSING TYPE \"%s\" **********\n", prototype )
     143                        exit 0
     144                  } # if
     145                } # for
     146          } # if
     147          printf( ")\n" )
     148        } # for
    149149
    150150        # extras
     
    152152        printf( "\n#include \"sync-builtins.cf\"\n\n" );
    153153        printf( "extern const char *__PRETTY_FUNCTION__;\n" );
     154        printf( "float _Complex __builtin_complex( float, float );\n" );
     155        printf( "double _Complex __builtin_complex( double, double );\n" );
     156        printf( "long double _Complex __builtin_complex( long double, long double );\n" );
    154157} # END
    155158
  • libcfa/src/iostream.cfa

    r9856ca9 re7f8119  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue May 21 13:01:26 2019
    13 // Update Count     : 674
     12// Last Modified On : Sun Jun  9 16:27:17 2019
     13// Update Count     : 803
    1414//
    1515
     
    2020#include <stdbool.h>                                                                    // true/false
    2121//#include <string.h>                                                                   // strlen, strcmp
     22extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
    2223extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
    23 extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
     24extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
     25extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
    2426#include <float.h>                                                                              // DBL_DIG, LDBL_DIG
    2527#include <math.h>                                                                               // isfinite
    2628#include <complex.h>                                                                    // creal, cimag
    27 }
     29} // extern "C"
     30
     31
     32//*********************************** Ostream ***********************************
     33
    2834
    2935forall( dtype ostype | ostream( ostype ) ) {
     
    198204                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    199205//              os | crealf( fc ) | nonl;
    200                 float f = crealf( fc );
    201                 PrintWithDP( os, "%g", f );
    202                 f = cimagf( fc );
    203                 PrintWithDP( os, "%+g", f );
     206                PrintWithDP( os, "%g", crealf( fc ) );
     207                PrintWithDP( os, "%+g", cimagf( fc ) );
    204208                fmt( os, "i" );
    205209                return os;
     
    212216                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    213217//              os | creal( dc ) | nonl;
    214                 double d = creal( dc );
    215                 PrintWithDP( os, "%.*lg", d, DBL_DIG );
    216                 d = cimag( dc );
    217                 PrintWithDP( os, "%+.*lg", d, DBL_DIG );
     218                PrintWithDP( os, "%.*lg", creal( dc ), DBL_DIG );
     219                PrintWithDP( os, "%+.*lg", cimag( dc ), DBL_DIG );
    218220                fmt( os, "i" );
    219221                return os;
     
    226228                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    227229//              os | creall( ldc ) || nonl;
    228                 long double ld = creall( ldc );
    229                 PrintWithDP( os, "%.*Lg", ld, LDBL_DIG );
    230                 ld = cimagl( ldc );
    231                 PrintWithDP( os, "%+.*Lg", ld, LDBL_DIG );
     230                PrintWithDP( os, "%.*Lg", creall( ldc ), LDBL_DIG );
     231                PrintWithDP( os, "%+.*Lg", cimagl( ldc ), LDBL_DIG );
    232232                fmt( os, "i" );
    233233                return os;
     
    395395} // distribution
    396396
    397 //---------------------------------------
    398 
    399397// writes the range [begin, end) to the given stream
    400398forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otype iterator_type | iterator( iterator_type, elt_type ) ) {
     
    410408} // distribution
    411409
    412 //---------------------------------------
     410//*********************************** Manipulators ***********************************
     411
     412//*********************************** Integral ***********************************
     413
     414static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
     415static const char * longbin[]  = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
     416
     417// Default prefix for non-decimal prints is 0b, 0, 0x.
     418#define IntegralFMTImpl( T, CODE, IFMTNP, IFMTP ) \
     419forall( dtype ostype | ostream( ostype ) ) { \
     420        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
     421                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); \
     422\
     423                if ( f.base == 'b' || f.base == 'B' ) {                 /* bespoke binary format */ \
     424                        int bits;                                                                                                       \
     425                        if ( f.val == (T){0} ) bits = 1;                        /* force at least one bit to print */ \
     426                        else bits = sizeof(long long int) * 8 - __builtin_clzll( f.val ); /* position of most significant bit */ \
     427                        bits = bits > sizeof(f.val) * 8 ? sizeof(f.val) * 8 : bits; \
     428                        int spaces = f.wd - bits;                                       /* can be negative */ \
     429                        if ( ! f.flags.nobsdp ) { spaces -= 2; }        /* base prefix takes space */ \
     430                        /* printf( "%d %d\n", bits, spaces ); */ \
     431                        if ( ! f.flags.left ) {                                         /* right justified ? */ \
     432                                /* Note, base prefix then zero padding or spacing then prefix. */ \
     433                                if ( f.flags.pad0 || f.flags.pc ) { \
     434                                        if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
     435                                        if ( f.flags.pc ) spaces = f.pc - bits; \
     436                                        if ( spaces > 0 ) fmt( os, "%0*d", spaces, 0 ); /* zero pad */ \
     437                                } else { \
     438                                        if ( spaces > 0 ) fmt( os, "%*s", spaces, " " ); /* space pad */ \
     439                                        if ( ! f.flags.nobsdp ) { fmt( os, "0%c", f.base ); } \
     440                                } /* if */ \
     441                        } else if ( ! f.flags.nobsdp ) { \
     442                                fmt( os, "0%c", f.base ); \
     443                        } /* if */ \
     444                        int shift = (bits - 1) / 4 * 4; /* floor( bits - 1, 4 ) */ \
     445                        typeof( f.val ) temp = f.val; \
     446                        fmt( os, "%s", shortbin[(temp >> shift) & 0xf] ); \
     447                        for () { \
     448                                shift -= 4; \
     449                          if ( shift < 0 ) break; \
     450                                temp = f.val; \
     451                                fmt( os, "%s", longbin[(temp >> shift) & 0xf] ); \
     452                        } /* for */ \
     453                        if ( f.flags.left && spaces > 0 ) fmt( os, "%*s", spaces, " " ); \
     454                        return os; \
     455                } /* if  */ \
     456\
     457                char fmtstr[sizeof(IFMTP)];                                             /* sizeof includes '\0' */ \
     458                if ( ! f.flags.pc ) memcpy( &fmtstr, IFMTNP, sizeof(IFMTNP) ); \
     459                else memcpy( &fmtstr, IFMTP, sizeof(IFMTP) ); \
     460                int star = 4;                                                                   /* position before first '*' */ \
     461\
     462                /* Insert flags into spaces before '*', from right to left. */ \
     463                if ( ! f.flags.nobsdp ) { fmtstr[star] = '#'; star -= 1; } \
     464                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
     465                if ( f.flags.sign && f.base == CODE ) { fmtstr[star] = '+'; star -= 1; } \
     466                if ( f.flags.pad0 && ! f.flags.pc ) { fmtstr[star] = '0'; star -= 1; } \
     467                fmtstr[star] = '%'; \
     468\
     469                if ( ! f.flags.pc ) {                                                   /* no precision */ \
     470                        /* printf( "%s\n", &fmtstr[star] ); */ \
     471                        fmtstr[sizeof(IFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
     472                        fmt( os, &fmtstr[star], f.wd, f.val ); \
     473                } else {                                                                                /* precision */ \
     474                        fmtstr[sizeof(IFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
     475                        /* printf( "%s\n", &fmtstr[star] ); */ \
     476                        fmt( os, &fmtstr[star], f.wd, f.pc, f.val ); \
     477                } /* if */ \
     478                return os; \
     479        } /* ?|? */ \
     480        void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); nl( os ); } \
     481} // distribution
     482
     483IntegralFMTImpl( signed char, 'd', "%    *hh ", "%    *.*hh " )
     484IntegralFMTImpl( unsigned char, 'u', "%    *hh ", "%    *.*hh " )
     485IntegralFMTImpl( signed short int, 'd', "%    *h ", "%    *.*h " )
     486IntegralFMTImpl( unsigned short int, 'u', "%    *h ", "%    *.*h " )
     487IntegralFMTImpl( signed int, 'd', "%    * ", "%    *.* " )
     488IntegralFMTImpl( unsigned int, 'u', "%    * ", "%    *.* " )
     489IntegralFMTImpl( signed long int, 'd', "%    *l ", "%    *.*l " )
     490IntegralFMTImpl( unsigned long int, 'u', "%    *l ", "%    *.*l " )
     491IntegralFMTImpl( signed long long int, 'd', "%    *ll ", "%    *.*ll " )
     492IntegralFMTImpl( unsigned long long int, 'u', "%    *ll ", "%    *.*ll " )
     493
     494//*********************************** Floating Point ***********************************
     495
     496#define PrintWithDP2( os, format, val, ... ) \
     497        { \
     498                enum { size = 48 }; \
     499                char buf[size]; \
     500                int bufbeg = 0, i, len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
     501                if ( isfinite( val ) && (f.base != 'g' || f.pc != 0) ) { /* if number, print decimal point */ \
     502                        for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */ \
     503                        if ( i == len && ! f.flags.nobsdp ) { \
     504                                if ( ! f.flags.left ) { \
     505                                        buf[i] = '.'; buf[i + 1] = '\0'; \
     506                                        if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \
     507                                } else { \
     508                                        for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \
     509                                        buf[i] = '.'; \
     510                                        if ( i == len ) buf[i + 1] = '\0'; \
     511                                } /* if */ \
     512                        } /* if */ \
     513                } /* if */ \
     514                fmt( os, "%s", &buf[bufbeg] ); \
     515        }
     516
     517#define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \
     518forall( dtype ostype | ostream( ostype ) ) { \
     519        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
     520                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) ); \
     521                char fmtstr[sizeof(DFMTP)];                                             /* sizeof includes '\0' */ \
     522                if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \
     523                else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \
     524                int star = 4;                                                                   /* position before first '*' */ \
     525\
     526                /* Insert flags into spaces before '*', from right to left. */ \
     527                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; } \
     528                if ( f.flags.sign ) { fmtstr[star] = '+'; star -= 1; } \
     529                if ( f.flags.pad0 ) { fmtstr[star] = '0'; star -= 1; } \
     530                fmtstr[star] = '%'; \
     531\
     532                if ( ! f.flags.pc ) {                                                   /* no precision */ \
     533                        fmtstr[sizeof(DFMTNP)-2] = f.base;                      /* sizeof includes '\0' */ \
     534                        /* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star]); */ \
     535                        PrintWithDP2( os, &fmtstr[star], f.val, f.wd ) \
     536                } else {                                                                                /* precision */ \
     537                        fmtstr[sizeof(DFMTP)-2] = f.base;                       /* sizeof includes '\0' */ \
     538                        /* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \
     539                        PrintWithDP2( os, &fmtstr[star], f.val, f.wd, f.pc ) \
     540                } /* if */ \
     541                return os; \
     542        } /* ?|? */ \
     543        void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); nl( os ); } \
     544} // distribution
     545
     546FloatingPointFMTImpl( double, "%    * ", "%    *.* " )
     547FloatingPointFMTImpl( long double, "%    *L ", "%    *.*L " )
     548
     549//*********************************** Character ***********************************
     550
     551forall( dtype ostype | ostream( ostype ) ) {
     552        ostype & ?|?( ostype & os, _Ostream_Manip(char) f ) {
     553                if ( f.base != 'c' ) {                                                          // bespoke binary/octal/hex format
     554                        _Ostream_Manip(unsigned char) fmtuc @= { f.val, f.wd, f.pc, f.base, {'\0'} };
     555                        fmtuc.flags.pc = f.flags.pc;
     556                        fmtuc.flags.nobsdp = f.flags.nobsdp;
     557//                      os | fmtuc | nonl;
     558                        (ostype &)(os | fmtuc);
     559                        return os;
     560                } // if
     561
     562                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
     563
     564                #define CFMTNP "% * "
     565                char fmtstr[sizeof(CFMTNP)];                                            // sizeof includes '\0'
     566                memcpy( &fmtstr, CFMTNP, sizeof(CFMTNP) );
     567                int star = 1;                                                                           // position before first '*'
     568
     569                // Insert flags into spaces before '*', from right to left.
     570                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
     571                fmtstr[star] = '%';
     572
     573                fmtstr[sizeof(CFMTNP)-2] = f.base;                                      // sizeof includes '\0'
     574                // printf( "%d %s\n", f.wd, &fmtstr[star] );
     575                fmt( os, &fmtstr[star], f.wd, f.val );
     576                return os;
     577        } // ?|?
     578        void ?|?( ostype & os, _Ostream_Manip(char) f ) { (ostype &)(os | f); nl( os ); }
     579} // distribution
     580
     581//*********************************** C String ***********************************
     582
     583forall( dtype ostype | ostream( ostype ) ) {
     584        ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f ) {
     585                if ( ! f.val ) return os;                                               // null pointer ?
     586
     587                if ( f.base != 's' ) {                                                  // bespoke binary/octal/hex format
     588                        _Ostream_Manip(unsigned char) fmtuc @= { 0, f.wd, f.pc, f.base, {'\0'} };
     589                        fmtuc.flags.pc = f.flags.pc;
     590                        fmtuc.flags.nobsdp = f.flags.nobsdp;
     591                        for ( unsigned int i = 0; f.val[i] != '\0'; i += 1 ) {
     592                                fmtuc.val = f.val[i];
     593//                              os | fmtuc | nonl;
     594                                (ostype &)(os | fmtuc);
     595                        } // for
     596                        return os;
     597                } // if
     598
     599                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
     600
     601                #define SFMTNP "% * "
     602                #define SFMTP "% *.* "
     603                char fmtstr[sizeof(SFMTP)];                                             // sizeof includes '\0'
     604                if ( ! f.flags.pc ) memcpy( &fmtstr, SFMTNP, sizeof(SFMTNP) );
     605                else memcpy( &fmtstr, SFMTP, sizeof(SFMTP) );
     606                int star = 1;                                                                   // position before first '*'
     607
     608                // Insert flags into spaces before '*', from right to left.
     609                if ( f.flags.left ) { fmtstr[star] = '-'; star -= 1; }
     610                fmtstr[star] = '%';
     611
     612                if ( ! f.flags.pc ) {                                                   // no precision
     613                        // printf( "%d %s\n", f.wd, &fmtstr[star] );
     614                        fmtstr[sizeof(SFMTNP)-2] = f.base;                      // sizeof includes '\0'
     615                        fmt( os, &fmtstr[star], f.wd, f.val );
     616                } else {                                                                                // precision
     617                        fmtstr[sizeof(SFMTP)-2] = f.base;                       // sizeof includes '\0'
     618                        // printf( "%d %d %s\n", f.wd, f.pc, &fmtstr[star] );
     619                        fmt( os, &fmtstr[star], f.wd, f.pc, f.val );
     620                } // if
     621                return os;
     622        } // ?|?
     623        void ?|?( ostype & os, _Ostream_Manip(const char *) f ) { (ostype &)(os | f); nl( os ); }
     624} // distribution
     625
     626
     627//*********************************** Istream ***********************************
     628
    413629
    414630forall( dtype istype | istream( istype ) ) {
     
    437653
    438654        istype & ?|?( istype & is, signed char & sc ) {
    439                 fmt( is, "%hhd", &sc );
     655                fmt( is, "%hhi", &sc );
    440656                return is;
    441657        } // ?|?
    442658
    443659        istype & ?|?( istype & is, unsigned char & usc ) {
    444                 fmt( is, "%hhu", &usc );
     660                fmt( is, "%hhi", &usc );
    445661                return is;
    446662        } // ?|?
    447663
    448664        istype & ?|?( istype & is, short int & si ) {
    449                 fmt( is, "%hd", &si );
     665                fmt( is, "%hi", &si );
    450666                return is;
    451667        } // ?|?
    452668
    453669        istype & ?|?( istype & is, unsigned short int & usi ) {
    454                 fmt( is, "%hu", &usi );
     670                fmt( is, "%hi", &usi );
    455671                return is;
    456672        } // ?|?
    457673
    458674        istype & ?|?( istype & is, int & i ) {
    459                 fmt( is, "%d", &i );
     675                fmt( is, "%i", &i );
    460676                return is;
    461677        } // ?|?
    462678
    463679        istype & ?|?( istype & is, unsigned int & ui ) {
    464                 fmt( is, "%u", &ui );
     680                fmt( is, "%i", &ui );
    465681                return is;
    466682        } // ?|?
    467683
    468684        istype & ?|?( istype & is, long int & li ) {
    469                 fmt( is, "%ld", &li );
     685                fmt( is, "%li", &li );
    470686                return is;
    471687        } // ?|?
    472688
    473689        istype & ?|?( istype & is, unsigned long int & ulli ) {
    474                 fmt( is, "%lu", &ulli );
     690                fmt( is, "%li", &ulli );
    475691                return is;
    476692        } // ?|?
    477693
    478694        istype & ?|?( istype & is, long long int & lli ) {
    479                 fmt( is, "%lld", &lli );
     695                fmt( is, "%lli", &lli );
    480696                return is;
    481697        } // ?|?
    482698
    483699        istype & ?|?( istype & is, unsigned long long int & ulli ) {
    484                 fmt( is, "%llu", &ulli );
     700                fmt( is, "%lli", &ulli );
    485701                return is;
    486702        } // ?|?
     
    505721        istype & ?|?( istype & is, float _Complex & fc ) {
    506722                float re, im;
    507                 fmt( is, "%g%gi", &re, &im );
     723                fmt( is, "%f%fi", &re, &im );
    508724                fc = re + im * _Complex_I;
    509725                return is;
     
    545761} // distribution
    546762
    547 _Istream_cstrUC cstr( char * str ) { return (_Istream_cstrUC){ str }; }
     763//*********************************** Manipulators ***********************************
     764
    548765forall( dtype istype | istream( istype ) )
    549 istype & ?|?( istype & is, _Istream_cstrUC cstr ) {
    550         fmt( is, "%s", cstr.s );
     766istype & ?|?( istype & is, _Istream_Cstr f ) {
     767        // skip xxx
     768        if ( ! f.s ) {
     769                // printf( "skip %s\n", f.scanset );
     770                fmt( is, f.scanset, "" );                                               // no input arguments
     771                return is;
     772        } // if
     773        size_t len = 0;
     774        if ( f.scanset ) len = strlen( f.scanset );
     775        char fmtstr[len + 16];
     776        int start = 1;
     777        fmtstr[0] = '%';
     778        if ( f.flags.ignore ) { fmtstr[1] = '*'; start += 1; }
     779        if ( f.wd != -1 ) { start += sprintf( &fmtstr[start], "%d", f.wd ); }
     780        // cstr %s, %*s, %ws, %*ws
     781        if ( ! f.scanset ) {
     782                fmtstr[start] = 's'; fmtstr[start + 1] = '\0';
     783                // printf( "cstr %s\n", fmtstr );
     784                fmt( is, fmtstr, f.s );
     785                return is;
     786        } // if
     787        // incl %[xxx],  %*[xxx],  %w[xxx],  %*w[xxx]
     788        // excl %[^xxx], %*[^xxx], %w[^xxx], %*w[^xxx]
     789        fmtstr[start] = '['; start += 1;
     790        if ( f.flags.inex ) { fmtstr[start] = '^'; start += 1; }
     791        strcpy( &fmtstr[start], f.scanset );                            // copy includes '\0'
     792        len += start;
     793        fmtstr[len] = ']'; fmtstr[len + 1] = '\0';
     794        // printf( "incl/excl %s\n", fmtstr );
     795        fmt( is, fmtstr, f.s );
    551796        return is;
    552 } // cstr
    553 
    554 _Istream_cstrC cstr( char * str, int size ) { return (_Istream_cstrC){ str, size }; }
     797} // ?|?
     798
     799#define InputFMTImpl( T, CODE ) \
     800forall( dtype istype | istream( istype ) ) \
     801istype & ?|?( istype & is, _Istream_Manip(T) f ) { \
     802        enum { size = 16 }; \
     803        char fmtstr[size]; \
     804        if ( f.wd == -1 || strcmp( CODE, "c" ) == 0 ) { /* ignore width with "c" */     \
     805                snprintf( fmtstr, size, "%%%s%s", f.ignore ? "*" : "", CODE ); \
     806        } else { \
     807                snprintf( fmtstr, size, "%%%s%d%s", f.ignore ? "*" : "", f.wd, CODE ); \
     808        } /* if */ \
     809        /* printf( "%d %s %p\n", f.wd, fmtstr, &f.val ); */ \
     810        fmt( is, fmtstr, &f.val ); \
     811        return is; \
     812} // ?|?
     813
     814InputFMTImpl( char, "c" )
     815InputFMTImpl( signed char, "hhi" )
     816InputFMTImpl( unsigned char, "hhi" )
     817InputFMTImpl( signed short int, "hi" )
     818InputFMTImpl( unsigned short int, "hi" )
     819InputFMTImpl( signed int, "i" )
     820InputFMTImpl( unsigned int, "i" )
     821InputFMTImpl( signed long int, "li" )
     822InputFMTImpl( unsigned long int, "li" )
     823InputFMTImpl( signed long long int, "lli" )
     824InputFMTImpl( unsigned long long int, "lli" )
     825
     826InputFMTImpl( float, "f" )
     827InputFMTImpl( double, "lf" )
     828InputFMTImpl( long double, "Lf" )
     829
    555830forall( dtype istype | istream( istype ) )
    556 istype & ?|?( istype & is, _Istream_cstrC cstr ) {
    557         char buf[16];
    558         sprintf( buf, "%%%ds", cstr.size );
    559         fmt( is, buf, cstr.s );
     831istype & ?|?( istype & is, _Istream_Manip(float _Complex) fc ) {
     832        float re, im;
     833        _Istream_Manip(float) fmtuc @= { re, fc.wd, fc.ignore };
     834        is | fmtuc;
     835        &fmtuc.val = &im;
     836        is | fmtuc;
     837        if ( ! fc.ignore ) fc.val = re + im * _Complex_I;       // re/im are uninitialized for ignore
    560838        return is;
    561 } // cstr
     839} // ?|?
     840
     841forall( dtype istype | istream( istype ) )
     842istype & ?|?( istype & is, _Istream_Manip(double _Complex) dc ) {
     843        double re, im;
     844        _Istream_Manip(double) fmtuc @= { re, dc.wd, dc.ignore };
     845        is | fmtuc;
     846        &fmtuc.val = &im;
     847        is | fmtuc;
     848        if ( ! dc.ignore ) dc.val = re + im * _Complex_I;       // re/im are uninitialized for ignore
     849        return is;
     850} // ?|?
     851
     852forall( dtype istype | istream( istype ) )
     853istype & ?|?( istype & is, _Istream_Manip(long double _Complex) ldc ) {
     854        long double re, im;
     855        _Istream_Manip(long double) fmtuc @= { re, ldc.wd, ldc.ignore };
     856        is | fmtuc;
     857        &fmtuc.val = &im;
     858        is | fmtuc;
     859        if ( ! ldc.ignore ) ldc.val = re + im * _Complex_I;     // re/im are uninitialized for ignore
     860        return is;
     861} // ?|?
    562862
    563863// Local Variables: //
  • libcfa/src/iostream.hfa

    r9856ca9 re7f8119  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // iostream --
     7// iostream.hfa --
    88//
    99// Author           : Peter A. Buhr
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat May 11 10:31:27 2019
    13 // Update Count     : 232
     12// Last Modified On : Sat Jun  8 17:28:44 2019
     13// Update Count     : 312
    1414//
    1515
     
    1717
    1818#include "iterator.hfa"
     19
     20
     21//*********************************** Ostream ***********************************
     22
    1923
    2024trait ostream( dtype ostype ) {
     
    146150} // distribution
    147151
    148 //---------------------------------------
     152//*********************************** Manipulators ***********************************
     153
     154forall( otype T )
     155struct _Ostream_Manip {
     156        T val;                                                                                          // polymorphic base-type
     157        unsigned char wd, pc;                                                           // width, precision
     158        char base;                                                                                      // numeric base / floating-point style
     159        union {
     160                unsigned char all;
     161                struct {
     162                        unsigned char pc:1;                                                     // precision specified
     163                        unsigned char left:1;                                           // left justify
     164                        unsigned char nobsdp:1;                                         // base prefix / decimal point
     165                        unsigned char sign:1;                                           // plus / minus sign
     166                        unsigned char pad0:1;                                           // zero pad
     167                } flags;
     168        };
     169}; // _Ostream_Manip
     170
     171//*********************************** Integral ***********************************
     172
     173// See 6.7.9. 19) The initialization shall occur in initializer list order, each initializer provided for a particular
     174// subobject overriding any previously listed initializer for the same subobject; ***all subobjects that are not
     175// initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.***
     176
     177#define IntegralFMTDecl( T, CODE ) \
     178static inline { \
     179        _Ostream_Manip(T) bin( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'b', { .all : 0 } }; } \
     180        _Ostream_Manip(T) oct( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'o', { .all : 0 } }; } \
     181        _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'x', { .all : 0 } }; } \
     182        _Ostream_Manip(T) wd( unsigned char w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, CODE, { .all : 0 } }; } \
     183        _Ostream_Manip(T) wd( unsigned char w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, CODE, { .flags.pc : true } }; } \
     184        _Ostream_Manip(T) & wd( unsigned char w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \
     185        _Ostream_Manip(T) & wd( unsigned char w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \
     186        _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \
     187        _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; } \
     188        _Ostream_Manip(T) & nobase( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \
     189        _Ostream_Manip(T) & pad0( _Ostream_Manip(T) & fmt ) { fmt.flags.pad0 = true; return fmt; } \
     190        _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, CODE, { .flags.sign : true } }; } \
     191        _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \
     192} \
     193forall( dtype ostype | ostream( ostype ) ) { \
     194        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
     195        void ?|?( ostype & os, _Ostream_Manip(T) f ); \
     196} // ?|?
     197
     198IntegralFMTDecl( signed char, 'd' )
     199IntegralFMTDecl( unsigned char, 'u' )
     200IntegralFMTDecl( signed short int, 'd' )
     201IntegralFMTDecl( unsigned short int, 'u' )
     202IntegralFMTDecl( signed int, 'd' )
     203IntegralFMTDecl( unsigned int, 'u' )
     204IntegralFMTDecl( signed long int, 'd' )
     205IntegralFMTDecl( unsigned long int, 'u' )
     206IntegralFMTDecl( signed long long int, 'd' )
     207IntegralFMTDecl( unsigned long long int, 'u' )
     208
     209//*********************************** Floating Point ***********************************
     210
     211// Default suffix for values with no fraction is "."
     212#define FloatingPointFMTDecl( T ) \
     213static inline { \
     214        _Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \
     215        _Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'e', { .all : 0 } }; } \
     216        _Ostream_Manip(T) wd( unsigned char w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'f', { .all : 0 } }; } \
     217        _Ostream_Manip(T) wd( unsigned char w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \
     218        _Ostream_Manip(T) ws( unsigned char w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \
     219        _Ostream_Manip(T) & wd( unsigned char w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \
     220        _Ostream_Manip(T) & wd( unsigned char w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \
     221        _Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \
     222        _Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'G', { .all : 0 } }; } \
     223        _Ostream_Manip(T) & upcase( _Ostream_Manip(T) & fmt ) { fmt.base -= 32; /* upper case */ return fmt; } \
     224        _Ostream_Manip(T) & pad0( _Ostream_Manip(T) & fmt ) { fmt.flags.pad0 = true; return fmt; } \
     225        _Ostream_Manip(T) sign( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.sign : true } }; } \
     226        _Ostream_Manip(T) & sign( _Ostream_Manip(T) & fmt ) { fmt.flags.sign = true; return fmt; } \
     227        _Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \
     228        _Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \
     229} \
     230forall( dtype ostype | ostream( ostype ) ) { \
     231        ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
     232        void ?|?( ostype & os, _Ostream_Manip(T) f ); \
     233} // ?|?
     234
     235FloatingPointFMTDecl( double )
     236FloatingPointFMTDecl( long double )
     237
     238//*********************************** Character ***********************************
     239
     240static inline {
     241        _Ostream_Manip(char) bin( char val ) { return (_Ostream_Manip(char))@{ val, 1, 0, 'b', { .all : 0 } }; }
     242        _Ostream_Manip(char) oct( char val ) { return (_Ostream_Manip(char))@{ val, 1, 0, 'o', { .all : 0 } }; }
     243        _Ostream_Manip(char) hex( char val ) { return (_Ostream_Manip(char))@{ val, 1, 0, 'x', { .all : 0 } }; }
     244        _Ostream_Manip(char) wd( unsigned char w, char val ) { return (_Ostream_Manip(char))@{ val, w, 0, 'c', { .all : 0 } }; }
     245        _Ostream_Manip(char) & wd( unsigned char w, _Ostream_Manip(char) & fmt ) { fmt.wd = w; return fmt; }
     246        _Ostream_Manip(char) & left( _Ostream_Manip(char) & fmt ) { fmt.flags.left = true; return fmt; }
     247        _Ostream_Manip(char) & upcase( _Ostream_Manip(char) & fmt ) { if ( fmt.base == 'x' || fmt.base == 'b' ) fmt.base -= 32; /* upper case */ return fmt; }
     248        _Ostream_Manip(char) & nobase( _Ostream_Manip(char) & fmt ) { fmt.flags.nobsdp = true; return fmt; }
     249} // distribution
     250forall( dtype ostype | ostream( ostype ) ) {
     251        ostype & ?|?( ostype & os, _Ostream_Manip(char) f );
     252        void ?|?( ostype & os, _Ostream_Manip(char) f );
     253} // ?|?
     254
     255//*********************************** C String ***********************************
     256
     257static inline {
     258        _Ostream_Manip(const char *) bin( const char * val ) { return (_Ostream_Manip(const char *))@{ val, 1, 0, 'b', { .all : 0 } }; }
     259        _Ostream_Manip(const char *) oct( const char * val ) { return (_Ostream_Manip(const char *))@{ val, 1, 0, 'o', { .all : 0 } }; }
     260        _Ostream_Manip(const char *) hex( const char * val ) { return (_Ostream_Manip(const char *))@{ val, 1, 0, 'x', { .all : 0 } }; }
     261        _Ostream_Manip(const char *) wd( unsigned char w, const char * val ) { return (_Ostream_Manip(const char *))@{ val, w, 0, 's', { .all : 0 } }; }
     262        _Ostream_Manip(const char *) wd( unsigned char w, unsigned char pc, const char * val ) { return (_Ostream_Manip(const char *))@{ val, w, pc, 's', { .flags.pc : true } }; }
     263        _Ostream_Manip(const char *) & wd( unsigned char w, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; return fmt; }
     264        _Ostream_Manip(const char *) & wd( unsigned char w, unsigned char pc, _Ostream_Manip(const char *) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; }
     265        _Ostream_Manip(const char *) & left( _Ostream_Manip(const char *) & fmt ) { fmt.flags.left = true; return fmt; }
     266        _Ostream_Manip(const char *) & nobase( _Ostream_Manip(const char *) & fmt ) { fmt.flags.nobsdp = true; return fmt; }
     267} // distribution
     268forall( dtype ostype | ostream( ostype ) ) {
     269        ostype & ?|?( ostype & os, _Ostream_Manip(const char *) f );
     270        void ?|?( ostype & os, _Ostream_Manip(const char *) f );
     271} // ?|?
     272
     273
     274//*********************************** Istream ***********************************
     275
    149276
    150277trait istream( dtype istype ) {
     
    189316        istype & ?|?( istype &, long double _Complex & );
    190317
     318        // Cannot have char & and char * => cstr manipulator
     319        // istype & ?|?( istype &, char * );
     320
    191321        // manipulators
    192322        istype & ?|?( istype &, istype & (*)( istype & ) );
     
    196326} // distribution
    197327
    198 struct _Istream_cstrUC { char * s; };
    199 _Istream_cstrUC cstr( char * );
    200 forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_cstrUC );
    201 
    202 struct _Istream_cstrC { char * s; int size; };
    203 _Istream_cstrC cstr( char *, int size );
    204 forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_cstrC );
     328//*********************************** Manipulators ***********************************
     329
     330struct _Istream_Cstr {
     331        char * s;
     332        const char * scanset;
     333        int wd;                                                                                         // width
     334        union {
     335                unsigned char all;
     336                struct {
     337                        unsigned char ignore:1;                                         // do not change input argument
     338                        unsigned char inex:1;                                           // include/exclude characters in scanset
     339                } flags;
     340        };
     341}; // _Istream_Cstr
     342
     343static inline _Istream_Cstr skip( const char * scanset ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; }
     344static inline _Istream_Cstr incl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; }
     345static inline _Istream_Cstr incl( const char * scanset, _Istream_Cstr & fmt ) { fmt.flags.inex = false; return fmt; }
     346static inline _Istream_Cstr excl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; }
     347static inline _Istream_Cstr excl( const char * scanset, _Istream_Cstr & fmt ) { fmt.flags.inex = true; return fmt; }
     348static inline _Istream_Cstr cstr( char * s ) { return (_Istream_Cstr){ s, 0p, -1, { .all : 0 } }; }
     349static inline _Istream_Cstr ignore( const char * s ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }
     350static inline _Istream_Cstr ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; }
     351static inline _Istream_Cstr wd( unsigned int w, char * s ) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; }
     352static inline _Istream_Cstr wd( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; }
     353forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_Cstr );
     354
     355forall( otype T )
     356struct _Istream_Manip {
     357        T & val;                                                                                        // polymorphic base-type
     358        int wd;                                                                                         // width
     359        bool ignore;                                                                            // do not change input argument
     360}; // _Istream_Manip
     361
     362#define InputFMTDecl( T ) \
     363static inline _Istream_Manip(T) ignore( const T & val ) { return (_Istream_Manip(T))@{ (T &)val, -1, true }; } \
     364static inline _Istream_Manip(T) ignore( _Istream_Manip(T) & fmt ) { fmt.ignore = true; return fmt; } \
     365static inline _Istream_Manip(T) wd( unsigned int w, T & val ) { return (_Istream_Manip(T))@{ val, w, false }; } \
     366forall( dtype istype | istream( istype ) ) { \
     367        istype & ?|?( istype & is, _Istream_Manip(T) f ); \
     368} // ?|?
     369
     370InputFMTDecl( char )
     371InputFMTDecl( signed char )
     372InputFMTDecl( unsigned char )
     373InputFMTDecl( signed short int )
     374InputFMTDecl( unsigned short int )
     375InputFMTDecl( signed int )
     376InputFMTDecl( unsigned int )
     377InputFMTDecl( signed long int )
     378InputFMTDecl( unsigned long int )
     379InputFMTDecl( signed long long int )
     380InputFMTDecl( unsigned long long int )
     381
     382InputFMTDecl( float )
     383InputFMTDecl( double )
     384InputFMTDecl( long double )
     385
     386InputFMTDecl( float _Complex )
     387InputFMTDecl( double _Complex )
     388InputFMTDecl( long double _Complex )
     389
     390
     391//*********************************** Time ***********************************
    205392
    206393
  • src/AST/Convert.cpp

    r9856ca9 re7f8119  
    1616#include "Convert.hpp"
    1717
     18#include <deque>
    1819#include <unordered_map>
    1920
     
    575576
    576577                if ( srcInferred.mode == ast::Expr::InferUnion::Params ) {
    577                         const ast::InferredParams &srcParams = srcInferred.inferParamsConst();
     578                        const ast::InferredParams &srcParams = srcInferred.inferParams();
    578579                        for (auto srcParam : srcParams) {
    579580                                tgtInferParams[srcParam.first] = ParamEntry(
     
    585586                        }
    586587                } else if ( srcInferred.mode == ast::Expr::InferUnion::Slots  ) {
    587                         const ast::ResnSlots &srcSlots = srcInferred.resnSlotsConst();
     588                        const ast::ResnSlots &srcSlots = srcInferred.resnSlots();
    588589                        for (auto srcSlot : srcSlots) {
    589590                                tgtResnSlots.push_back(srcSlot);
     
    14211422#       define GET_ACCEPT_V(child, type) \
    14221423                getAcceptV< ast::type, decltype( old->child ) >( old->child )
     1424       
     1425        template<typename NewT, typename OldC>
     1426        std::deque< ast::ptr<NewT> > getAcceptD( OldC& old ) {
     1427                std::deque< ast::ptr<NewT> > ret;
     1428                for ( auto a : old ) {
     1429                        a->accept( *this );
     1430                        ret.emplace_back( strict_dynamic_cast< NewT * >(node) );
     1431                        node = nullptr;
     1432                }
     1433                return ret;
     1434        }
     1435
     1436#       define GET_ACCEPT_D(child, type) \
     1437                getAcceptD< ast::type, decltype( old->child ) >( old->child )
    14231438
    14241439        ast::Label make_label(Label* old) {
     
    24622477
    24632478        virtual void visit( UntypedInitExpr * old ) override final {
    2464                 std::vector<ast::InitAlternative> initAlts;
     2479                std::deque<ast::InitAlternative> initAlts;
    24652480                for (auto ia : old->initAlts) {
    24662481                        initAlts.push_back(ast::InitAlternative(
     
    27272742                this->node = new ast::Designation(
    27282743                        old->location,
    2729                         GET_ACCEPT_V(designators, Expr)
     2744                        GET_ACCEPT_D(designators, Expr)
    27302745                );
    27312746        }
  • src/AST/Expr.cpp

    r9856ca9 re7f8119  
    163163        result = mem->get_type();
    164164        // substitute aggregate generic parameters into member type
    165         genericSubsitution( aggregate->result ).apply( result );
     165        genericSubstitution( aggregate->result ).apply( result );
    166166        // ensure lvalue and appropriate restrictions from aggregate type
    167167        add_qualifiers( result, aggregate->result->qualifiers | CV::Lvalue );
  • src/AST/Expr.hpp

    r9856ca9 re7f8119  
    1717
    1818#include <cassert>
     19#include <deque>
    1920#include <map>
    2021#include <string>
     
    111112                }
    112113
    113                 const ResnSlots& resnSlotsConst() const {
     114                const ResnSlots& resnSlots() const {
    114115                        if (mode == Slots) {
    115116                                return data.resnSlots;
     
    128129                }
    129130
    130                 const InferredParams& inferParamsConst() const {
     131                const InferredParams& inferParams() const {
    131132                        if (mode == Params) {
    132133                                return data.inferParams;
     
    134135                        assert(!"Mode was not already Params");
    135136                        return *((InferredParams*)nullptr);
     137                }
     138
     139                /// splices other InferUnion into this one. Will fail if one union is in `Slots` mode
     140                /// and the other is in `Params`.
     141                void splice( InferUnion && o ) {
     142                        if ( o.mode == Empty ) return;
     143                        if ( mode == Empty ) { init_from( o ); return; }
     144                        assert( mode == o.mode && "attempt to splice incompatible InferUnion" );
     145
     146                        if ( mode == Slots ){
     147                                data.resnSlots.insert(
     148                                        data.resnSlots.end(), o.data.resnSlots.begin(), o.data.resnSlots.end() );
     149                        } else if ( mode == Params ) {
     150                                for ( const auto & p : o.data.inferParams ) {
     151                                        data.inferParams[p.first] = std::move(p.second);
     152                                }
     153                        } else assert(!"invalid mode");
    136154                }
    137155        };
     
    695713public:
    696714        ptr<Expr> expr;
    697         std::vector<InitAlternative> initAlts;
    698 
    699         UntypedInitExpr( const CodeLocation & loc, const Expr * e, std::vector<InitAlternative> && as )
     715        std::deque<InitAlternative> initAlts;
     716
     717        UntypedInitExpr( const CodeLocation & loc, const Expr * e, std::deque<InitAlternative> && as )
    700718        : Expr( loc ), expr( e ), initAlts( std::move(as) ) {}
    701719
  • src/AST/GenericSubstitution.cpp

    r9856ca9 re7f8119  
    3131                TypeSubstitution sub;
    3232
    33                 void previsit( const Type * ty ) {
    34                         assertf( false, "Attempted generic substitution for non-aggregate type: %s",
    35                                 toString( ty ).c_str() );
     33                void previsit( const Type * ) {
     34                        // allow empty substitution for non-generic type
     35                        visit_children = false;
    3636                }
    3737
     
    4040                }
    4141
    42                 void previsit( const ReferenceToType * ty ) {
     42        private:
     43                // make substitution for generic type
     44                void makeSub( const ReferenceToType * ty ) {
    4345                        visit_children = false;
    44                         // build substitution from base parameters
    4546                        const AggregateDecl * aggr = ty->aggr();
    4647                        sub = TypeSubstitution{ aggr->params.begin(), aggr->params.end(), ty->params.begin() };
     48                }
     49
     50        public:
     51                void previsit( const StructInstType * ty ) {
     52                        makeSub( ty );
     53                }
     54
     55                void previsit( const UnionInstType * ty ) {
     56                        makeSub( ty );
    4757                }
    4858        };
    4959}
    5060
    51 TypeSubstitution genericSubsitution( const Type * ty ) {
     61TypeSubstitution genericSubstitution( const Type * ty ) {
    5262        Pass<GenericSubstitutionBuilder> builder;
    5363        maybe_accept( ty, builder );
  • src/AST/GenericSubstitution.hpp

    r9856ca9 re7f8119  
    2222class Type;
    2323
    24 TypeSubstitution genericSubsitution( const Type * );
     24TypeSubstitution genericSubstitution( const Type * );
    2525
    2626}
  • src/AST/Init.hpp

    r9856ca9 re7f8119  
    1616#pragma once
    1717
     18#include <deque>
    1819#include <utility>        // for move
    1920#include <vector>
     
    3536class Designation final : public ParseNode {
    3637public:
    37         std::vector<ptr<Expr>> designators;
     38        std::deque<ptr<Expr>> designators;
    3839
    39         Designation( const CodeLocation& loc, std::vector<ptr<Expr>>&& ds = {} )
     40        Designation( const CodeLocation& loc, std::deque<ptr<Expr>>&& ds = {} )
    4041        : ParseNode( loc ), designators( std::move(ds) ) {}
    4142
  • src/AST/Node.hpp

    r9856ca9 re7f8119  
    154154
    155155        template< enum Node::ref_type o_ref_t >
    156         ptr_base( const ptr_base<node_t, o_ref_t> & o ) : node(o.node) {
     156        ptr_base( const ptr_base<node_t, o_ref_t> & o ) : node(o.get()) {
    157157                if( node ) _inc(node);
    158158        }
    159159
    160160        template< enum Node::ref_type o_ref_t >
    161         ptr_base( ptr_base<node_t, o_ref_t> && o ) : node(o.node) {
     161        ptr_base( ptr_base<node_t, o_ref_t> && o ) : node(o.get()) {
    162162                if( node ) _inc(node);
    163163        }
     
    184184        template< enum Node::ref_type o_ref_t >
    185185        ptr_base & operator=( const ptr_base<node_t, o_ref_t> & o ) {
    186                 assign(o.node);
     186                assign(o.get());
    187187                return *this;
    188188        }
     
    190190        template< enum Node::ref_type o_ref_t >
    191191        ptr_base & operator=( ptr_base<node_t, o_ref_t> && o ) {
    192                 assign(o.node);
     192                assign(o.get());
    193193                return *this;
    194194        }
     
    228228        void _check() const;
    229229
    230 protected:
    231230        const node_t * node;
    232231};
  • src/AST/porting.md

    r9856ca9 re7f8119  
    238238    * also now returns `const AggregateDecl *`
    239239* `genericSubstitution()` moved to own visitor in `AST/GenericSubstitution.hpp`
     240  * subsumes old `makeGenericSubstitution()`
    240241
    241242`BasicType`
  • src/Parser/parser.yy

    r9856ca9 re7f8119  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed May 15 21:25:27 2019
    13 // Update Count     : 4296
     12// Last Modified On : Tue May 28 17:06:37 2019
     13// Update Count     : 4354
    1414//
    1515
     
    278278%token OTYPE FTYPE DTYPE TTYPE TRAIT                                    // CFA
    279279%token SIZEOF OFFSETOF
     280// %token SUSPEND RESUME                                                                        // CFA
    280281%token ATTRIBUTE EXTENSION                                                              // GCC
    281282%token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN
     
    482483%precedence '}'
    483484%precedence '('
     485
     486// %precedence RESUME
     487// %precedence '{'
     488// %precedence ')'
    484489
    485490%locations                                                                                              // support location tracking for error messages
     
    599604                        $$ = new ExpressionNode( $5 );
    600605                }
     606        // | RESUME '(' comma_expression ')'
     607        //      { SemanticError( yylloc, "Resume expression is currently unimplemented." ); $$ = nullptr; }
     608        // | RESUME '(' comma_expression ')' compound_statement
     609        //      { SemanticError( yylloc, "Resume expression is currently unimplemented." ); $$ = nullptr; }
    601610        ;
    602611
     
    12631272        | RETURN comma_expression_opt ';'
    12641273                { $$ = new StatementNode( build_return( $2 ) ); }
    1265         | RETURN '{' initializer_list_opt comma_opt '}'
     1274        | RETURN '{' initializer_list_opt comma_opt '}' ';'
    12661275                { SemanticError( yylloc, "Initializer return is currently unimplemented." ); $$ = nullptr; }
     1276        // | SUSPEND ';'
     1277        //      { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; }
     1278        // | SUSPEND compound_statement ';'
     1279        //      { SemanticError( yylloc, "Suspend expression is currently unimplemented." ); $$ = nullptr; }
    12671280        | THROW assignment_expression_opt ';'                           // handles rethrow
    12681281                { $$ = new StatementNode( build_throw( $2 ) ); }
     
    21582171
    21592172bit_subrange_size:
    2160         ':' constant_expression
     2173        ':' assignment_expression
    21612174                { $$ = $2; }
    21622175        ;
  • src/ResolvExpr/CurrentObject.cc

    r9856ca9 re7f8119  
    1616#include <stddef.h>                    // for size_t
    1717#include <cassert>                     // for assertf, assert, safe_dynamic_...
     18#include <deque>
    1819#include <iostream>                    // for ostream, operator<<, basic_ost...
    1920#include <stack>                       // for stack
     
    2122
    2223#include "AST/Expr.hpp"                // for InitAlternative
     24#include "AST/GenericSubstitution.hpp" // for genericSubstitution
    2325#include "AST/Init.hpp"                // for Designation
    2426#include "AST/Node.hpp"                // for readonly
     27#include "AST/Type.hpp"
    2528#include "Common/Indenter.h"           // for Indenter, operator<<
    2629#include "Common/SemanticError.h"      // for SemanticError
     
    583586
    584587namespace ast {
    585 
    586         /// Iterates members of a type by initializer
    587         class MemberIterator {
    588         public:
    589                 virtual ~MemberIterator() {}
    590 
    591                 /// retrieve the list of possible (Type,Designation) pairs for the current position in the
    592                 /// current object
    593                 virtual std::vector< InitAlternative > operator* () const = 0;
    594        
    595         protected:
    596                 /// helper for operator*; aggregates must add designator to each init alternative, but
    597                 /// adding designators in operator* creates duplicates
    598                 virtual std::vector< InitAlternative > first() const = 0;
    599         };
     588        /// create a new MemberIterator that traverses a type correctly
     589        MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type );
    600590
    601591        /// Iterates "other" types (e.g. basic, pointer) which do not change at list initializer entry
     
    606596                SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
    607597
    608                 std::vector< InitAlternative > operator* () const override { return first(); }
    609 
    610         protected:
    611                 std::vector< InitAlternative > first() const override {
     598                void setPosition(
     599                        std::deque< ptr< Expr > >::const_iterator begin,
     600                        std::deque< ptr< Expr > >::const_iterator end
     601                ) override {
     602                        if ( begin != end ) {
     603                                SemanticError( location, "Un-designated initializer given non-empty designator" );
     604                        }
     605                }
     606
     607                std::deque< InitAlternative > operator* () const override { return first(); }
     608
     609                operator bool() const override { return type; }
     610
     611                SimpleIterator & bigStep() override { return smallStep(); }
     612                SimpleIterator & smallStep() override {
     613                        type = nullptr;  // empty on increment because no members
     614                        return *this;
     615                }
     616
     617                const Type * getType() override { return type; }
     618
     619                const Type * getNext() override { return type; }
     620
     621                std::deque< InitAlternative > first() const override {
    612622                        if ( type ) return { InitAlternative{ type, new Designation{ location } } };
    613623                        return {};
     
    615625        };
    616626
     627        /// Iterates array types
     628        class ArrayIterator final : public MemberIterator {
     629                CodeLocation location;
     630                readonly< ArrayType > array = nullptr;
     631                readonly< Type > base = nullptr;
     632                size_t index = 0;
     633                size_t size = 0;
     634                std::unique_ptr< MemberIterator > memberIter;
     635
     636                void setSize( const Expr * expr ) {
     637                        auto res = eval(expr);
     638                        if ( ! res.second ) {
     639                                SemanticError( location,
     640                                        toString("Array designator must be a constant expression: ", expr ) );
     641                        }
     642                        size = res.first;
     643                }
     644
     645        public:
     646                ArrayIterator( const CodeLocation & loc, const ArrayType * at )
     647                : location( loc ), array( at ), base( at->base ) {
     648                        PRINT( std::cerr << "Creating array iterator: " << at << std::endl; )
     649                        memberIter.reset( createMemberIterator( loc, base ) );
     650                        if ( at->isVarLen ) {
     651                                SemanticError( location, at, "VLA initialization does not support @=: " );
     652                        }
     653                        setSize( at->dimension );
     654                }
     655
     656                void setPosition( const Expr * expr ) {
     657                        // need to permit integer-constant-expressions, including: integer constants,
     658                        // enumeration constants, character constants, sizeof expressions, alignof expressions,
     659                        // cast expressions
     660                        if ( auto constExpr = dynamic_cast< const ConstantExpr * >( expr ) ) {
     661                                try {
     662                                        index = constExpr->intValue();
     663                                } catch ( SemanticErrorException & ) {
     664                                        SemanticError( expr,
     665                                                "Constant expression of non-integral type in array designator: " );
     666                                }
     667                        } else if ( auto castExpr = dynamic_cast< const CastExpr * >( expr ) ) {
     668                                setPosition( castExpr->arg );
     669                        } else if (
     670                                dynamic_cast< const SizeofExpr * >( expr )
     671                                || dynamic_cast< const AlignofExpr * >( expr )
     672                        ) {
     673                                index = 0;
     674                        } else {
     675                                assertf( false,
     676                                        "bad designator given to ArrayIterator: %s", toString( expr ).c_str() );
     677                        }
     678                }
     679
     680                void setPosition(
     681                        std::deque< ptr< Expr > >::const_iterator begin,
     682                        std::deque< ptr< Expr > >::const_iterator end
     683                ) override {
     684                        if ( begin == end ) return;
     685
     686                        setPosition( *begin );
     687                        memberIter->setPosition( ++begin, end );
     688                }
     689
     690                std::deque< InitAlternative > operator* () const override { return first(); }
     691
     692                operator bool() const override { return index < size; }
     693
     694                ArrayIterator & bigStep() override {
     695                        PRINT( std::cerr << "bigStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
     696                        ++index;
     697                        memberIter.reset( index < size ? createMemberIterator( location, base ) : nullptr );
     698                        return *this;
     699                }
     700
     701                ArrayIterator & smallStep() override {
     702                        PRINT( std::cerr << "smallStep in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
     703                        if ( memberIter ) {
     704                                PRINT( std::cerr << "has member iter: " << *memberIter << std::endl; )
     705                                memberIter->smallStep();
     706                                if ( *memberIter ) {
     707                                        PRINT( std::cerr << "has valid member iter" << std::endl; )
     708                                        return *this;
     709                                }
     710                        }
     711                        return bigStep();
     712                }
     713
     714                const Type * getType() override { return array; }
     715
     716                const Type * getNext() override { return base; }
     717
     718                std::deque< InitAlternative > first() const override {
     719                        PRINT( std::cerr << "first in ArrayIterator (" << index << "/" << size << ")" << std::endl; )
     720                        if ( memberIter && *memberIter ) {
     721                                std::deque< InitAlternative > ret = memberIter->first();
     722                                for ( InitAlternative & alt : ret ) {
     723                                        alt.designation.get_and_mutate()->designators.emplace_front(
     724                                                ConstantExpr::from_ulong( location, index ) );
     725                                }
     726                                return ret;
     727                        }
     728                        return {};
     729                }
     730        };
     731
     732        class AggregateIterator : public MemberIterator {
     733        protected:
     734                using MemberList = std::vector< ptr< Decl > >;
     735
     736                CodeLocation location;
     737                std::string kind;  // for debug
     738                std::string name;
     739                const Type * inst;
     740                const MemberList & members;
     741                MemberList::const_iterator curMember;
     742                bool atbegin = true;  // false at first {small,big}Step
     743                const Type * curType = nullptr;
     744                std::unique_ptr< MemberIterator > memberIter = nullptr;
     745                TypeSubstitution sub;
     746
     747                bool init() {
     748                        PRINT( std::cerr << "--init()--" << members.size() << std::endl; )
     749                        if ( curMember != members.end() ) {
     750                                if ( auto field = curMember->as< ObjectDecl >() ) {
     751                                        PRINT( std::cerr << "incremented to field: " << field << std::endl; )
     752                                        curType = field->get_type();
     753                                        memberIter.reset( createMemberIterator( location, curType ) );
     754                                        return true;
     755                                }
     756                        }
     757                        return false;
     758                }
     759
     760                AggregateIterator(
     761                        const CodeLocation & loc, const std::string k, const std::string & n, const Type * i,
     762                        const MemberList & ms )
     763                : location( loc ), kind( k ), name( n ), inst( i ), members( ms ), curMember( ms.begin() ),
     764                  sub( genericSubstitution( i ) ) {
     765                        PRINT( std::cerr << "Creating " << kind << "(" << name << ")"; )
     766                        init();
     767                }
     768
     769        public:
     770                void setPosition(
     771                        std::deque< ptr< Expr > >::const_iterator begin,
     772                        std::deque< ptr< Expr > >::const_iterator end
     773                ) final {
     774                        if ( begin == end ) return;
     775
     776                        if ( auto varExpr = begin->as< VariableExpr >() ) {
     777                                for ( curMember = members.begin(); curMember != members.end(); ++curMember ) {
     778                                        if ( *curMember != varExpr->var ) continue;
     779
     780                                        ++begin;
     781
     782                                        memberIter.reset( createMemberIterator( location, varExpr->result ) );
     783                                        curType = varExpr->result;
     784                                        atbegin = curMember == members.begin() && begin == end;
     785                                        memberIter->setPosition( begin, end );
     786                                        return;
     787                                }
     788                                assertf( false,
     789                                        "could not find member in %s: %s", kind.c_str(), toString( varExpr ).c_str() );
     790                        } else {
     791                                assertf( false,
     792                                        "bad designator given to %s: %s", kind.c_str(), toString( *begin ).c_str() );
     793                        }
     794                }
     795
     796                std::deque< InitAlternative > operator* () const final {
     797                        if ( memberIter && *memberIter ) {
     798                                std::deque< InitAlternative > ret = memberIter->first();
     799                                PRINT( std::cerr << "sub: " << sub << std::endl; )
     800                                for ( InitAlternative & alt : ret ) {
     801                                        PRINT( std::cerr << "iterating and adding designators" << std::endl; )
     802                                        alt.designation.get_and_mutate()->designators.emplace_front(
     803                                                new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
     804                                        // need to substitute for generic types so that casts are to concrete types
     805                                        PRINT( std::cerr << "  type is: " << alt.type; )
     806                                        sub.apply( alt.type ); // also apply to designation??
     807                                        PRINT( std::cerr << " ==> " << alt.type << std::endl; )
     808                                }
     809                                return ret;
     810                        }
     811                        return {};
     812                }
     813
     814                AggregateIterator & smallStep() final {
     815                        PRINT( std::cerr << "smallStep in " << kind << std::endl; )
     816                        atbegin = false;
     817                        if ( memberIter ) {
     818                                PRINT( std::cerr << "has member iter, incrementing..." << std::endl; )
     819                                memberIter->smallStep();
     820                                if ( *memberIter ) {
     821                                        PRINT( std::cerr << "success!" << std::endl; )
     822                                        return *this;
     823                                }
     824                        }
     825                        return bigStep();
     826                }
     827
     828                AggregateIterator & bigStep() override = 0;
     829
     830                const Type * getType() final { return inst; }
     831
     832                const Type * getNext() final {
     833                        return ( memberIter && *memberIter ) ? memberIter->getType() : nullptr;
     834                }
     835
     836                std::deque< InitAlternative > first() const final {
     837                        std::deque< InitAlternative > ret;
     838                        PRINT( std::cerr << "first " << kind << std::endl; )
     839                        if ( memberIter && *memberIter ) {
     840                                PRINT( std::cerr << "adding children" << std::endl; )
     841                                ret = memberIter->first();
     842                                for ( InitAlternative & alt : ret ) {
     843                                        PRINT( std::cerr << "iterating and adding designators" << std::endl; )
     844                                        alt.designation.get_and_mutate()->designators.emplace_front(
     845                                                new VariableExpr{ location, curMember->strict_as< ObjectDecl >() } );
     846                                }
     847                        }
     848                        if ( atbegin ) {
     849                                // only add self if at the very beginning of the structure
     850                                PRINT( std::cerr << "adding self" << std::endl; )
     851                                ret.emplace_front( inst, new Designation{ location } );
     852                        }
     853                        return ret;
     854                }
     855        };
     856
     857        class StructIterator final : public AggregateIterator {
     858        public:
     859                StructIterator( const CodeLocation & loc, const StructInstType * inst )
     860                : AggregateIterator( loc, "StructIterator", inst->name, inst, inst->base->members ) {}
     861
     862                operator bool() const override {
     863                        return curMember != members.end() || (memberIter && *memberIter);
     864                }
     865
     866                StructIterator & bigStep() override {
     867                        PRINT( std::cerr << "bigStep in " << kind << std::endl; )
     868                        atbegin = false;
     869                        memberIter = nullptr;
     870                        curType = nullptr;
     871                        while ( curMember != members.end() ) {
     872                                ++curMember;
     873                                if ( init() ) return *this;
     874                        }
     875                        return *this;
     876                }
     877        };
     878
     879        class UnionIterator final : public AggregateIterator {
     880        public:
     881                UnionIterator( const CodeLocation & loc, const UnionInstType * inst )
     882                : AggregateIterator( loc, "UnionIterator", inst->name, inst, inst->base->members ) {}
     883
     884                operator bool() const override { return memberIter && *memberIter; }
     885
     886                UnionIterator & bigStep() override {
     887                        // unions only initialize one member
     888                        PRINT( std::cerr << "bigStep in " << kind << std::endl; )
     889                        atbegin = false;
     890                        memberIter = nullptr;
     891                        curType = nullptr;
     892                        curMember = members.end();
     893                        return *this;
     894                }
     895        };
     896
     897        class TupleIterator final : public AggregateIterator {
     898        public:
     899                TupleIterator( const CodeLocation & loc, const TupleType * inst )
     900                : AggregateIterator(
     901                        loc, "TupleIterator", toString("Tuple", inst->size()), inst, inst->members
     902                ) {}
     903
     904                operator bool() const override {
     905                        return curMember != members.end() || (memberIter && *memberIter);
     906                }
     907
     908                TupleIterator & bigStep() override {
     909                        PRINT( std::cerr << "bigStep in " << kind << std::endl; )
     910                        atbegin = false;
     911                        memberIter = nullptr;
     912                        curType = nullptr;
     913                        while ( curMember != members.end() ) {
     914                                ++curMember;
     915                                if ( init() ) return *this;
     916                        }
     917                        return *this;
     918                }
     919        };
     920
     921        MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type ) {
     922                if ( auto aggr = dynamic_cast< const ReferenceToType * >( type ) ) {
     923                        if ( auto sit = dynamic_cast< const StructInstType * >( aggr ) ) {
     924                                return new StructIterator{ loc, sit };
     925                        } else if ( auto uit = dynamic_cast< const UnionInstType * >( aggr ) ) {
     926                                return new UnionIterator{ loc, uit };
     927                        } else {
     928                                assertf(
     929                                        dynamic_cast< const EnumInstType * >( aggr )
     930                                                || dynamic_cast< const TypeInstType * >( aggr ),
     931                                        "Encountered unhandled ReferenceToType in createMemberIterator: %s",
     932                                                toString( type ).c_str() );
     933                                return new SimpleIterator{ loc, type };
     934                        }
     935                } else if ( auto at = dynamic_cast< const ArrayType * >( type ) ) {
     936                        return new ArrayIterator{ loc, at };
     937                } else if ( auto tt = dynamic_cast< const TupleType * >( type ) ) {
     938                        return new TupleIterator{ loc, tt };
     939                } else {
     940                        return new SimpleIterator{ loc, type };
     941                }
     942        }
     943
    617944        CurrentObject::CurrentObject( const CodeLocation & loc, const Type * type ) : objStack() {
    618945                objStack.emplace_back( new SimpleIterator{ loc, type } );
    619946        }
    620947
    621         std::vector< InitAlternative > CurrentObject::getOptions() {
     948        void CurrentObject::setNext( const ast::Designation * designation ) {
     949                PRINT( std::cerr << "____setNext" << designation << std::endl; )
     950                assertf( ! objStack.empty(), "obj stack empty in setNext" );
     951                objStack.back()->setPosition( designation->designators );
     952        }
     953
     954        void CurrentObject::increment() {
     955                PRINT( std::cerr << "____increment" << std::endl; )
     956                if ( objStack.empty() ) return;
     957                PRINT( std::cerr << *objStack.back() << std::endl; )
     958                objStack.back()->smallStep();
     959        }
     960
     961        void CurrentObject::enterListInit( const CodeLocation & loc ) {
     962                PRINT( std::cerr << "____entering list init" << std::endl; )
     963                assertf( ! objStack.empty(), "empty obj stack entering list init" );
     964                const ast::Type * type = objStack.back()->getNext();
     965                assert( type );
     966                objStack.emplace_back( createMemberIterator( loc, type ) );
     967        }
     968
     969        void CurrentObject::exitListInit() {
     970                PRINT( std::cerr << "____exiting list init" << std::endl; )
     971                assertf( ! objStack.empty(), "objstack empty" );
     972                objStack.pop_back();
     973                if ( ! objStack.empty() ) {
     974                        PRINT( std::cerr << *objStack.back() << std::endl; )
     975                        objStack.back()->bigStep();
     976                }
     977        }
     978
     979        std::deque< InitAlternative > CurrentObject::getOptions() {
    622980                PRINT( std::cerr << "____getting current options" << std::endl; )
    623981                assertf( ! objStack.empty(), "objstack empty in getOptions" );
    624982                return **objStack.back();
     983        }
     984
     985        const Type * CurrentObject::getCurrentType() {
     986                PRINT( std::cerr << "____getting current type" << std::endl; )
     987                assertf( ! objStack.empty(), "objstack empty in getCurrentType" );
     988                return objStack.back()->getNext();
    625989        }
    626990}
  • src/ResolvExpr/CurrentObject.h

    r9856ca9 re7f8119  
    1616#pragma once
    1717
     18#include <deque>
    1819#include <list>   // for list
    1920#include <memory> // for unique_ptr
     
    2122#include <vector>
    2223
     24#include "AST/Node.hpp"  // for ptr
    2325#include "Common/CodeLocation.h"
    2426
     
    5961        // AST class types
    6062        class Designation;
    61         class InitAlternative;
     63        struct InitAlternative;
    6264        class Type;
    6365
    64         // forward declaration of internal detail
    65         class MemberIterator;
     66        /// Iterates members of a type by initializer
     67        class MemberIterator {
     68        public:
     69                virtual ~MemberIterator() {}
     70
     71                /// Internal set position based on iterator ranges
     72                virtual void setPosition(
     73                        std::deque< ptr< Expr > >::const_iterator it,
     74                        std::deque< ptr< Expr > >::const_iterator end ) = 0;
     75
     76                /// walks the current object using the given designators as a guide
     77                void setPosition( const std::deque< ptr< Expr > > & designators ) {
     78                        setPosition( designators.begin(), designators.end() );
     79                }
     80
     81                /// retrieve the list of possible (Type,Designation) pairs for the current position in the
     82                /// current object
     83                virtual std::deque< InitAlternative > operator* () const = 0;
     84
     85                /// true if the iterator is not currently at the end
     86                virtual operator bool() const = 0;
     87
     88                /// moves the iterator by one member in the current object
     89                virtual MemberIterator & bigStep() = 0;
     90
     91                /// moves the iterator by one member in the current subobject
     92                virtual MemberIterator & smallStep() = 0;
     93
     94                /// the type of the current object
     95                virtual const Type * getType() = 0;
     96
     97                /// the type of the current subobject
     98                virtual const Type * getNext() = 0;
     99       
     100                /// helper for operator*; aggregates must add designator to each init alternative, but
     101                /// adding designators in operator* creates duplicates
     102                virtual std::deque< InitAlternative > first() const = 0;
     103        };
    66104
    67105        /// Builds initializer lists in resolution
     
    73111                CurrentObject( const CodeLocation & loc, const Type * type );
    74112
     113                /// sets current position using the resolved designation
     114                void setNext( const ast::Designation * designation );
     115                /// steps to next sub-object of current object
     116                void increment();
     117                /// sets new current object for the duration of this brace-enclosed intializer-list
     118                void enterListInit( const CodeLocation & loc );
     119                /// restores previous current object
     120                void exitListInit();
    75121                /// produces a list of alternatives (Type *, Designation *) for the current sub-object's
    76122                /// initializer.
    77                 std::vector< InitAlternative > getOptions();
     123                std::deque< InitAlternative > getOptions();
     124                /// produces the type of the current object but no subobjects
     125                const Type * getCurrentType();
    78126        };
    79127} // namespace ast
  • src/ResolvExpr/Resolver.cc

    r9856ca9 re7f8119  
    781781        }
    782782
    783         template< typename T >
    784         bool isCharType( T t ) {
     783        bool isCharType( Type * t ) {
    785784                if ( BasicType * bt = dynamic_cast< BasicType * >( t ) ) {
    786785                        return bt->get_kind() == BasicType::Char || bt->get_kind() == BasicType::SignedChar ||
     
    10711070                };
    10721071
     1072                /// Swaps argument into expression pointer, saving original environment
     1073                void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) {
     1074                        ast::ptr< ast::TypeSubstitution > env = expr->env;
     1075                        expr.set_and_mutate( newExpr )->env = env;
     1076                }
     1077
    10731078                /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
    10741079                void removeExtraneousCast( ast::ptr<ast::Expr> & expr, const ast::SymbolTable & symtab ) {
     
    10761081                                if ( typesCompatible( castExpr->arg->result, castExpr->result, symtab ) ) {
    10771082                                        // cast is to the same type as its argument, remove it
    1078                                         ast::ptr< ast::TypeSubstitution > env = castExpr->env;
    1079                                         expr.set_and_mutate( castExpr->arg )->env = env;
     1083                                        swap_and_save_env( expr, castExpr->arg );
    10801084                                }
    10811085                        }
     
    11751179                        return findKindExpression( untyped, symtab, hasIntegralType, "condition" );
    11761180                }
     1181
     1182                /// check if a type is a character type
     1183                bool isCharType( const ast::Type * t ) {
     1184                        if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) {
     1185                                return bt->kind == ast::BasicType::Char
     1186                                        || bt->kind == ast::BasicType::SignedChar
     1187                                        || bt->kind == ast::BasicType::UnsignedChar;
     1188                        }
     1189                        return false;
     1190                }
    11771191        }
    11781192
     
    12131227                void previsit( const ast::WaitForStmt * );
    12141228
    1215                 void previsit( const ast::SingleInit * );
    1216                 void previsit( const ast::ListInit * );
     1229                const ast::SingleInit * previsit( const ast::SingleInit * );
     1230                const ast::ListInit * previsit( const ast::ListInit * );
    12171231                void previsit( const ast::ConstructorInit * );
    12181232        };
     
    13631377        const ast::CaseStmt * Resolver_new::previsit( const ast::CaseStmt * caseStmt ) {
    13641378                if ( caseStmt->cond ) {
    1365                         std::vector< ast::InitAlternative > initAlts = currentObject.getOptions();
     1379                        std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
    13661380                        assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
    13671381                                "expression." );
     
    13741388                        // whether it would perform a conversion.
    13751389                        if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
    1376                                 ast::ptr< ast::TypeSubstitution > env = castExpr->env;
    1377                                 newExpr.set_and_mutate( castExpr->arg )->env = env;
     1390                                swap_and_save_env( newExpr, castExpr->arg );
    13781391                        }
    13791392                       
     
    14381451        }
    14391452
    1440         void Resolver_new::previsit( const ast::SingleInit * singleInit ) {
    1441                 #warning unimplemented; Resolver port in progress
    1442                 (void)singleInit;
    1443                 assert(false);
    1444         }
    1445 
    1446         void Resolver_new::previsit( const ast::ListInit * listInit ) {
    1447                 #warning unimplemented; Resolver port in progress
    1448                 (void)listInit;
    1449                 assert(false);
     1453
     1454
     1455        const ast::SingleInit * Resolver_new::previsit( const ast::SingleInit * singleInit ) {
     1456                visit_children = false;
     1457                // resolve initialization using the possibilities as determined by the `currentObject`
     1458                // cursor.
     1459                ast::Expr * untyped = new ast::UntypedInitExpr{
     1460                        singleInit->location, singleInit->value, currentObject.getOptions() };
     1461                ast::ptr<ast::Expr> newExpr = findKindExpression( untyped, symtab );
     1462                const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
     1463
     1464                // move cursor to the object that is actually initialized
     1465                currentObject.setNext( initExpr->designation );
     1466
     1467                // discard InitExpr wrapper and retain relevant pieces.
     1468                // `initExpr` may have inferred params in the case where the expression specialized a
     1469                // function pointer, and newExpr may already have inferParams of its own, so a simple
     1470                // swap is not sufficient
     1471                ast::Expr::InferUnion inferred = initExpr->inferred;
     1472                swap_and_save_env( newExpr, initExpr->expr );
     1473                newExpr.get_and_mutate()->inferred.splice( std::move(inferred) );
     1474
     1475                // get the actual object's type (may not exactly match what comes back from the resolver
     1476                // due to conversions)
     1477                const ast::Type * initContext = currentObject.getCurrentType();
     1478
     1479                removeExtraneousCast( newExpr, symtab );
     1480
     1481                // check if actual object's type is char[]
     1482                if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) {
     1483                        if ( isCharType( at->base ) ) {
     1484                                // check if the resolved type is char*
     1485                                if ( auto pt = newExpr->result.as< ast::PointerType >() ) {
     1486                                        if ( isCharType( pt->base ) ) {
     1487                                                // strip cast if we're initializing a char[] with a char*
     1488                                                // e.g. char x[] = "hello"
     1489                                                if ( auto ce = newExpr.as< ast::CastExpr >() ) {
     1490                                                        swap_and_save_env( newExpr, ce->arg );
     1491                                                }
     1492                                        }
     1493                                }
     1494                        }
     1495                }
     1496
     1497                // move cursor to next object in preparation for next initializer
     1498                currentObject.increment();
     1499
     1500                // set initializer expression to resolved expression
     1501                return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) );
     1502        }
     1503
     1504        const ast::ListInit * Resolver_new::previsit( const ast::ListInit * listInit ) {
     1505                // move cursor into brace-enclosed initializer-list
     1506                currentObject.enterListInit( listInit->location );
     1507
     1508                assert( listInit->designations.size() == listInit->initializers.size() );
     1509                for ( unsigned i = 0; i < listInit->designations.size(); ++i ) {
     1510                        // iterate designations and initializers in pairs, moving the cursor to the current
     1511                        // designated object and resolving the initializer against that object
     1512                        #warning unimplemented; Resolver port in progress
     1513                        assert(false);
     1514                }
     1515
     1516                visit_children = false;
     1517                return listInit;
    14501518        }
    14511519
  • src/main.cc

    r9856ca9 re7f8119  
    1010// Created On       : Fri May 15 23:12:02 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jun  5 13:48:41 2019
    13 // Update Count     : 600
     12// Last Modified On : Wed Jun  5 20:35:13 2019
     13// Update Count     : 601
    1414//
    1515
     
    162162        backtrace( 6 );                                                                         // skip first 6 stack frames
    163163        signal( SIGABRT, SIG_DFL);                                                      // reset default signal handler
    164                 raise( SIGABRT );                                                                       // reraise SIGABRT
     164        raise( SIGABRT );                                                                       // reraise SIGABRT
    165165} // sigAbortHandler
    166166
  • tests/io2.cfa

    r9856ca9 re7f8119  
    1010// Created On       : Wed Mar  2 16:56:02 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Apr 18 08:03:30 2019
    13 // Update Count     : 113
     12// Last Modified On : Sun Jun  9 08:07:42 2019
     13// Update Count     : 117
    1414//
    1515
     
    4949        in       | f | d | ld;                                                                  // floating point
    5050        in       | fc | dc | ldc;                                                               // floating-point complex
    51         in       | cstr( s1 ) | cstr( s2, size );                               // C string, length unchecked and checked
     51        in       | cstr( s1 ) | wd( size, cstr( s2 ) );                 // C string, length unchecked and checked
    5252        sout | nl;
    5353
     
    133133// Local Variables: //
    134134// tab-width: 4 //
    135 // compile-command: "cfa -DIN_DIR=".in/" io2.cfa" //
     135// compile-command: "cfa -DIN_DIR=\".in/\" io2.cfa" //
    136136// End: //
Note: See TracChangeset for help on using the changeset viewer.