Changes in / [e7f8119:9856ca9]


Ignore:
Files:
14 deleted
24 edited

Legend:

Unmodified
Added
Removed
  • doc/LaTeXmacros/common.tex

    re7f8119 r9856ca9  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Fri May 24 07:59:54 2019
    14 %% Update Count     : 382
     13%% Last Modified On : Mon Jul  9 08:28:05 2018
     14%% Update Count     : 380
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    6161\newlength{\gcolumnposn}                                % temporary hack because lstlisting does not handle tabs correctly
    6262\newlength{\columnposn}
    63 \setlength{\gcolumnposn}{2.75in}
     63\setlength{\gcolumnposn}{2.5in}
    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

    re7f8119 r9856ca9  
    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,
    2626     volume      = 10,
    2627     number      = 3,
    2628     pages        = {120-123},
     2625    month       = apr, volume = 10, number = 3, pages = {120-123},
    26292626    comment     = {
    26302627        The ``two-pass'' algorithm.  An upward pass over a parse tree
     
    26602657}
    26612658
    2662 @inproceedings{chambers89a,
     2659@InProceedings{chambers89a,
    26632660    keywords    = {maps, delegation},
    26642661    author      = "Craig Chambers and David Ungar and Elgin Lee",
    2665     title       = "An Efficient Implementation of {SELF}, a Dynamically-Typed Object-Oriented Language Based on Prototypes",
     2662    title       = "An Efficient Implementation of {SELF}, a Dynamically-Typed
     2663                 Object-Oriented Language Based on Prototypes",
    26662664    crossref    = "OOPSLA89",
    26672665    pages       = {49-70}
    26682666}
    26692667
    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 
    26822668@article{oop:encapsulation,
    26832669    keywords    = {Encapsulation, Inheritance, Subclasses, Multiple Inheritance},
    26842670    contributer = {gjditchfield@plg},
    26852671    author      = {Alan Snyder},
    2686     title       = {Encapsulation and Inheritance in Object-Oriented Programming Languages},
     2672    title       = {Encapsulation and Inheritance in Object-Oriented Programming
     2673        Languages},
    26872674    journal     = sigplan,
    26882675    volume      = {21},    number = {11},
     
    27192706    title       = {Encapsulators: A New Software Paradigm in Smalltalk-80},
    27202707    journal     = sigplan,
    2721     volume      = {21},
    2722     number      = {11},
     2708    volume      = {21},    number       = {11},
    27232709    pages       = {341-346},
    2724     month       = nov,
    2725     year        = 1986,
     2710    month       = nov, year = 1986,
    27262711    comment     = {
    27272712        Encapsulators are objects that surround other objects.
  • doc/papers/concurrency/Makefile

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

    re7f8119 r9856ca9  
    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, generator, _Generic, _Imaginary, __imag, __imag__,
     155                __float80, float80, __float128, float128, forall, ftype, _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, suspend, thread,
     157                otype, restrict, __restrict, __restrict__, __signed, __signed__, _Static_assert, 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 
    238233\title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}}
    239234
     
    252247This paper discusses the design philosophy and implementation of its advanced control-flow and concurrent/parallel features, along with the supporting runtime.
    253248These 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.
    254 \CFA introduces modern language-level control-flow mechanisms, like generators, coroutines, user-level threading, and monitors for mutual exclusion and synchronization.
     249\CFA introduces modern language-level control-flow mechanisms, like coroutines, user-level threading, and monitors for mutual exclusion and synchronization.
    255250Library extension for executors, futures, and actors are built on these basic mechanisms.
    256 The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and optional monitor barging.
     251The runtime provides significant programmer simplification and safety by eliminating spurious wakeup and reducing monitor barging.
    257252The runtime also ensures multiple monitors can be safely acquired \emph{simultaneously} (deadlock free), and this feature is fully integrated with all monitor synchronization mechanisms.
    258 All control-flow features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers.
     253All language features integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers.
    259254Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming-languages.
    260255}%
    261256
    262 \keywords{generator, coroutine, concurrency, parallelism, thread, monitor, runtime, C, \CFA (Cforall)}
     257\keywords{coroutines, concurrency, parallelism, threads, monitors, runtime, C, \CFA (Cforall)}
    263258
    264259
     
    290285As 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.
    291286From 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}.
    292 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 medium work-units to facilitate load balancing by the runtime~\cite{Verch12}.
     287The 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}.
    293288As well, user-threading facilitates a simpler concurrency approach using thread objects that leverage sequential patterns versus events with call-backs~\cite{vonBehren03}.
    294289Finally, 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.
     
    305300
    306301Finally, it is important for a language to provide safety over performance \emph{as the default}, allowing careful reduction of safety for performance when necessary.
    307 Two 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.
    308 However, spurious wakeup is \emph{not} a foundational concurrency property~\cite[\S~8]{Buhr05a}, it is a performance design choice.
    309 Similarly, signals-as-hints is also a performance decision.
    310 We 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.)
    312 Clawing back performance, when local non-determinism is unimportant, should be an option not the default.
     302Two 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.
     303If you believe spurious wakeup is a foundational concurrency property, than unblocking (signalling) a thread is always a hint.
     304If you \emph{do not} believe spurious wakeup is foundational, than signalling-as-hints is a performance decision.
     305Most importantly, removing spurious wakeup and signals-as-hints makes concurrent programming significantly safer because it removes local non-determinism.
     306Clawing back performance where the local non-determinism is unimportant, should be an option not the default.
    313307
    314308\begin{comment}
     
    333327
    334328\CFA embraces user-level threading, language extensions for advanced control-flow, and safety as the default.
    335 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.
     329We 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.
    336330The main contributions of this work are:
    337331\begin{itemize}
    338332\item
    339 language-level generators, coroutines and user-level threading, which respect the expectations of C programmers.
     333expressive language-level coroutines and user-level threading, which respect the expectations of C programmers.
    340334\item
    341 monitor 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.
     335monitor synchronization without barging.
     336\item
     337safely acquiring multiple monitors \emph{simultaneously} (deadlock free), while seamlessly integrating this capability with all monitor synchronization mechanisms.
    342338\item
    343339providing statically type-safe interfaces that integrate with the \CFA polymorphic type-system and other language features.
     
    347343a runtime system with no spurious wakeup.
    348344\item
    349 a dynamic partitioning mechanism to segregate the execution environment for specialized requirements.
    350 \item
    351 a non-blocking I/O library
    352 \item
    353 experimental results showing comparable performance of the new features with similar mechanisms in other programming languages.
     345experimental results showing comparable performance of the new features with similar mechanisms in other concurrent programming-languages.
    354346\end{itemize}
    355347
    356 
    357 \section{Stateful Function}
    358 
    359 The generator/coroutine provides a stateful function, which is an old idea~\cite{Conway63,Marlin80} that is new again~\cite{C++20Coroutine19}.
    360 A stateful function allows execution to be temporarily suspended and later resumed, e.g., plugin, device driver, finite-state machine.
    361 Hence, 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.
    362 This capability is accomplished by retaining a data/execution \emph{closure} between invocations.
    363 If 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.
    364 If 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.
    365 Hence, refactoring a stackless coroutine may require changing it to stackful.
    366 A 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.
    367 As well, activating a stateful function is \emph{asymmetric} or \emph{symmetric}, identified by resume/suspend (no cycles) and resume/resume (cycles).
    368 A fixed closure activated by modified call/return is faster than a variable closure activated by context switching.
    369 Additionally, any storage management for the closure (especially in unmanaged languages, i.e., no garbage collection) must also be factored into design and performance.
    370 Therefore, selecting between stackless and stackful semantics is a tradeoff between programming requirements and performance, where stackless is faster and stackful is more general.
    371 Note, 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]
    378 typedef struct {
    379         int fn1, fn;
    380 } Fib;
    381 #define FibCtor { 1, 0 }
    382 int 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 }
    391 int 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;
     348\begin{comment}
     349This paper provides a minimal concurrency \newterm{Application Program Interface} (API) that is simple, efficient and can be used to build other concurrency features.
     350While the simplest concurrency system is a thread and a lock, this low-level approach is hard to master.
     351An easier approach for programmers is to support higher-level constructs as the basis of concurrency.
     352Indeed, for highly-productive concurrent-programming, high-level approaches are much more popular~\cite{Hochstein05}.
     353Examples 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
     355The following terminology is used.
     356A \newterm{thread} is a fundamental unit of execution that runs a sequence of code and requires a stack to maintain state.
     357Multiple 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.
     360Parallelism implies \emph{actual} simultaneous execution, where concurrency only requires \emph{apparent} simultaneous execution.
     361As such, parallelism only affects performance, which is observed through differences in space and/or time at runtime.
     362Hence, there are two problems to be solved: concurrency and parallelism.
     363While these two concepts are often combined, they are distinct, requiring different tools~\cite[\S~2]{Buhr05a}.
     364Concurrency tools handle mutual exclusion and synchronization, while parallelism tools handle performance, cost, and resource utilization.
     365
     366The proposed concurrency API is implemented in a dialect of C, called \CFA (pronounced C-for-all).
     367The 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
     374The following is a quick introduction to the \CFA language, specifically tailored to the features needed to support concurrency.
     375Extended 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.
     378Like C, the building blocks of \CFA are structures and routines.
     379Virtually all of the code generated by the \CFA translator respects C memory layouts and calling conventions.
     380While \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}.
     381While 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}
     388int x = 1, y = 2, z = 3;
     389int * p1 = &x, ** p2 = &p1,  *** p3 = &p2,      $\C{// pointers to x}$
     390    `&` r1 = x,   `&&` r2 = r1,   `&&&` r3 = r2;        $\C{// references to x}$
     391int * p4 = &z, `&` r4 = z;
     392
     393*p1 = 3; **p2 = 3; ***p3 = 3;       // change x
     394r1 =  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}
     398A reference is a handle to an object, like a pointer, but is automatically dereferenced the specified number of levels.
     399Referencing (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
     405Heterogeneous data is aggregated into a structure/union.
     406To 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}
     411struct S { char c; int i; double d; };
     412struct 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}
     417void 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}
     424void 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}
     432Object-oriented programming languages only provide implicit qualification for the receiver.
     433
     434In detail, the @with@-statement syntax is:
     435\begin{cfa}
     436$\emph{with-statement}$:
     437        'with' '(' $\emph{expression-list}$ ')' $\emph{compound-statement}$
     438\end{cfa}
     439and may appear as the body of a routine or nested within a routine body.
     440Each expression in the expression-list provides a type and object.
     441The type must be an aggregate type.
     442(Enumerations are already opened.)
     443The object is the implicit qualifier for the open structure-fields.
     444All 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.
     450Both 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}
     460const short int `MIN` = -32768;
     461const int `MIN` = -2147483648;
     462const long int `MIN` = -9223372036854775808L;
     463\end{cfa}
     464&
     465\begin{cfa}
     466short int si = `MIN`;
     467int i = `MIN`;
     468long 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}
     476void `f`( void );
     477void `f`( char );
     478void `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}
     492char `f`( int );
     493double `f`( int );
     494[char, double] `f`( int );
     495\end{cfa}
     496&
     497\begin{cfa}
     498char c = `f`( 3 );
     499double d = `f`( 3 );
     500[d, c] = `f`( 3 );
     501\end{cfa}
     502\end{tabular}
     503\lstMakeShortInline@%
     504\end{cquote}
     505Overloading is important for \CFA concurrency since the runtime system relies on creating different types to represent concurrency objects.
     506Therefore, overloading eliminates long prefixes and other naming conventions to prevent name clashes.
     507As seen in Section~\ref{s:Concurrency}, routine @main@ is heavily overloaded.
     508As another example, variable overloading is useful in the parallel semantics of the @with@ statement for fields with the same name:
     509\begin{cfa}
     510struct S { int `i`; int j; double m; } s;
     511struct T { int `i`; int k; int m; } t;
     512with ( 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}
     522For 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
     527Overloading also extends to operators.
     528Operator-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}
     533int ++?(int op);
     534int ?++(int op);
     535int `?+?`(int op1, int op2);
     536int ?<=?(int op1, int op2);
     537int ?=? (int & op1, int op2);
     538int ?+=?(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}
     551struct S { int i, j; };
     552S `?+?`( S op1, S op2) { // add two structures
     553        return (S){op1.i + op2.i, op1.j + op2.j};
     554}
     555S s1 = {1, 2}, s2 = {2, 3}, s3;
     556s3 = 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
     565Object 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}
     568struct VLA { int len, * data; };                        $\C{// variable length array of integers}$
     569void ?{}( VLA & vla ) with ( vla ) { len = 10;  data = alloc( len ); }  $\C{// default constructor}$
     570void ?{}( VLA & vla, int size, char fill ) with ( vla ) { len = size;  data = alloc( len, fill ); } // initialization
     571void ?{}( VLA & vla, VLA other ) { vla.len = other.len;  vla.data = other.data; } $\C{// copy, shallow}$
     572void ^?{}( 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}
     584Like \CC, construction is implicit on allocation (stack/heap) and destruction is implicit on deallocation.
     585The 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}$
     591struct S * s = new();                                           $\C{// allocation, call constructor}$
     592...
     593delete( 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
     601The 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.
     602For example, the following sum routine works for any type that supports construction from 0 and addition:
     603\begin{cfa}
     604forall( otype T | { void `?{}`( T *, zero_t ); T `?+?`( T, T ); } ) // constraint type, 0 and +
     605T 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}
     611S sa[5];
     612int i = sum( sa, 5 );                                           $\C{// use S's 0 construction and +}$
     613\end{cfa}
     614Type 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.
     617The 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}
     621trait `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 & );
    404627};
    405 
    406 void `main(Fib & fib)` with(fib) {
    407 
    408         [fn1, fn] = [1, 0];
    409         for () {
    410                 `suspend;`
    411                 [fn1, fn] = [fn, fn + fn1];
    412 
    413         }
    414 }
    415 int 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]
    426 typedef struct {
    427         int fn1, fn;  void * `next`;
    428 } Fib;
    429 #define FibCtor { 1, 0, NULL }
    430 Fib * 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 }
    439 int 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 };
    468 void ?{}( Fmt & fmt ) { `resume(fmt);` } // constructor
    469 void ^?{}( Fmt & f ) with(f) { $\C[1.75in]{// destructor}$
    470         if ( g != 0 || b != 0 ) sout | nl; }
    471 void `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 }
    482 int 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]
    496 typedef struct {
    497         void * next;
    498         char ch;
    499         int g, b;
    500 } Fmt;
    501 void 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 }
    514 int 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 
    539 Stackless generators have the potential to be very small and fast, \ie as small and fast as function call/return for both creation and execution.
    540 The \CFA goal is to achieve this performance target, possibly at the cost of some semantic complexity.
    541 How this goal is accomplished is done through a series of different kinds of generators and their implementation.
    542 
    543 Figure~\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.
    544 This kind of generator is an \emph{output generator}, producing a new result on each resumption.
    545 To 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.
    546 An additional requirement is the ability to create an arbitrary number of generators (of any kind), \ie retaining state in global variables is insufficient;
    547 hence, state is retained in a closure between calls.
    548 Figure~\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.
    549 The C version only has the middle execution state because the top execution state becomes declaration initialization.
    550 Figure~\ref{f:CFAFibonacciGen} shows the \CFA approach, which also has a manual closure, but replaces the structure with a special \CFA @generator@ type.
    551 This 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}.
    552 The generator main contains @suspend@ statements that suspend execution without ending the generator versus @return@.
    553 For the Fibonacci generator-main,\footnote{
    554 The \CFA \lstinline|with| opens an aggregate scope making its fields directly accessible, like Pascal \lstinline|with|, but using parallel semantics.
    555 Multiple aggregates may be opened.}
    556 the top initialization state appears at the start and the middle execution state is denoted by statement @suspend@.
    557 Any local variables in @main@ \emph{are not retained} between calls;
    558 hence local variable are only for temporary computations \emph{between} suspends.
    559 All retained state \emph{must} appear in the generator's type.
    560 As 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.
    561 The 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.
    562 Resuming an ended (returned) generator is undefined.
    563 Function @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.
    564 Figure~\ref{f:CFibonacciSim} shows the C implementation of the \CFA generator only needs one additional field, @next@, to handle retention of execution state.
    565 The computed @goto@ at the start of the generator main, which branches after the previous suspend, adds very little cost to the resume call.
    566 Finally, 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}
    568 int ?()( Fib & fib ) with( fib ) { return `resume( fib )`.fn; }   // function-call interface
    569 int ?()( Fib & fib, int N ) with( fib ) { for ( N - 1 ) `fib()`; return `fib()`; }   // use simple interface
    570 double ?()( Fib & fib ) with( fib ) { return (int)`fib()` / 3.14159; } // cast prevents recursive call
    571 sout | (int)f1() | (double)f1() | f2( 2 );   // simple interface, cast selects call based on return type, step 2 values
    572 \end{cfa}
    573 Now, the generator can be a separately-compiled opaque-type only accessed through its interface functions.
    574 For 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 
    576 Having 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}.)
    578 Our concern is that static analysis to discriminate local state from temporary variables is complex and beyond the current scope of the \CFA project.
    579 As 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.
    580 But 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.
    581 Our current experience is that most generator problems have simple data state, including local state, but complex execution state.
    582 As well, C programmers are not afraid with this kind of semantic programming requirement, if it results in very small, fast generators.
    583 
    584 Figure~\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@{}}
    590 abcdefghijklmnopqrstuvwxyz \\
    591 abcdefghijklmnopqrstuvwxyz
    592 \end{tabular}
    593 &
    594 \begin{tabular}[t]{@{}lllll@{}}
    595 abcd    & efgh  & ijkl  & mnop  & qrst  \\
    596 uvwx    & yzab  & cdef  & ghij  & klmn  \\
    597 opqr    & stuv  & wxyz  &               &
    598 \end{tabular}
    599 \end{tabular}
    600 \end{center}
    601 The 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.
    602 The destructor provides a newline, if formatted text ends with a full line.
    603 Figure~\ref{f:CFormatSim} shows the C implementation of the \CFA input generator with one additional field and the computed @goto@.
    604 For contrast, Figure~\ref{f:PythonFormatter} shows the equivalent Python format generator with the same properties as the Fibonacci generator.
    605 
    606 Figure~\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}.
    607 Device drives follow the pattern of simple data state but complex execution state, \ie finite state-machine (FSM) parsing a protocol.
    608 For 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}
    612 is a network message beginning with the control character STX, ending with an ETX, and followed by a 2-byte cyclic-redundancy check.
    613 Control characters may appear in a message if preceded by an ESC.
    614 When 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.
    615 The 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.
    616 Hence, the device driver is an input/output generator.
    617 
    618 Note, 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.
    619 As 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.
    620 Manually, detecting and hoisting local-state variables is easy when the number is small.
    621 Finally, the execution state is large, with one @resume@ and seven @suspend@s.
    622 Hence, 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.
    623 Because 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]
    630 def Fib():
    631     fn1, fn = 0, 1
    632     while True:
    633         `yield fn1`
    634         fn1, fn = fn, fn1 + fn
    635 f1 = Fib()
    636 f2 = Fib()
    637 for 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]
    651 def 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()
    662 fmt = Fmt()
    663 `next( fmt )`                    # prime, next prewritten
    664 for 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]
    680 enum 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 };
    688 void ?{}( Driver & d, char * m ) { d.msg = m; }
    689 Status next( Driver & d, char b ) with( d ) {
    690         byte = b; `resume( d );` return status;
    691 }
    692 void 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 
    731 Figure~\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.)
    733 This control flow is similar to recursion for functions, but without stack growth.
    734 The steps for symmetric control-flow are creating, executing, and terminating the cycle.
    735 Constructing 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.
    739 Once 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).
    740 Terminating 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).
    741 The starting stack-frame is below the last active generator because the resume/resume cycle does not grow the stack.
    742 Also, 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.
    743 Destructor cost occurs when the generator instance is deallocated, which is easily controlled by the programmer.
    744 
    745 Figure~\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.
    746 This 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.
    747 However, before the jump, the caller must reset its stack (and any registers) equivalent to a @return@, but subsequently jump forward not backwards.
    748 This semantics is basically a tail-call optimization, which compilers already perform.
    749 Hence, assembly code is manually insert in the example to undo the generator's entry code before the direct jump.
    750 This assembly code is fragile as it depends on what entry code is generated, specifically if there are local variables and the level of optimization.
    751 To provide this new calling convention requires a mechanism built into the compiler, which is beyond the scope of \CFA at this time.
    752 Nevertheless, it is possible to hand generate any symmetric generators for proof of concept and performance testing.
    753 A 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 };
    764 void ?{}(PingPong & pp, char * nm, int N) with(pp) {
    765         name = nm;  partner = 0p;  pp.N = N;  i = 0; }
    766 void `main( PingPong & pp )` with(pp) {
    767         for ( ; i < N; i += 1 ) {
    768                 sout | name | i;
    769                 `resume( partner );`
    770         }
    771 }
    772 int 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]
    783 typedef 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 }
    790 void 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 
    815 Finally, part of this generator work was inspired by the recent \CCtwenty generator proposal~\cite{C++20Coroutine19} (which they call coroutines).
    816 Our work provides the same high-performance asymmetric-generators as \CCtwenty, and extends their work with symmetric generators.
    817 An 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}
    822 Since the current generator's stack is released before calling the compound statement, the compound statement can only reference variables in the generator's type.
    823 This 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.
    824 Hence, this mechanism provides a general and safe handoff of the generator among competing threads.
    825 
    826 
    827 \subsection{Coroutine}
    828 \label{s:Coroutine}
    829 
    830 Stackful coroutines extend generator semantics, \ie there is an implicit closure and @suspend@ may appear in a helper function called from the coroutine main.
    831 A coroutine is specified by replacing @generator@ with @coroutine@ for the type.
    832 This 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.
    833 How coroutines differ from generators is done through a series of examples.
    834 
    835 First, 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]
    838 Move the declaration of @fn1@ to the start of coroutine main.
    839 \begin{cfa}[xleftmargin=0pt]
    840 void main( Fib & fib ) with(fib) {
    841         `int fn1;`
    842 \end{cfa}
    843 \item[Formatter]
    844 Move the declaration of @g@ and @b@ to the for loops in the coroutine main.
    845 \begin{cfa}[xleftmargin=0pt]
    846 for ( `g`; 5 ) {
    847         for ( `b`; 4 ) {
    848 \end{cfa}
    849 \item[Device Driver]
    850 Move 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]
    858 Move the declaration of @i@ to the for loop in the coroutine main.
    859 \begin{cfa}[xleftmargin=0pt]
    860 void main( PingPong & pp ) with(pp) {
    861         for ( `i`; N ) {
    862 \end{cfa}
    863 \end{description}
    864 It 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}
    866 unsigned 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}
    874 A call to this function is placed at the end of the driver's coroutine-main.
    875 For complex finite-state machines, refactoring is part of normal program abstraction, especially when code is used in multiple places.
    876 Again, this complexity is usually associated with execution state rather than data state.
    877 
    878 \begin{comment}
    879 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@.
    880 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.
    881 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@.
    882 The interface routine @next@, takes a Fibonacci instance and context switches to it using @resume@;
    883 on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned.
    884 The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack;
    885 when the coroutine main returns, its stack is deallocated.
    886 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.
    887 Figure~\ref{f:Coroutine1State} shows the coroutine version of the C version in Figure~\ref{f:ExternalState}.
    888 Coroutine generators are called \newterm{output coroutines} because values are only returned.
     628forall( otype T `| sumable( T )` )                      $\C{// use trait}$
     629T sum( T a[$\,$], size_t size );
     630\end{cfa}
     631
     632Using the return type for overload discrimination, it is possible to write a type-safe @alloc@ based on the C @malloc@:
     633\begin{cfa}
     634forall( dtype T | sized(T) ) T * alloc( void ) { return (T *)malloc( sizeof(T) ); }
     635int * ip = alloc();                                                     $\C{// select type and size from left-hand side}$
     636double * dp = alloc();
     637struct S {...} * sp = alloc();
     638\end{cfa}
     639where 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
     646Coroutines are generalized routines allowing execution to be temporarily suspended and later resumed.
     647Hence, 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.
     648This capability is accomplished via the coroutine's stack, where suspend/resume context switch among stacks.
     649Because 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.
     650Therefore, the two fundamental features of the core \CFA coroutine-API are independent call-stacks and @suspend@/@resume@ operations.
     651
     652For 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}
     6560                                       & n = 0         \\
     6571                                       & n = 1         \\
     658\mathsf{fib}(n-1) + \mathsf{fib}(n-2)   & n \ge 2       \\
     659\end{array}
     660\right.
     661\end{displaymath}
     662where Figure~\ref{f:C-fibonacci} shows conventional approaches for writing a Fibonacci generator in C.
     663Figure~\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.
     664Figure~\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)$.
    889665
    890666\begin{figure}
     
    913689\begin{lrbox}{\myboxA}
    914690\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    915 #define FibCtor { 0, 1 }
     691#define FIB_INIT { 0, 1 }
    916692typedef struct { int fn1, fn; } Fib;
    917693int fib( Fib * f ) {
     
    926702
    927703int main() {
    928         Fib f1 = FibCtor, f2 = FibCtor;
     704        Fib f1 = FIB_INIT, f2 = FIB_INIT;
    929705        for ( int i = 0; i < 10; i += 1 ) {
    930706                printf( "%d %d\n",
     
    943719        [fn1, fn] = [0, 1];
    944720        for () {
    945                 `suspend;`
     721                `suspend();`
    946722                [fn1, fn] = [fn, fn1 + fn];
    947723        }
    948724}
    949725int ?()( Fib & fib ) with( fib ) {
    950         return `resume( fib )`.fn1;
     726        `resume( fib );`  return fn1;
    951727}
    952728int main() {
     
    996772\caption{Fibonacci Generator}
    997773\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}
    998818\end{figure}
    999819
    1000 \bigskip
    1001 
     820Using a coroutine, it is possible to express the Fibonacci formula directly without any of the C problems.
     821Figure~\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@.
     822Like 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.
     823The 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@.
     824The interface routine @next@, takes a Fibonacci instance and context switches to it using @resume@;
     825on restart, the Fibonacci field, @fn@, contains the next value in the sequence, which is returned.
     826The first @resume@ is special because it allocates the coroutine stack and cocalls its coroutine main on that stack;
     827when the coroutine main returns, its stack is deallocated.
     828Hence, @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.
     829Figure~\ref{f:Coroutine1State} shows the coroutine version of the C version in Figure~\ref{f:ExternalState}.
     830Coroutine generators are called \newterm{output coroutines} because values are only returned.
     831
     832Figure~\ref{f:CFAFmt} shows an \newterm{input coroutine}, @Format@, for restructuring text into groups of characters of fixed-size blocks.
     833For 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}}} \\
     838abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
     839&
     840\begin{tabular}[t]{@{}lllll@{}}
     841abcd    & efgh  & ijkl  & mnop  & qrst  \\
     842uvwx    & yzab  & cdef  & ghij  & klmn  \\
     843opqr    & stuv  & wxyz  &               &
     844\end{tabular}
     845\end{tabular}
     846\end{quote}
     847The 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.
     848The destructor provides a newline, if formatted text ends with a full line.
     849Figure~\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
    1002854\newbox\myboxA
    1003855\begin{lrbox}{\myboxA}
    1004856\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    1005 `coroutine` Fib { int fn; };
    1006 void main( Fib & fib ) with( fib ) {
    1007         fn = 0;  int fn1 = fn; `suspend`;
    1008         fn = 1;  int fn2 = fn1;  fn1 = fn; `suspend`;
     857`coroutine` Fmt {
     858        char ch;   // communication variables
     859        int g, b;   // needed in destructor
     860};
     861void main( Fmt & fmt ) with( fmt ) {
    1009862        for () {
    1010                 fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `suspend`; }
    1011 }
    1012 int next( Fib & fib ) with( fib ) { `resume( fib );` return fn; }
     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}
     870void ?{}( Fmt & fmt ) { `resume( fmt );` } // prime
     871void ^?{}( Fmt & fmt ) with( fmt ) { // destructor
     872        if ( g != 0 || b != 0 ) // special case
     873                sout | nl; }
     874void send( Fmt & fmt, char c ) { fmt.ch = c; `resume( fmt )`; }
    1013875int main() {
    1014         Fib f1, f2;
    1015         for ( 10 )
    1016                 sout | next( f1 ) | next( f2 );
     876        Fmt fmt;
     877        sout | nlOff;   // turn off auto newline
     878        for ( 41 )
     879                send( fmt, 'a' );
    1017880}
    1018881\end{cfa}
    1019882\end{lrbox}
     883
    1020884\newbox\myboxB
    1021885\begin{lrbox}{\myboxB}
    1022886\begin{python}[aboveskip=0pt,belowskip=0pt]
    1023887
    1024 def 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 
    1031 f1 = Fibonacci()
    1032 f2 = Fibonacci()
    1033 for i in range( 10 ):
    1034         print( `next( f1 )`, `next( f2 )` ) # resume
     888
     889
     890def 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
     906fmt = Fmt()
     907`next( fmt )`                    # prime
     908for i in range( 41 ):
     909        `fmt.send( 'a' );`      # send to yield
    1035910
    1036911\end{python}
    1037912\end{lrbox}
    1038 \subfloat[\CFA]{\label{f:Coroutine3States}\usebox\myboxA}
     913\subfloat[\CFA]{\label{f:CFAFmt}\usebox\myboxA}
    1039914\qquad
    1040 \subfloat[Python]{\label{f:Coroutine1State}\usebox\myboxB}
    1041 \caption{Fibonacci input coroutine, 3 states, internal variables}
    1042 \label{f:cfa-fibonacci}
     915\subfloat[Python]{\label{f:CFmt}\usebox\myboxB}
     916\caption{Output formatting text}
     917\label{f:fmt-line}
    1043918\end{figure}
    1044 \end{comment}
     919
     920The 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.
     921However, @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.)
     924This control flow is similar to recursion for normal routines, but again there is no stack growth from the context switch.
    1045925
    1046926\begin{figure}
     
    1050930\begin{cfa}
    1051931`coroutine` Prod {
    1052         Cons & c;                       // communication
     932        Cons & c;
    1053933        int N, money, receipt;
    1054934};
     
    1071951}
    1072952void start( Prod & prod, int N, Cons &c ) {
    1073         &prod.c = &c;
     953        &prod.c = &c; // reassignable reference
    1074954        prod.[N, receipt] = [N, 0];
    1075955        `resume( prod );`
     
    1084964\begin{cfa}
    1085965`coroutine` Cons {
    1086         Prod & p;                       // communication
     966        Prod & p;
    1087967        int p1, p2, status;
    1088968        bool done;
     
    1092972        cons.[status, done ] = [0, false];
    1093973}
     974void ^?{}( Cons & cons ) {}
    1094975void main( Cons & cons ) with( cons ) {
    1095976        // 1st resume starts here
     
    1113994        `resume( cons );`
    1114995}
    1115 
    1116996\end{cfa}
    1117997\end{tabular}
    1118998\caption{Producer / consumer: resume-resume cycle, bi-directional communication}
    1119999\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}
    11381000\end{figure}
    11391001
    1140 Figure~\ref{f:ProdCons} shows the ping-pong example in Figure~\ref{f:CFAPingPongGen} extended into a producer/consumer symmetric-coroutine performing bidirectional communication.
    1141 This example is illustrative because both producer/consumer have two interface functions with @resume@s that suspend execution in these interface (helper) functions.
    1142 The 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.
    1143 The 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.
     1002Figure~\ref{f:ProdCons} shows a producer/consumer symmetric-coroutine performing bi-directional communication.
     1003Since 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@.
     1004The @start@ routine communicates both the number of elements to be produced and the consumer into the producer's coroutine-structure.
     1005Then 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.
    11451007
    11461008The producer call to @delivery@ transfers values into the consumer's communication variables, resumes the consumer, and returns the consumer status.
    1147 On the first resume, @cons@'s stack is created and initialized, holding local-state variables retained between subsequent activations of the coroutine.
     1009For the first resume, @cons@'s stack is initialized, creating local variables retained between subsequent activations of the coroutine.
    11481010The 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).
    11491011The call from the consumer to @payment@ introduces the cycle between producer and consumer.
    11501012When @payment@ is called, the consumer copies values into the producer's communication variable and a resume is executed.
    11511013The context switch restarts the producer at the point where it last context switched, so it continues in @delivery@ after the resume.
     1014
    11521015@delivery@ returns the status value in @prod@'s coroutine main, where the status is printed.
    11531016The loop then repeats calling @delivery@, where each call resumes the consumer coroutine.
     
    11551018The consumer increments and returns the receipt to the call in @cons@'s coroutine main.
    11561019The loop then repeats calling @payment@, where each call resumes the producer coroutine.
    1157 Figure~\ref{f:ProdConsRuntimeStacks} shows the runtime stacks of the program main, and the coroutine mains for @prod@ and @cons@ during the cycling.
    1158 
    1159 Terminating 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.
    1160 Furthermore, 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.
    1161 When a coroutine's main ends, its stack is already unwound so any stack allocated objects with destructors have been finalized.
    1162 The na\"{i}ve semantics for coroutine-cycle termination is context switch to the last resumer, like executing a @suspend@/@return@ in a generator.
    1163 However, for coroutines, the last resumer is \emph{not} implicitly below the current stack frame, as for generators, because each coroutine's stack is independent.
    1164 Unfortunately, it is impossible to determine statically if a coroutine is in a cycle and unrealistic to check dynamically (graph-cycle problem).
    1165 Hence, a compromise solution is necessary that works for asymmetric (acyclic) and symmetric (cyclic) coroutines.
    1166 
    1167 Our solution for coroutine termination works well for the most common asymmetric and symmetric coroutine usage-patterns.
    1168 For asymmetric coroutines, it is common for the first resumer (starter) coroutine to be the only resumer.
    1169 All previous generators converted to coroutines have this property.
    1170 For symmetric coroutines, it is common for the cycle creator to persist for the life-time of the cycle.
    1171 Hence, the starter coroutine is remembered on the first resume and ending the coroutine resumes the starter.
    1172 Figure~\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.
    1173 For other scenarios, it is always possible to devise a solution with additional programming effort.
    1174 
    1175 The producer/consumer example does not illustrate the full power of the starter semantics because @cons@ always ends first.
    1176 Assume generator @PingPong@ is converted to a coroutine.
    1177 Figure~\ref{f:PingPongFullCoroutineSteps} shows the creation, starter, and cyclic execution steps of the coroutine version.
    1178 The program main creates (declares) coroutine instances @ping@ and @pong@.
    1179 Next, program main resumes @ping@, making it @ping@'s starter, and @ping@'s main resumes @pong@'s main, making it @pong@'s starter.
    1180 Execution forms a cycle when @pong@ resumes @ping@, and cycles $N$ times.
    1181 By adjusting $N$ for either @ping@/@pong@, it is possible to have either one finish first, instead of @pong@ always ending first.
    1182 If @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@.
    1183 If @ping@ ends first, it resumes its starter the program main in function @start@.
    1184 Regardless of the cycle complexity, the starter stack always leads back to the program main, but the stack can be entered at an arbitrary point.
    1185 Once back at the program main, coroutines @ping@ and @pong@ are deallocated.
    1186 For generators, deallocation runs the destructors for all objects in the generator type.
    1187 For 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.
    1188 Hence, 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.
    1189 So 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.
    1190 Explicitly 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 
    1192 Finally, there is an interesting effect for @suspend@ with symmetric coroutines.
    1193 A coroutine must retain its last resumer to suspend back because the resumer is on a different stack.
    1194 These reverse pointers allow @suspend@ to cycle \emph{backwards}, which may be useful in certain cases.
    1195 However, 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.
    1196 To prevent losing this information, a self-resume does not overwrite the last resumer.
     1020
     1021After iterating $N$ times, the producer calls @stop@.
     1022The @done@ flag is set to stop the consumer's execution and a resume is executed.
     1023The context switch restarts @cons@ in @payment@ and it returns with the last receipt.
     1024The 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.
     1026The program main restarts after the resume in @start@.
     1027@start@ returns and the program main terminates.
     1028
     1029One \emph{killer} application for a coroutine is device drivers, which at one time caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}.
     1030Many 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}
     1035where a network message begins with the control character STX and ends with an ETX, followed by a 2-byte cyclic-redundancy check.
     1036Control characters may appear in a message if preceded by an ESC.
     1037Because 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}
     1041enum Status { CONT, MSG, ESTX, ELNTH, ECRC };
     1042`coroutine` Driver {
     1043        Status status;
     1044        char * msg, byte;
     1045};
     1046void ?{}( Driver & d, char * m ) { d.msg = m; }         $\C[3.0in]{// constructor}$
     1047Status next( Driver & d, char b ) with( d ) {           $\C{// 'with' opens scope}$
     1048        byte = b; `resume( d );` return status;
     1049}
     1050void 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}
    11971083
    11981084
     
    12001086
    12011087A 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.
    1202 There are several solutions to this problem and the chosen option directed the \CFA coroutine design.
    1203 
    1204 For object-oriented languages, inheritance can be used to provide extra fields and code, but it requires explicitly writing the inheritance:
     1088There are several solutions to this problem and the chosen option forced the \CFA coroutine design.
     1089
     1090Object-oriented inheritance provides extra fields and code in a restricted context, but it requires programmers to explicitly perform the inheritance:
    12051091\begin{cfa}[morekeywords={class,inherits}]
    12061092class mycoroutine inherits baseCoroutine { ... }
    12071093\end{cfa}
    1208 In addition, the programming language and possibly its tool set, \eg debugger, @valgrind@ need to understand @baseCoroutine@ because of special stack property of coroutines.
     1094and the programming language (and possibly its tool set, \eg debugger) may need to understand @baseCoroutine@ because of the stack.
    12091095Furthermore, the execution of constructors/destructors is in the wrong order for certain operations.
    12101096For 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.
     
    12171103}
    12181104\end{cfa}
    1219 which also requires an explicit declaration that must be last to ensure correct initialization order.
    1220 However, there is nothing preventing wrong placement or multiple declarations of @baseCoroutine@.
    1221 
    1222 For coroutines, as for threads, many implementations are based on function pointers or function objects~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}.
     1105which also requires an explicit declaration that must be the last one to ensure correct initialization order.
     1106However, there is nothing preventing wrong placement or multiple declarations.
     1107
     1108For coroutines as for threads, many implementations are based on routine pointers or routine objects~\cite{Butenhof97, C++14, MS:VisualC++, BoostCoroutines15}.
    12231109For example, Boost implements coroutines in terms of four functor object-types:
    12241110\begin{cfa}
     
    12281114symmetric_coroutine<>::yield_type
    12291115\end{cfa}
    1230 
    1231 Figure~\ref{f:CoroutineMemoryLayout} shows different memory-layout options for a coroutine (where a task is similar).
    1232 The coroutine handle is the @coroutine@ instance containing programmer specified global/communication variables.
    1233 The coroutine descriptor contains all implicit declarations needed by the runtime, \eg @suspend@/@resume@, and can be part of the coroutine handle or separate.\footnote{
    1234 We are examining variable-sized structures (VLS), where fields can be variable-sized structures or arrays.
    1235 Once allocated, a VLS is fixed sized.}
    1236 The coroutine stack can appear in a number of locations and forms, fixed or variable sized.
    1237 Hence, the coroutine's stack could be a VLS on the allocating stack, provided the allocating stack is large enough.
    1238 For stack allocation, allocation/deallocation only requires a few arithmetic operation to compute the size and adjust the stack point, modulo any constructor costs.
    1239 For heap allocation, allocation/deallocation is an expensive heap operation (where the heap can be a shared resource), modulo any constructor costs.
    1240 For 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.
    1241 Currently, \CFA supports stack/heap allocated descriptors but only heap allocated stacks;
    1242 split-stack allocation is under development.
    1243 In \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 
    1252 Similarly, the canonical threading paradigm is often based on function pointers, \eg @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}.
    1253 However, the generic thread-handle (identifier) is limited (few operations), unless it is wrapped in a custom type, as in the pthreads approach.
     1116Similarly, the canonical threading paradigm is often based on routine pointers, \eg @pthreads@~\cite{Butenhof97}, \Csharp~\cite{Csharp}, Go~\cite{Go}, and Scala~\cite{Scala}.
     1117However, the generic thread-handle (identifier) is limited (few operations), unless it is wrapped in a custom type.
    12541118\begin{cfa}
    12551119void mycor( coroutine_t cid, void * arg ) {
     
    12631127}
    12641128\end{cfa}
    1265 Since the custom type is simple to write in \CFA and solves several issues, added support for function/lambda-based coroutines adds very little.
     1129Since the custom type is simple to write in \CFA and solves several issues, added support for routine/lambda-based coroutines adds very little.
    12661130
    12671131Note, 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

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

    re7f8119 r9856ca9  
    1313}
    1414
    15 #define FibCtor { 0, 1 }
    16 typedef struct { int fn, fn1; } Fib;
     15#define FIB_INIT { 0, 1 }
     16typedef struct { int fn1, fn2; } Fib;
    1717int fib_state( Fib & f ) with( f ) {
    18         int fn0 = fn1 + fn2;  fn2 = fn1;  fn = fn0;
    19         return fn1;
     18        int ret = fn2;
     19        int fn = fn1 + fn2;
     20        fn2 = fn1; fn1 = fn;
     21        return ret;
    2022}
    2123
     
    2830        }
    2931}
    30 int ?()( Fib1 & fib ) with( fib ) { return resume( fib ).fn; }
     32int next( Fib1 & fib ) with( fib ) { resume( fib ); return fn; }
    3133
    32 coroutine Fib2 { int fn; };                                             // used for communication
     34coroutine Fib2 { int ret; };                                    // used for communication
    3335void main( Fib2 & fib ) with( fib ) {                   // called on first resume
    34         int fn1 = 1, fn2 = 0;                                           // precompute first two states
     36        int fn, fn1 = 1, fn2 = 0;                                       // precompute first two states
    3537        for () {
     38                ret = fn2;
    3639                fn = fn1 + fn2;  fn2 = fn1;  fn1 = fn;  // general case
    3740                suspend();                                                              // restart last resume
    3841        }
    3942}
    40 int ?()( Fib2 & fib ) with( fib ) {
    41         return resume( fib ).fn;                                        // restart last suspend
    42 }
    43 int ?()( Fib2 & fib, int N ) with( fib ) {
    44         for ( N - 1 ) fib();
    45         return fib();
    46 }
    47 double ?()( Fib2 & fib ) with( fib ) {
    48         return (int)(fib()) / 3.14159;                                          // restart last suspend
     43int next( Fib2 & fib ) with( fib ) {
     44        resume( fib );                                                          // restart last suspend
     45        return ret;
    4946}
    5047
     
    5451        sout | nl;
    5552
    56         Fib f1 = FibCtor, f2 = FibCtor;
     53        Fib f1 = FIB_INIT, f2 = FIB_INIT;
    5754        for ( 10 )
    5855                sout | fib_state( f1 ) | fib_state( f2 );
     
    6158        Fib1 f1, f2;
    6259        for ( 10 )
    63                 sout | f1() | f2();
     60                sout | next( f1 ) | next( f2 );
    6461        sout | nl;
    6562
    66         Fib2 f12, f22;
     63        Fib2 f1, f2;
    6764        for ( 10 )
    68                 sout | (int)f12() | (double)f12() | f22( 2 );
     65                sout | next( (Fib2 &)f1 ) | next( (Fib2 &)f2 );
    6966}
    7067
  • doc/papers/concurrency/examples/Fib2.cfa

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

    re7f8119 r9856ca9  
    1010# Created On       : Sat May 16 07:57:37 2015
    1111# Last Modified By : Peter A. Buhr
    12 # Last Modified On : Thu Jun  6 20:46:28 2019
    13 # Update Count     : 34
     12# Last Modified On : Tue Jul  5 14:32:52 2016
     13# Update Count     : 32
    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" );
    157154} # END
    158155
  • libcfa/src/iostream.cfa

    re7f8119 r9856ca9  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jun  9 16:27:17 2019
    13 // Update Count     : 803
     12// Last Modified On : Tue May 21 13:01:26 2019
     13// Update Count     : 674
    1414//
    1515
     
    2020#include <stdbool.h>                                                                    // true/false
    2121//#include <string.h>                                                                   // strlen, strcmp
     22extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
    2223extern size_t strlen (const char *__s) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1)));
    23 extern int strcmp (const char *__s1, const char *__s2) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__pure__)) __attribute__ ((__nonnull__ (1, 2)));
    24 extern char *strcpy (char *__restrict __dest, const char *__restrict __src) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
    25 extern void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2)));
    2624#include <float.h>                                                                              // DBL_DIG, LDBL_DIG
    2725#include <math.h>                                                                               // isfinite
    2826#include <complex.h>                                                                    // creal, cimag
    29 } // extern "C"
    30 
    31 
    32 //*********************************** Ostream ***********************************
    33 
     27}
    3428
    3529forall( dtype ostype | ostream( ostype ) ) {
     
    204198                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    205199//              os | crealf( fc ) | nonl;
    206                 PrintWithDP( os, "%g", crealf( fc ) );
    207                 PrintWithDP( os, "%+g", cimagf( fc ) );
     200                float f = crealf( fc );
     201                PrintWithDP( os, "%g", f );
     202                f = cimagf( fc );
     203                PrintWithDP( os, "%+g", f );
    208204                fmt( os, "i" );
    209205                return os;
     
    216212                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    217213//              os | creal( dc ) | nonl;
    218                 PrintWithDP( os, "%.*lg", creal( dc ), DBL_DIG );
    219                 PrintWithDP( os, "%+.*lg", cimag( dc ), DBL_DIG );
     214                double d = creal( dc );
     215                PrintWithDP( os, "%.*lg", d, DBL_DIG );
     216                d = cimag( dc );
     217                PrintWithDP( os, "%+.*lg", d, DBL_DIG );
    220218                fmt( os, "i" );
    221219                return os;
     
    228226                if ( sepPrt( os ) ) fmt( os, "%s", sepGetCur( os ) );
    229227//              os | creall( ldc ) || nonl;
    230                 PrintWithDP( os, "%.*Lg", creall( ldc ), LDBL_DIG );
    231                 PrintWithDP( os, "%+.*Lg", cimagl( ldc ), LDBL_DIG );
     228                long double ld = creall( ldc );
     229                PrintWithDP( os, "%.*Lg", ld, LDBL_DIG );
     230                ld = cimagl( ldc );
     231                PrintWithDP( os, "%+.*Lg", ld, LDBL_DIG );
    232232                fmt( os, "i" );
    233233                return os;
     
    395395} // distribution
    396396
     397//---------------------------------------
     398
    397399// writes the range [begin, end) to the given stream
    398400forall( dtype ostype, otype elt_type | writeable( elt_type, ostype ), otype iterator_type | iterator( iterator_type, elt_type ) ) {
     
    408410} // distribution
    409411
    410 //*********************************** Manipulators ***********************************
    411 
    412 //*********************************** Integral ***********************************
    413 
    414 static const char * shortbin[] = { "0", "1", "10", "11", "100", "101", "110", "111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
    415 static 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 ) \
    419 forall( 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 
    483 IntegralFMTImpl( signed char, 'd', "%    *hh ", "%    *.*hh " )
    484 IntegralFMTImpl( unsigned char, 'u', "%    *hh ", "%    *.*hh " )
    485 IntegralFMTImpl( signed short int, 'd', "%    *h ", "%    *.*h " )
    486 IntegralFMTImpl( unsigned short int, 'u', "%    *h ", "%    *.*h " )
    487 IntegralFMTImpl( signed int, 'd', "%    * ", "%    *.* " )
    488 IntegralFMTImpl( unsigned int, 'u', "%    * ", "%    *.* " )
    489 IntegralFMTImpl( signed long int, 'd', "%    *l ", "%    *.*l " )
    490 IntegralFMTImpl( unsigned long int, 'u', "%    *l ", "%    *.*l " )
    491 IntegralFMTImpl( signed long long int, 'd', "%    *ll ", "%    *.*ll " )
    492 IntegralFMTImpl( 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 ) \
    518 forall( 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 
    546 FloatingPointFMTImpl( double, "%    * ", "%    *.* " )
    547 FloatingPointFMTImpl( long double, "%    *L ", "%    *.*L " )
    548 
    549 //*********************************** Character ***********************************
    550 
    551 forall( 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 
    583 forall( 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 
     412//---------------------------------------
    629413
    630414forall( dtype istype | istream( istype ) ) {
     
    653437
    654438        istype & ?|?( istype & is, signed char & sc ) {
    655                 fmt( is, "%hhi", &sc );
     439                fmt( is, "%hhd", &sc );
    656440                return is;
    657441        } // ?|?
    658442
    659443        istype & ?|?( istype & is, unsigned char & usc ) {
    660                 fmt( is, "%hhi", &usc );
     444                fmt( is, "%hhu", &usc );
    661445                return is;
    662446        } // ?|?
    663447
    664448        istype & ?|?( istype & is, short int & si ) {
    665                 fmt( is, "%hi", &si );
     449                fmt( is, "%hd", &si );
    666450                return is;
    667451        } // ?|?
    668452
    669453        istype & ?|?( istype & is, unsigned short int & usi ) {
    670                 fmt( is, "%hi", &usi );
     454                fmt( is, "%hu", &usi );
    671455                return is;
    672456        } // ?|?
    673457
    674458        istype & ?|?( istype & is, int & i ) {
    675                 fmt( is, "%i", &i );
     459                fmt( is, "%d", &i );
    676460                return is;
    677461        } // ?|?
    678462
    679463        istype & ?|?( istype & is, unsigned int & ui ) {
    680                 fmt( is, "%i", &ui );
     464                fmt( is, "%u", &ui );
    681465                return is;
    682466        } // ?|?
    683467
    684468        istype & ?|?( istype & is, long int & li ) {
    685                 fmt( is, "%li", &li );
     469                fmt( is, "%ld", &li );
    686470                return is;
    687471        } // ?|?
    688472
    689473        istype & ?|?( istype & is, unsigned long int & ulli ) {
    690                 fmt( is, "%li", &ulli );
     474                fmt( is, "%lu", &ulli );
    691475                return is;
    692476        } // ?|?
    693477
    694478        istype & ?|?( istype & is, long long int & lli ) {
    695                 fmt( is, "%lli", &lli );
     479                fmt( is, "%lld", &lli );
    696480                return is;
    697481        } // ?|?
    698482
    699483        istype & ?|?( istype & is, unsigned long long int & ulli ) {
    700                 fmt( is, "%lli", &ulli );
     484                fmt( is, "%llu", &ulli );
    701485                return is;
    702486        } // ?|?
     
    721505        istype & ?|?( istype & is, float _Complex & fc ) {
    722506                float re, im;
    723                 fmt( is, "%f%fi", &re, &im );
     507                fmt( is, "%g%gi", &re, &im );
    724508                fc = re + im * _Complex_I;
    725509                return is;
     
    761545} // distribution
    762546
    763 //*********************************** Manipulators ***********************************
    764 
     547_Istream_cstrUC cstr( char * str ) { return (_Istream_cstrUC){ str }; }
    765548forall( dtype istype | istream( istype ) )
    766 istype & ?|?( 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 );
     549istype & ?|?( istype & is, _Istream_cstrUC cstr ) {
     550        fmt( is, "%s", cstr.s );
    796551        return is;
    797 } // ?|?
    798 
    799 #define InputFMTImpl( T, CODE ) \
    800 forall( dtype istype | istream( istype ) ) \
    801 istype & ?|?( 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 
    814 InputFMTImpl( char, "c" )
    815 InputFMTImpl( signed char, "hhi" )
    816 InputFMTImpl( unsigned char, "hhi" )
    817 InputFMTImpl( signed short int, "hi" )
    818 InputFMTImpl( unsigned short int, "hi" )
    819 InputFMTImpl( signed int, "i" )
    820 InputFMTImpl( unsigned int, "i" )
    821 InputFMTImpl( signed long int, "li" )
    822 InputFMTImpl( unsigned long int, "li" )
    823 InputFMTImpl( signed long long int, "lli" )
    824 InputFMTImpl( unsigned long long int, "lli" )
    825 
    826 InputFMTImpl( float, "f" )
    827 InputFMTImpl( double, "lf" )
    828 InputFMTImpl( long double, "Lf" )
    829 
     552} // cstr
     553
     554_Istream_cstrC cstr( char * str, int size ) { return (_Istream_cstrC){ str, size }; }
    830555forall( dtype istype | istream( istype ) )
    831 istype & ?|?( 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
     556istype & ?|?( istype & is, _Istream_cstrC cstr ) {
     557        char buf[16];
     558        sprintf( buf, "%%%ds", cstr.size );
     559        fmt( is, buf, cstr.s );
    838560        return is;
    839 } // ?|?
    840 
    841 forall( dtype istype | istream( istype ) )
    842 istype & ?|?( 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 
    852 forall( dtype istype | istream( istype ) )
    853 istype & ?|?( 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 } // ?|?
     561} // cstr
    862562
    863563// Local Variables: //
  • libcfa/src/iostream.hfa

    re7f8119 r9856ca9  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // iostream.hfa --
     7// iostream --
    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 Jun  8 17:28:44 2019
    13 // Update Count     : 312
     12// Last Modified On : Sat May 11 10:31:27 2019
     13// Update Count     : 232
    1414//
    1515
     
    1717
    1818#include "iterator.hfa"
    19 
    20 
    21 //*********************************** Ostream ***********************************
    22 
    2319
    2420trait ostream( dtype ostype ) {
     
    150146} // distribution
    151147
    152 //*********************************** Manipulators ***********************************
    153 
    154 forall( otype T )
    155 struct _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 ) \
    178 static 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 } \
    193 forall( dtype ostype | ostream( ostype ) ) { \
    194         ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
    195         void ?|?( ostype & os, _Ostream_Manip(T) f ); \
    196 } // ?|?
    197 
    198 IntegralFMTDecl( signed char, 'd' )
    199 IntegralFMTDecl( unsigned char, 'u' )
    200 IntegralFMTDecl( signed short int, 'd' )
    201 IntegralFMTDecl( unsigned short int, 'u' )
    202 IntegralFMTDecl( signed int, 'd' )
    203 IntegralFMTDecl( unsigned int, 'u' )
    204 IntegralFMTDecl( signed long int, 'd' )
    205 IntegralFMTDecl( unsigned long int, 'u' )
    206 IntegralFMTDecl( signed long long int, 'd' )
    207 IntegralFMTDecl( unsigned long long int, 'u' )
    208 
    209 //*********************************** Floating Point ***********************************
    210 
    211 // Default suffix for values with no fraction is "."
    212 #define FloatingPointFMTDecl( T ) \
    213 static 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 } \
    230 forall( dtype ostype | ostream( ostype ) ) { \
    231         ostype & ?|?( ostype & os, _Ostream_Manip(T) f ); \
    232         void ?|?( ostype & os, _Ostream_Manip(T) f ); \
    233 } // ?|?
    234 
    235 FloatingPointFMTDecl( double )
    236 FloatingPointFMTDecl( long double )
    237 
    238 //*********************************** Character ***********************************
    239 
    240 static 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
    250 forall( 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 
    257 static 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
    268 forall( 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 
     148//---------------------------------------
    276149
    277150trait istream( dtype istype ) {
     
    316189        istype & ?|?( istype &, long double _Complex & );
    317190
    318         // Cannot have char & and char * => cstr manipulator
    319         // istype & ?|?( istype &, char * );
    320 
    321191        // manipulators
    322192        istype & ?|?( istype &, istype & (*)( istype & ) );
     
    326196} // distribution
    327197
    328 //*********************************** Manipulators ***********************************
    329 
    330 struct _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 
    343 static inline _Istream_Cstr skip( const char * scanset ) { return (_Istream_Cstr){ 0p, scanset, -1, { .all : 0 } }; }
    344 static inline _Istream_Cstr incl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : false } }; }
    345 static inline _Istream_Cstr incl( const char * scanset, _Istream_Cstr & fmt ) { fmt.flags.inex = false; return fmt; }
    346 static inline _Istream_Cstr excl( const char * scanset, char * s ) { return (_Istream_Cstr){ s, scanset, -1, { .flags.inex : true } }; }
    347 static inline _Istream_Cstr excl( const char * scanset, _Istream_Cstr & fmt ) { fmt.flags.inex = true; return fmt; }
    348 static inline _Istream_Cstr cstr( char * s ) { return (_Istream_Cstr){ s, 0p, -1, { .all : 0 } }; }
    349 static inline _Istream_Cstr ignore( const char * s ) { return (_Istream_Cstr)@{ s, 0p, -1, { .flags.ignore : true } }; }
    350 static inline _Istream_Cstr ignore( _Istream_Cstr & fmt ) { fmt.flags.ignore = true; return fmt; }
    351 static inline _Istream_Cstr wd( unsigned int w, char * s ) { return (_Istream_Cstr)@{ s, 0p, w, { .all : 0 } }; }
    352 static inline _Istream_Cstr wd( unsigned int w, _Istream_Cstr & fmt ) { fmt.wd = w; return fmt; }
    353 forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_Cstr );
    354 
    355 forall( otype T )
    356 struct _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 ) \
    363 static inline _Istream_Manip(T) ignore( const T & val ) { return (_Istream_Manip(T))@{ (T &)val, -1, true }; } \
    364 static inline _Istream_Manip(T) ignore( _Istream_Manip(T) & fmt ) { fmt.ignore = true; return fmt; } \
    365 static inline _Istream_Manip(T) wd( unsigned int w, T & val ) { return (_Istream_Manip(T))@{ val, w, false }; } \
    366 forall( dtype istype | istream( istype ) ) { \
    367         istype & ?|?( istype & is, _Istream_Manip(T) f ); \
    368 } // ?|?
    369 
    370 InputFMTDecl( char )
    371 InputFMTDecl( signed char )
    372 InputFMTDecl( unsigned char )
    373 InputFMTDecl( signed short int )
    374 InputFMTDecl( unsigned short int )
    375 InputFMTDecl( signed int )
    376 InputFMTDecl( unsigned int )
    377 InputFMTDecl( signed long int )
    378 InputFMTDecl( unsigned long int )
    379 InputFMTDecl( signed long long int )
    380 InputFMTDecl( unsigned long long int )
    381 
    382 InputFMTDecl( float )
    383 InputFMTDecl( double )
    384 InputFMTDecl( long double )
    385 
    386 InputFMTDecl( float _Complex )
    387 InputFMTDecl( double _Complex )
    388 InputFMTDecl( long double _Complex )
    389 
    390 
    391 //*********************************** Time ***********************************
     198struct _Istream_cstrUC { char * s; };
     199_Istream_cstrUC cstr( char * );
     200forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_cstrUC );
     201
     202struct _Istream_cstrC { char * s; int size; };
     203_Istream_cstrC cstr( char *, int size );
     204forall( dtype istype | istream( istype ) ) istype & ?|?( istype &, _Istream_cstrC );
    392205
    393206
  • src/AST/Convert.cpp

    re7f8119 r9856ca9  
    1616#include "Convert.hpp"
    1717
    18 #include <deque>
    1918#include <unordered_map>
    2019
     
    576575
    577576                if ( srcInferred.mode == ast::Expr::InferUnion::Params ) {
    578                         const ast::InferredParams &srcParams = srcInferred.inferParams();
     577                        const ast::InferredParams &srcParams = srcInferred.inferParamsConst();
    579578                        for (auto srcParam : srcParams) {
    580579                                tgtInferParams[srcParam.first] = ParamEntry(
     
    586585                        }
    587586                } else if ( srcInferred.mode == ast::Expr::InferUnion::Slots  ) {
    588                         const ast::ResnSlots &srcSlots = srcInferred.resnSlots();
     587                        const ast::ResnSlots &srcSlots = srcInferred.resnSlotsConst();
    589588                        for (auto srcSlot : srcSlots) {
    590589                                tgtResnSlots.push_back(srcSlot);
     
    14221421#       define GET_ACCEPT_V(child, type) \
    14231422                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 )
    14381423
    14391424        ast::Label make_label(Label* old) {
     
    24772462
    24782463        virtual void visit( UntypedInitExpr * old ) override final {
    2479                 std::deque<ast::InitAlternative> initAlts;
     2464                std::vector<ast::InitAlternative> initAlts;
    24802465                for (auto ia : old->initAlts) {
    24812466                        initAlts.push_back(ast::InitAlternative(
     
    27422727                this->node = new ast::Designation(
    27432728                        old->location,
    2744                         GET_ACCEPT_D(designators, Expr)
     2729                        GET_ACCEPT_V(designators, Expr)
    27452730                );
    27462731        }
  • src/AST/Expr.cpp

    re7f8119 r9856ca9  
    163163        result = mem->get_type();
    164164        // substitute aggregate generic parameters into member type
    165         genericSubstitution( aggregate->result ).apply( result );
     165        genericSubsitution( 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

    re7f8119 r9856ca9  
    1717
    1818#include <cassert>
    19 #include <deque>
    2019#include <map>
    2120#include <string>
     
    112111                }
    113112
    114                 const ResnSlots& resnSlots() const {
     113                const ResnSlots& resnSlotsConst() const {
    115114                        if (mode == Slots) {
    116115                                return data.resnSlots;
     
    129128                }
    130129
    131                 const InferredParams& inferParams() const {
     130                const InferredParams& inferParamsConst() const {
    132131                        if (mode == Params) {
    133132                                return data.inferParams;
     
    135134                        assert(!"Mode was not already Params");
    136135                        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");
    154136                }
    155137        };
     
    713695public:
    714696        ptr<Expr> expr;
    715         std::deque<InitAlternative> initAlts;
    716 
    717         UntypedInitExpr( const CodeLocation & loc, const Expr * e, std::deque<InitAlternative> && as )
     697        std::vector<InitAlternative> initAlts;
     698
     699        UntypedInitExpr( const CodeLocation & loc, const Expr * e, std::vector<InitAlternative> && as )
    718700        : Expr( loc ), expr( e ), initAlts( std::move(as) ) {}
    719701
  • src/AST/GenericSubstitution.cpp

    re7f8119 r9856ca9  
    3131                TypeSubstitution sub;
    3232
    33                 void previsit( const Type * ) {
    34                         // allow empty substitution for non-generic type
    35                         visit_children = false;
     33                void previsit( const Type * ty ) {
     34                        assertf( false, "Attempted generic substitution for non-aggregate type: %s",
     35                                toString( ty ).c_str() );
    3636                }
    3737
     
    4040                }
    4141
    42         private:
    43                 // make substitution for generic type
    44                 void makeSub( const ReferenceToType * ty ) {
     42                void previsit( const ReferenceToType * ty ) {
    4543                        visit_children = false;
     44                        // build substitution from base parameters
    4645                        const AggregateDecl * aggr = ty->aggr();
    4746                        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 );
    5747                }
    5848        };
    5949}
    6050
    61 TypeSubstitution genericSubstitution( const Type * ty ) {
     51TypeSubstitution genericSubsitution( const Type * ty ) {
    6252        Pass<GenericSubstitutionBuilder> builder;
    6353        maybe_accept( ty, builder );
  • src/AST/GenericSubstitution.hpp

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

    re7f8119 r9856ca9  
    1616#pragma once
    1717
    18 #include <deque>
    1918#include <utility>        // for move
    2019#include <vector>
     
    3635class Designation final : public ParseNode {
    3736public:
    38         std::deque<ptr<Expr>> designators;
     37        std::vector<ptr<Expr>> designators;
    3938
    40         Designation( const CodeLocation& loc, std::deque<ptr<Expr>>&& ds = {} )
     39        Designation( const CodeLocation& loc, std::vector<ptr<Expr>>&& ds = {} )
    4140        : ParseNode( loc ), designators( std::move(ds) ) {}
    4241
  • src/AST/Node.hpp

    re7f8119 r9856ca9  
    154154
    155155        template< enum Node::ref_type o_ref_t >
    156         ptr_base( const ptr_base<node_t, o_ref_t> & o ) : node(o.get()) {
     156        ptr_base( const ptr_base<node_t, o_ref_t> & o ) : node(o.node) {
    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.get()) {
     161        ptr_base( ptr_base<node_t, o_ref_t> && o ) : node(o.node) {
    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.get());
     186                assign(o.node);
    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.get());
     192                assign(o.node);
    193193                return *this;
    194194        }
     
    228228        void _check() const;
    229229
     230protected:
    230231        const node_t * node;
    231232};
  • src/AST/porting.md

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

    re7f8119 r9856ca9  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue May 28 17:06:37 2019
    13 // Update Count     : 4354
     12// Last Modified On : Wed May 15 21:25:27 2019
     13// Update Count     : 4296
    1414//
    1515
     
    278278%token OTYPE FTYPE DTYPE TTYPE TRAIT                                    // CFA
    279279%token SIZEOF OFFSETOF
    280 // %token SUSPEND RESUME                                                                        // CFA
    281280%token ATTRIBUTE EXTENSION                                                              // GCC
    282281%token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR BREAK CONTINUE GOTO RETURN
     
    483482%precedence '}'
    484483%precedence '('
    485 
    486 // %precedence RESUME
    487 // %precedence '{'
    488 // %precedence ')'
    489484
    490485%locations                                                                                              // support location tracking for error messages
     
    604599                        $$ = new ExpressionNode( $5 );
    605600                }
    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; }
    610601        ;
    611602
     
    12721263        | RETURN comma_expression_opt ';'
    12731264                { $$ = new StatementNode( build_return( $2 ) ); }
    1274         | RETURN '{' initializer_list_opt comma_opt '}' ';'
     1265        | RETURN '{' initializer_list_opt comma_opt '}'
    12751266                { 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; }
    12801267        | THROW assignment_expression_opt ';'                           // handles rethrow
    12811268                { $$ = new StatementNode( build_throw( $2 ) ); }
     
    21712158
    21722159bit_subrange_size:
    2173         ':' assignment_expression
     2160        ':' constant_expression
    21742161                { $$ = $2; }
    21752162        ;
  • src/ResolvExpr/CurrentObject.cc

    re7f8119 r9856ca9  
    1616#include <stddef.h>                    // for size_t
    1717#include <cassert>                     // for assertf, assert, safe_dynamic_...
    18 #include <deque>
    1918#include <iostream>                    // for ostream, operator<<, basic_ost...
    2019#include <stack>                       // for stack
     
    2221
    2322#include "AST/Expr.hpp"                // for InitAlternative
    24 #include "AST/GenericSubstitution.hpp" // for genericSubstitution
    2523#include "AST/Init.hpp"                // for Designation
    2624#include "AST/Node.hpp"                // for readonly
    27 #include "AST/Type.hpp"
    2825#include "Common/Indenter.h"           // for Indenter, operator<<
    2926#include "Common/SemanticError.h"      // for SemanticError
     
    586583
    587584namespace ast {
    588         /// create a new MemberIterator that traverses a type correctly
    589         MemberIterator * createMemberIterator( const CodeLocation & loc, const Type * type );
     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        };
    590600
    591601        /// Iterates "other" types (e.g. basic, pointer) which do not change at list initializer entry
     
    596606                SimpleIterator( const CodeLocation & loc, const Type * t ) : location( loc ), type( t ) {}
    597607
    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 {
     608                std::vector< InitAlternative > operator* () const override { return first(); }
     609
     610        protected:
     611                std::vector< InitAlternative > first() const override {
    622612                        if ( type ) return { InitAlternative{ type, new Designation{ location } } };
    623613                        return {};
     
    625615        };
    626616
    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 
    944617        CurrentObject::CurrentObject( const CodeLocation & loc, const Type * type ) : objStack() {
    945618                objStack.emplace_back( new SimpleIterator{ loc, type } );
    946619        }
    947620
    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() {
     621        std::vector< InitAlternative > CurrentObject::getOptions() {
    980622                PRINT( std::cerr << "____getting current options" << std::endl; )
    981623                assertf( ! objStack.empty(), "objstack empty in getOptions" );
    982624                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();
    989625        }
    990626}
  • src/ResolvExpr/CurrentObject.h

    re7f8119 r9856ca9  
    1616#pragma once
    1717
    18 #include <deque>
    1918#include <list>   // for list
    2019#include <memory> // for unique_ptr
     
    2221#include <vector>
    2322
    24 #include "AST/Node.hpp"  // for ptr
    2523#include "Common/CodeLocation.h"
    2624
     
    6159        // AST class types
    6260        class Designation;
    63         struct InitAlternative;
     61        class InitAlternative;
    6462        class Type;
    6563
    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         };
     64        // forward declaration of internal detail
     65        class MemberIterator;
    10466
    10567        /// Builds initializer lists in resolution
     
    11173                CurrentObject( const CodeLocation & loc, const Type * type );
    11274
    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();
    12175                /// produces a list of alternatives (Type *, Designation *) for the current sub-object's
    12276                /// initializer.
    123                 std::deque< InitAlternative > getOptions();
    124                 /// produces the type of the current object but no subobjects
    125                 const Type * getCurrentType();
     77                std::vector< InitAlternative > getOptions();
    12678        };
    12779} // namespace ast
  • src/ResolvExpr/Resolver.cc

    re7f8119 r9856ca9  
    781781        }
    782782
    783         bool isCharType( Type * t ) {
     783        template< typename T >
     784        bool isCharType( T t ) {
    784785                if ( BasicType * bt = dynamic_cast< BasicType * >( t ) ) {
    785786                        return bt->get_kind() == BasicType::Char || bt->get_kind() == BasicType::SignedChar ||
     
    10701071                };
    10711072
    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 
    10781073                /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
    10791074                void removeExtraneousCast( ast::ptr<ast::Expr> & expr, const ast::SymbolTable & symtab ) {
     
    10811076                                if ( typesCompatible( castExpr->arg->result, castExpr->result, symtab ) ) {
    10821077                                        // cast is to the same type as its argument, remove it
    1083                                         swap_and_save_env( expr, castExpr->arg );
     1078                                        ast::ptr< ast::TypeSubstitution > env = castExpr->env;
     1079                                        expr.set_and_mutate( castExpr->arg )->env = env;
    10841080                                }
    10851081                        }
     
    11791175                        return findKindExpression( untyped, symtab, hasIntegralType, "condition" );
    11801176                }
    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                 }
    11911177        }
    11921178
     
    12271213                void previsit( const ast::WaitForStmt * );
    12281214
    1229                 const ast::SingleInit * previsit( const ast::SingleInit * );
    1230                 const ast::ListInit * previsit( const ast::ListInit * );
     1215                void previsit( const ast::SingleInit * );
     1216                void previsit( const ast::ListInit * );
    12311217                void previsit( const ast::ConstructorInit * );
    12321218        };
     
    13771363        const ast::CaseStmt * Resolver_new::previsit( const ast::CaseStmt * caseStmt ) {
    13781364                if ( caseStmt->cond ) {
    1379                         std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
     1365                        std::vector< ast::InitAlternative > initAlts = currentObject.getOptions();
    13801366                        assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
    13811367                                "expression." );
     
    13881374                        // whether it would perform a conversion.
    13891375                        if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
    1390                                 swap_and_save_env( newExpr, castExpr->arg );
     1376                                ast::ptr< ast::TypeSubstitution > env = castExpr->env;
     1377                                newExpr.set_and_mutate( castExpr->arg )->env = env;
    13911378                        }
    13921379                       
     
    14511438        }
    14521439
    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;
     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);
    15181450        }
    15191451
  • src/main.cc

    re7f8119 r9856ca9  
    1010// Created On       : Fri May 15 23:12:02 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Jun  5 20:35:13 2019
    13 // Update Count     : 601
     12// Last Modified On : Wed Jun  5 13:48:41 2019
     13// Update Count     : 600
    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

    re7f8119 r9856ca9  
    1010// Created On       : Wed Mar  2 16:56:02 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jun  9 08:07:42 2019
    13 // Update Count     : 117
     12// Last Modified On : Thu Apr 18 08:03:30 2019
     13// Update Count     : 113
    1414//
    1515
     
    4949        in       | f | d | ld;                                                                  // floating point
    5050        in       | fc | dc | ldc;                                                               // floating-point complex
    51         in       | cstr( s1 ) | wd( size, cstr( s2 ) );                 // C string, length unchecked and checked
     51        in       | cstr( s1 ) | cstr( s2, size );                               // 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.