Changeset a4da45e


Ignore:
Timestamp:
Feb 26, 2024, 3:53:42 AM (20 months ago)
Author:
JiadaL <j82liang@…>
Branches:
master
Children:
3f9a8d0
Parents:
0522ebe (diff), 022bce0 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Resolve conflict

Files:
3 added
2 deleted
51 edited

Legend:

Unmodified
Added
Removed
  • doc/LaTeXmacros/common.sty

    r0522ebe ra4da45e  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Jan 21 13:17:48 2024
    14 %% Update Count     : 633
     13%% Last Modified On : Sun Feb 25 17:37:46 2024
     14%% Update Count     : 640
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    4040\newcommand{\CFA}{\protect\CFAIcon\xspace}                              % CFA symbolic name
    4141\newcommand{\CFL}{\textrm{Cforall}\xspace}                              % Cforall non-icon name
    42 \newcommand{\Celeven}{\textrm{C11}\xspace}                              % C11 symbolic name
     42\newcommand{\Celeven}{\textrm{C1\!1}\xspace}                    % C11 symbolic name
     43
    4344\newcommand{\CCIcon}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}} % C++ icon
    44 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace}               % C++ symbolic name
    45 \newcommand{\Cpp}[1][]{\CC{#1}}                                                 % C++ synonym
    4645% numbers disallowed in latex variables names => use number names
    47 \newcommand{\CCeleven}{\protect\CCIcon{11}\xspace}              % C++11 symbolic name
     46\newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace}    % C++11 symbolic name
    4847\newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace}    % C++14 symbolic name
    4948\newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace}   % C++17 symbolic name
    5049\newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace}              % C++20 symbolic name
     50\newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace}               % C++ symbolic name
     51\newcommand{\Cpp}[1][]{\CC{#1}}                                                 % C++ synonym
     52
    5153\newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name
    5254
     
    293295xleftmargin=\parindentlnth,                             % indent code to paragraph indentation
    294296extendedchars=true,                                             % allow ASCII characters in the range 128-255
    295 escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    296297mathescape=false,                                               % disable LaTeX math escape in CFA code $...$
    297298keepspaces=true,                                                %
     
    304305literate=
    305306%  {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1
    306   {-}{\raisebox{-1pt}{\texttt{-}}}1
     307  {-}{\raisebox{-1pt}{\ttfamily-}}1
    307308  {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
    308309  {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1
     310  {'}{\ttfamily'\!}1
    309311  {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1
    310312  {<-}{$\leftarrow$}2
     
    318320\lstset{
    319321language=CFA,
    320 %moredelim=**[is][\color{red}]{@}{@},   % red highlighting @...@
     322escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    321323moredelim=**[is][\color{red}]{®}{®},    % red highlighting ®...® (registered trademark symbol) emacs: C-q M-.
    322324%moredelim=**[is][\color{blue}]{ß}{ß},  % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
     
    328330% inline code ©...© (copyright symbol) emacs: C-q M-)
    329331\lstMakeShortInline©                                    % single-character for \lstinline
     332
    330333\else% regular ASCI characters
     334
    331335\lstnewenvironment{cfa}[1][]{% necessary
    332336\lstset{
    333337language=CFA,
    334338escapechar=\$,                                                  % LaTeX escape in CFA code
    335 mathescape=false,                                               % LaTeX math escape in CFA code $...$
    336339moredelim=**[is][\color{red}]{@}{@},    % red highlighting @...@
    337340}% lstset
  • doc/LaTeXmacros/common.tex

    r0522ebe ra4da45e  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Wed Jan 24 08:43:57 2024
    14 %% Update Count     : 593
     13%% Last Modified On : Sun Feb 25 18:11:56 2024
     14%% Update Count     : 614
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    4040\newcommand{\CFA}{\protect\CFAIcon\xspace}                              % CFA symbolic name
    4141\newcommand{\CFL}{\textrm{Cforall}\xspace}                              % Cforall non-icon name
    42 \newcommand{\Celeven}{\textrm{C11}\xspace}                              % C11 symbolic name
     42\newcommand{\Celeven}{\textrm{C1\!1}\xspace}                    % C11 symbolic name
     43
    4344\newcommand{\CCIcon}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}} % C++ icon
    44 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace}               % C++ symbolic name
    45 \newcommand{\Cpp}[1][]{\CC{#1}}                                                 % C++ synonym
    4645% numbers disallowed in latex variables names => use number names
    47 \newcommand{\CCeleven}{\protect\CCIcon{11}\xspace}              % C++11 symbolic name
     46\newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace}    % C++11 symbolic name
    4847\newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace}    % C++14 symbolic name
    4948\newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace}   % C++17 symbolic name
    5049\newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace}              % C++20 symbolic name
     50\newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace}               % C++ symbolic name
     51\newcommand{\Cpp}[1][]{\CC{#1}}                                                 % C++ synonym
     52
    5153\newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name
    5254
     
    297299xleftmargin=\parindentlnth,                             % indent code to paragraph indentation
    298300extendedchars=true,                                             % allow ASCII characters in the range 128-255
    299 escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    300301mathescape=false,                                               % disable LaTeX math escape in CFA code $...$
    301302keepspaces=true,                                                %
     
    308309literate=
    309310%  {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1
    310   {-}{\raisebox{-1pt}{\texttt{-}}}1
     311  {-}{\raisebox{-1pt}{\ttfamily-}}1
    311312  {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
    312313  {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1
     314  {'}{\ttfamily'\!}1
    313315  {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1
    314316  {<-}{$\leftarrow$}2
     
    322324\lstset{
    323325language=CFA,
    324 %moredelim=**[is][\color{red}]{@}{@},   % red highlighting @...@
     326escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    325327moredelim=**[is][\color{red}]{®}{®},    % red highlighting ®...® (registered trademark symbol) emacs: C-q M-.
    326328%moredelim=**[is][\color{blue}]{ß}{ß},  % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
     
    332334% inline code ©...© (copyright symbol) emacs: C-q M-)
    333335\lstMakeShortInline©                                    % single-character for \lstinline
     336
    334337\else% regular ASCI characters
     338
    335339\lstnewenvironment{cfa}[1][]{% necessary
    336340\lstset{
    337341language=CFA,
    338342escapechar=\$,                                                  % LaTeX escape in CFA code
    339 mathescape=false,                                               % LaTeX math escape in CFA code $...$
    340343moredelim=**[is][\color{red}]{@}{@},    % red highlighting @...@
    341344}% lstset
  • doc/LaTeXmacros/lstlang.sty

    r0522ebe ra4da45e  
    88%% Created On       : Sat May 13 16:34:42 2017
    99%% Last Modified By : Peter A. Buhr
    10 %% Last Modified On : Thu Sep 21 08:40:05 2023
    11 %% Update Count     : 31
     10%% Last Modified On : Fri Feb 16 07:59:29 2024
     11%% Update Count     : 37
    1212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1313
     
    125125}
    126126
    127 % C++ programming language
    128 \lstdefinelanguage{C++}[ANSI]{C++}{
    129         morekeywords={nullptr,}
    130 }
    131 
    132127% uC++ programming language, based on ANSI C++
    133 \lstdefinelanguage{uC++}[ANSI]{C++}{
     128\lstdefinelanguage{uC++}[GNU]{C++}{
    134129        morekeywords={
    135130                _Accept, _AcceptReturn, _AcceptWait, _Actor, _At, _Catch, _CatchResume, _CorActor, _Cormonitor, _Coroutine,
  • doc/bibliography/pl.bib

    r0522ebe ra4da45e  
    37423742    month       = jul, year = 1987,
    37433743    volume      = 4, number = 4, pages = {9-16}
     3744}
     3745
     3746@manual{FreePascal,
     3747    keywords    = {Pascal, object oriented},
     3748    contributer = {pabuhr@plg},
     3749    author      = {Micha\"{e}l Van Canneyt},
     3750    title       = {{F}ree {P}ascal Reference Guide, version 3.2.2},
     3751    month       = may,
     3752    year        = 2021,
     3753    note        = {\url{http://downloads.freepascal.org/fpc/docs-pdf/ref.pdf}},
    37443754}
    37453755
     
    82438253}
    82448254
     8255@manual{Swift,
     8256    keywords    = {Swift programming language},
     8257    contributer = {pabuhr@plg},
     8258    key         = {Swift},
     8259    author      = {Chris Lattner and Doug Gregor and John McCall and Ted Kremenek and Joe Groff and Apple Inc.},
     8260    title       = {The Swift Programming Language},
     8261    edition     = {5.9.2},
     8262    organization= {Apple Inc.},
     8263    address     = {Cupertino, CA, USA},
     8264    year        = 2024,
     8265    note        = {\url{https://docs.swift.org/swift-book/documentation/the-swift-programming-language}},
     8266}
     8267
    82458268@inproceedings{Burns81,
    82468269    keywords    = {N-thread software-solution mutual exclusion},
  • doc/proposals/enum.tex

    r0522ebe ra4da45e  
    117117\end{abstract}
    118118
    119 \section{Background}
     119\section{Introduction}
    120120
    121121Naming values is a common practice in mathematics and engineering, e.g., $\pi$, $\tau$ (2$\pi$), $\phi$ (golden ratio), MHz (1E6), etc.
    122 Naming is also commonly used to represent many other numerical phenomenon, such as days of the week, months of a year, floors of a building (basement), time (noon, New Years).
     122Naming is also commonly used to represent many other numerical phenomenon, such as days of the week, months of a year, floors of a building (basement), specific times (noon, New Years).
    123123Many programming languages capture this important software-engineering capability through a mechanism called an \newterm{enumeration}.
    124124An enumeration is similar to other programming-language types by providing a set of constrained values, but adds the ability to name \emph{all} the values in its set.
     
    126126
    127127Specifically, an enumerated type restricts its values to a fixed set of named constants.
    128 Fundamentally, all types are restricted to a fixed set of values because of the underlying von Neumann architecture, and hence, to a corresponding set of constants, e.g., @3@, @3.5@, @3.5+2.1i@, @'c'@, @"abc"@, etc.
    129 However, the values for basic types are not named, other than the programming-language supplied constants.
    130 
    131 
    132 \section{C-Style Enum}
    133 
    134 The C-Style enumeration has the following syntax and semantics, and is representative of enumerations in many other programming languages (see Section~\ref{s:RelatedWork}).
    135 \begin{lstlisting}[label=lst:weekday]
    136 enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
    137                 $\(\uparrow\)$                                                                        $\(\uparrow\)$
    138     ${\rm \newterm{enumeration name}}$                                          ${\rm \newterm{enumerator names}}
    139 \end{lstlisting}
    140 Here, the enumeration type @Weekday@ defines the ordered \newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
    141 By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constants.
    142 Because an enumerator is a constant, it cannot appear in a mutable context, e.g. @Mon = Sun@ is meaningless, and has no address, it is an rvalue\footnote{
    143 The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}.
    144 Enumerators without explicitly designated constants are auto-initialized by the compiler: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
    145 For example, @Monday@ to @Wednesday@ are implicitly assigned with constants 0--2, @Thursday@ is explicitly set to constant @10@, and @Friday@ to @Sunday@ are implicitly assigned with constants 11--13.
    146 Hence, there are 3 universal enumeration attributes: \newterm{position}, \newterm{label}, and \newterm{value}:
     128While all types are restricted to a fixed set of values because of the underlying von Neumann architecture, and hence, to a corresponding set of constants, e.g., @3@, @3.5@, @3.5+2.1i@, @'c'@, @"abc"@, etc., these values are not named, other than the programming-language supplied constant names.
     129
     130Fundamentally, all enumeration systems have an \newterm{enumeration} type with an associated set of \newterm{enumerator} names.
     131An enumeration has three universal attributes, \newterm{position}, \newterm{label}, and \newterm{value}, as shown by this representative enumeration, where position and value can be different.
    147132\begin{cquote}
    148133\small\sf\setlength{\tabcolsep}{3pt}
    149134\begin{tabular}{rccccccccccc}
    150 @enum@ Weekday \{       & Monday,       & Tuesday,      & Wednesday,    & Thursday = 10,& Friday,       & Saturday,     & Sunday \}; \\
    151 \it\color{red}position          & 0                     & 1                     & 2                             & 3                             & 4                     & 5                     & 6                     \\
    152 \it\color{red}label                     & Monday        & Tuesday       & Wednesday             & Thursday              & Friday        & Saturday      & Sunday        \\
    153 \it\color{red}value                     & 0                     & 1                     & 2                             & {\color{red}10}& 11           & 12            & 13
     135\it\color{red}enumeration & \multicolumn{7}{c}{\it\color{red}enumerators}       \\
     136$\downarrow$\hspace*{25pt} & \multicolumn{7}{c}{$\downarrow$}                           \\
     137@enum@ Weekday \{                               & Monday,       & Tuesday,      & Wednesday,    & Thursday,& Friday,    & Saturday,     & Sunday \}; \\
     138\it\color{red}position                  & 0                     & 1                     & 2                             & 3                             & 4                     & 5                     & 6                     \\
     139\it\color{red}label                             & Monday        & Tuesday       & Wednesday             & Thursday              & Friday        & Saturday      & Sunday        \\
     140\it\color{red}value                             & 0                     & 1                     & 2                             & 3                             & 4                     & 5             & 6
    154141\end{tabular}
    155142\end{cquote}
    156 Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible in the enclosing scope of the @enum@ type.
     143Here, the \newterm{enumeration} @Weekday@ defines the ordered \newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
     144By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constant values.
     145Because an enumerator is a constant, it cannot appear in a mutable context, e.g. @Mon = Sun@ is meaningless, and an enumerator has no address, it is an \newterm{rvalue}\footnote{
     146The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}.
     147
     148
     149\section{C-Style Enum}
     150
     151The C-Style enumeration has the following syntax and semantics.
     152\begin{lstlisting}
     153enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
     154\end{lstlisting}
     155Enumerators without an explicitly designated constant value are \newterm{auto-initialized} by the compiler: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
     156For example, @Monday@ to @Wednesday@ are implicitly assigned with constants @0@--@2@, @Thursday@ is explicitly set to constant @10@, and @Friday@ to @Sunday@ are implicitly assigned with constants @11@--@13@.
     157Initialization may occur in any order.
     158\begin{lstlisting}
     159enum Weekday { Thursday@ = 10@, Friday, Saturday, Sunday, Monday@ = 0@, Tuesday, Wednesday };
     160\end{lstlisting}
     161Note, the comma in the enumerator list can be a terminator or a separator, allowing the list to end with a dangling comma.
     162\begin{lstlisting}
     163enum Weekday {
     164        Thursday = 10, Friday, Saturday, Sunday,
     165        Monday = 0, Tuesday, Wednesday@,@ // terminating comma
     166};
     167\end{lstlisting}
     168This feature allow enumerator lines to be interchanged without moving a comma.\footnote{
     169A terminating comma appears in other C syntax, e.g., the initializer list.}
     170Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible (projected) into the enclosing scope of the @enum@ type.
    157171
    158172In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values.
    159 In practice, since integral constants in C have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables.
    160 Furthermore, there is an implicit bidirectional conversion between an enumeration and integral types.
     173In practice, since integral constants are used, which have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables.
     174Finally, there is an implicit bidirectional conversion between an enumeration and integral types.
    161175\begin{lstlisting}[label=lst:enum_scope]
    162176{
    163         enum Weekday { ... };                           $\C{// enumerators implicitly projected into local scope}$
     177        enum Weekday { /* as above */ };        $\C{// enumerators implicitly projected into local scope}$
    164178        Weekday weekday = Monday;                       $\C{// weekday == 0}$
    165179        weekday = Friday;                                       $\C{// weekday == 11}$
    166         int i = Sunday                                          $\C{// implicit conversion to int, i == 13}$
     180        int i = Sunday;                                         $\C{// implicit conversion to int, i == 13}$
    167181        weekday = 10000;                                        $\C{// UNDEFINED! implicit conversion to Weekday}$
    168182}
     
    171185The implicit conversion from @int@ to an enumeration type is an unnecessary source of error.
    172186
     187It is common for C programmers to ``believe'' there are 3 equivalent forms of constant enumeration.
     188\begin{lstlisting}[label=lst:enum_scope]
     189#define Monday 0
     190static const int Monday = 0;
     191enum { Monday };
     192\end{lstlisting}
     193For @#define@, the programmer has to play compiler and explicitly manage the enumeration values;
     194furthermore, these are independent constants outside of any language type mechanism.
     195The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{
     196C allows variable-length array-declarations (VLA), so this case does work, but it fails in \CC, which does not support VLAs, unless it is \lstinline{g++}.} and immediate operands of assembler instructions.
     197Only the @enum@ form is managed by the compiler, is part of the language type-system, and works in all C constant-expression locations.
     198
     199
    173200\section{\CFA-Style Enum}
    174201
     
    176203\CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages.
    177204
     205
     206\subsection{Enumerator Name Resolution}
     207\label{s:EnumeratorNameResolution}
     208
     209In C, unscoping of enumerators presents a \newterm{naming problem} when multiple enumeration types appear in the same scope with duplicate enumerator names.
     210There is no mechanism in C to resolve these naming conflicts other than renaming of one of the duplicates, which may be impossible.
     211
     212The \CFA type-system allows extensive overloading, including enumerators.
     213Furthermore, \CFA uses the left-hand of assignment in type resolution to pinpoint the best overloaded name.
     214Finally, qualification is provided to disambiguate any ambiguous situations.
     215\begin{lstlisting}
     216enum C1 { First, Second, Third, Fourth };
     217enum C2 { @Fourth@, @Third@, @Second@, @First@ };
     218C1 p() { return Third; }                                $\C{// correctly resolved duplicate names}$
     219C2 p() { return Fourth; }
     220void foo() {
     221        C1 e1 = First;   C2 e2 = First;
     222        e1 = Second;   e2 = Second;
     223        e1 = p();   e2 = p();                           $\C{// correctly resolved function call}$
     224        int i = @C1.@First + @C2.@First;        $\C{// ambiguous without qualification}$
     225}
     226\end{lstlisting}
     227\CFA overloading allows programmers to use the most meaningful names without fear of unresolvable clashes from included files, which are correctable with qualification.
     228
     229
     230\subsection{Enumerator Scoping}
     231
     232An enumeration can be scoped, so the enumerator constants are not projected into the enclosing scope, using @'!'@.
     233\begin{lstlisting}
     234enum Weekday @!@ { /* as above */ };
     235enum( char * ) Names @!@ { /* as above */ };
     236\end{lstlisting}
     237Now the enumerators \emph{must} be qualified with the associated enumeration.
     238\begin{lstlisting}
     239Weekday weekday = @Weekday@.Monday;
     240Names names = @Names.@Fred;
     241names = @Names.@Jane;
     242\end{lstlisting}
     243It is possible to toggle back to unscoping using the \CFA @with@ clause/statement (see also \CC \lstinline[language=c++]{using enum} in Section~\ref{s:C++RelatedWork}).
     244\begin{lstlisting}
     245Weekday weekday;
     246with ( @Weekday@, @Names@ ) {                   $\C{// type names}$
     247         Names names = @Fred@;
     248         names = @Jane@;
     249         weekday = Saturday;
     250}
     251\end{lstlisting}
     252As in Section~\ref{s:EnumeratorNameResolution}, opening multiple unscoped enumerations can result in duplicate enumeration names, but \CFA type resolution and falling back to explicit qualification handles name resolution.
     253
    178254\subsection{Enumerator Typing}
    179255
    180 \CFA extends the enumeration by parameterizing the enumeration with a type for the enumerators, allowing enumerators to be assigned any values from the declared type.
    181 Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's constants used to set the enumerators.
    182 
    183 Typed enumerates deals with \emph{harmonizing} problem between an enumeration and its companion data.
    184 The following example is from the \CFA compiler, written in \CC.
    185 \begin{lstlisting}
    186 enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES };
    187 char * integral_names[NO_OF_ITYPES] = {
    188         "char", "signed char", "unsigned char",
    189         "signed short int", "unsigned short int",
    190         "signed int", "unsigned int",
    191         ...
    192 };
    193 \end{lstlisting}
    194 The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit.
    195 It is up to the programmer to ensure changes made in one location are harmonized with the other location (by identifying this requirement within a comment).
    196 The typed enumeration largely solves this problem by combining and managing the two data types.
    197 \begin{lstlisting}
    198 enum( char * ) integral_types {
    199         chr = "char", schar = "signed char", uschar = "unsigned char",
    200         sshort = "signed short int", ushort = "unsigned short int",
    201         sint = "signed int", usint = "unsigned int",
    202         ...
    203 };
     256\CFA extends the enumeration declaration by parameterizing with a type (like a generic type), allowing enumerators to be assigned any values from the declared type.
     257Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's constants used to set the enumerator constants.
     258Note, the synonyms @Liz@ and @Beth@ in the last declaration.
     259
     260Because enumerators are constants, the enumeration type is implicitly @const@, so all the enumerator types in Figure~\ref{f:EumeratorTyping} are rewritten with @const@.
     261A typed enumeration has an implicit (safe) conversion to its base type.
     262\begin{lstlisting}
     263char currency = Dollar;
     264string fred = Fred;                                             $\C{// implicit conversion from char * to \CFA string type}$
     265Person student = Beth;
    204266\end{lstlisting}
    205267
     
    223285        enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i };
    224286// pointer
    225         enum( @char *@ ) Names { Fred = "Fred", Mary = "Mary", Jane = "Jane" };
     287        enum( @char *@ ) Names { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
    226288        int i, j, k;
    227289        enum( @int *@ ) ptr { I = &i,  J = &j,  K = &k };
    228290        enum( @int &@ ) ref { I = i,   J = j,   K = k };
    229291// tuple
    230         enum( @[int, int]@ ) { T = [ 1, 2 ] };
     292        enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$
    231293// function
    232294        void f() {...}   void g() {...}
     
    234296// aggregate
    235297        struct Person { char * name; int age, height; };
    236         enum( @Person@ ) friends { Liz = { "Elizabeth", 22, 170 }, Beth = Liz, Jon = { "Jonathan", 35, 190 } };
     298@***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz, Jon = { "JONATHAN", 35, 190 } };
    237299\end{lstlisting}
    238300\caption{Enumerator Typing}
     
    240302\end{figure}
    241303
     304Typed enumerations deals with the \emph{harmonizing} problem between an enumeration and any companion data.
     305The following example is from the \CFA compiler, written in \CC.
     306\begin{lstlisting}
     307enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES };
     308char * integral_names[NO_OF_ITYPES] = {
     309        "char", "signed char", "unsigned char",
     310        "signed short int", "unsigned short int",
     311        "signed int", "unsigned int",
     312        ...
     313};
     314\end{lstlisting}
     315The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit.
     316It is up to the programmer to ensure changes made in one location are harmonized with the other location (by identifying this requirement within a comment).
     317The typed enumeration largely solves this problem by combining and managing the two data types.
     318\begin{lstlisting}
     319enum( char * ) integral_types {
     320        chr = "char", schar = "signed char", uschar = "unsigned char",
     321        sshort = "signed short int", ushort = "unsigned short int",
     322        sint = "signed int", usint = "unsigned int",
     323        ...
     324};
     325\end{lstlisting}
     326Note, the enumeration type can be a structure (see @Person@ in Figure~\ref{f:EumeratorTyping}), so it is possible to have the equivalent of multiple arrays of companion data using an array of structures.
     327
     328
    242329\subsection{Pure Enumerators}
    243330
    244 An empty type, @enum()@, implies the enumerators are pure symbols without values;
     331An empty enumerator type, @enum()@, implies the enumerators are pure symbols without values but set properties;
    245332hence, there is no default conversion to @int@.
    246333
    247334\begin{lstlisting}
    248335enum() Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND };
    249 Mode iomode = O_RDONLY;
     336@***@Mode iomode = O_RDONLY;
     337bool b = iomode == O_RDONLY || iomode < O_APPEND;
    250338int i = iomode;                                                 $\C{\color{red}// disallowed}$
    251 sout | O_TRUNC;                                                 $\C{\color{red}// disallowed}$
    252339\end{lstlisting}
    253340
    254341\subsection{Enumerator Subset}
    255342
    256 If follows from enumerator typing that the type of the enumerators can be another enumerator.
    257 \begin{lstlisting}
     343If follows from enumerator typing that the enumerator type can be another enumerator.
     344\begin{lstlisting}
     345enum( @char@ ) Currency { Dollar = '$\textdollar$', Euro = '$\texteuro$', Pound = '$\textsterling$'  };
     346enum( @Currency@ ) Europe { Euro = Currency.Euro, Pound = Currency.Pound }; // intersection
    258347enum( char ) Letter { A = 'A',  B = 'B', C = 'C', ..., Z = 'Z' };
    259 enum( Letter ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // alphabet intersection
     348enum( @Letter@ ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // intersection
     349\end{lstlisting}
     350Subset enumerations may have more or less enumerators than their typed enumeration, but the enumerator values must be from the typed enumeration.
     351For example, @Greek@ enumerators are a subset of type @Letter@ and are type compatible with enumeration @Letter@, but @Letter@ enumerators are not type compatible with enumeration @Greek@.
     352\begin{lstlisting}
    260353Letter letter = A;
    261 Greak greek = Alph;
    262 letter = Alph;                                                  $\C{// allowed}$
     354@***@Greak greek = Beta;
     355letter = Beta;                                                  $\C{// allowed, letter == B}$
    263356greek = A;                                                              $\C{\color{red}// disallowed}$
    264357\end{lstlisting}
    265 Enumeration @Greek@ may have more or less enumerators than @Letter@, but the enumerator values must be from @Letter@.
    266 Therefore, @Greek@ enumerators are a subset of type @Letter@ and are type compatible with enumeration @Letter@, but @Letter@ enumerators are not type compatible with enumeration @Greek@.
     358
    267359
    268360\subsection{Enumeration Inheritance}
    269361
    270 \CFA Plan-9 inheritance may be used with enumerations.
    271 \begin{lstlisting}
    272 enum( char * ) Name2 { @inline Name@, Jack = "Jack", Jill = "Jill" };
    273 enum /* inferred */ Name3 { @inline Name2@, Sue = "Sue", Tom = "Tom" };
    274 \end{lstlisting}
    275 Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Name@ by containment, and a @Name@ enumeration is a subtype of enumeration @Name2@.
     362\CFA Plan-9 inheritance may be used with enumerations, where Plan-9 inheritance is containment inheritance with implicit unscoping (like a nested unnamed @struct@/@union@ in C).
     363\begin{lstlisting}
     364enum( char * ) Names { /* as above */ };
     365enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" };
     366@***@enum /* inferred */ Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" };
     367\end{lstlisting}
     368Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Names@ by containment, and a @Names@ enumeration is a subtype of enumeration @Name2@.
    276369Note, enumerators must be unique in inheritance but enumerator values may be repeated.
    277370
    278371The enumeration type for the inheriting type must be the same as the inherited type;
    279372hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@.
    280 When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
    281 
    282 Specifically, the inheritance relationship for Names is:
    283 \begin{lstlisting}
    284 Name $\(\subset\)$ Name2 $\(\subset\)$ Name3 $\(\subset\)$ const char *         // enum type of Name
     373% When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
     374Specifically, the inheritance relationship for @Names@ is:
     375\begin{lstlisting}
     376Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\(\subset\)$ const char * $\C{// enum type of Names}$
    285377\end{lstlisting}
    286378For the given function prototypes, the following calls are valid.
     
    288380\begin{tabular}{ll}
    289381\begin{lstlisting}
    290 void f( Name );
    291 void g( Name2 );
    292 void h( Name3 );
     382void f( Names );
     383void g( Names2 );
     384void h( Names3 );
    293385void j( const char * );
    294386\end{lstlisting}
     
    298390g( Fred );   g( Jill );
    299391h( Fred );   h( Jill );   h( Sue );
    300 j( Fred );   j( Jill );   j( Sue );   j( "Will" );
     392j( Fred );   j( Jill );   j( Sue );   j( "WILL" );
    301393\end{lstlisting}
    302394\end{tabular}
    303395\end{cquote}
    304 Note, the validity of calls is the same for call-by-reference as for call-by-value, and const restrictions are the same as for other types.
    305 
    306 \subsection{Enumerator Scoping}
    307 
    308 A \CFA-enum can be scoped, meaning the enumerator constants are not projected into the enclosing scope.
    309 \begin{lstlisting}
    310 enum Weekday @!@ { /* as above */ };
    311 enum Colour( char * ) @!@ { /* as above */ };
    312 \end{lstlisting}
    313 where the @'!'@ implies the enumerators are \emph{not} projected.
    314 The enumerators of a scoped enumeration are accessed using qualifications, like the fields of an aggregate.
    315 % The syntax of $qualified\_expression$ for \CFA-enum is the following:
    316 % $$<qualified\_expression> := <enum\_type>.<enumerator>$$
    317 \begin{lstlisting}
    318 Weekday weekday = @Weekday.Monday@;             $\C{// qualification}$
    319 Colour colour = @Colour.@Red;
    320 colour = @Colour.@Blue;
    321 \end{lstlisting}
     396Note, the validity of calls is the same for call-by-reference as for call-by-value, and @const@ restrictions are the same as for other types.
     397
    322398
    323399\subsection{Enumeration Pseudo-functions}
    324400
    325 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@.
     401Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@, @offsetof@, @typeof@.
    326402Often a call to a pseudo-function is substituted with information extracted from the symbol table at compilation time, like storage size or alignment associated with the underlying architecture..
    327403
    328 \subsubsection{Enumerator Attributes}
    329404The attributes of an enumerator are accessed by pseudo-functions @position@, @value@, and @label@.
    330405\begin{lstlisting}
    331 int green_pos = @position@( Colour.Green );     $\C{// 1}$
    332 char * green_value = @value@( Colour.Green ); $\C{// "G"}$
    333 char * green_label = @label@( Colour.Green ); $\C{// "Green"}$
    334 \end{lstlisting}
    335 
    336 Enumeration Greek may have more or less enumerators than Letter, but the enumerator values must be from Letter.
    337 Therefore, Greek enumerators are a subset of type Letter and are type compatible with enumeration Letter, but Letter enumerators are not type compatible with enumeration Greek.
    338 
    339 % An instance of \CFA-enum (denoted as @<enum_instance>@) is a label for the defined enum name.
    340 % The label can be retrieved by calling the function @label( <enum_instance> )@.
    341 % Similarly, the @value()@ function returns the value used to initialize the \CFA-enum.
    342 
    343 \subsubsection{\lstinline{enumerate()}}
    344 
    345 \begin{lstlisting}[label=lst:c_switch]
    346 enum(int) C_ENUM { First, Second, Third = First, Fourth };
    347 int v( C_ENUM e ) {
    348         switch( e ) {
    349                 case First: return 0; break;
    350                 case Second: return 1; break;
    351                 // case Third: return 2; break;
    352                 // case Fourth: return 3; break;
    353         };
    354 };
    355 \end{lstlisting}
    356 In the @C_ENUM@ example, @Third@ is an alias of @First@ and @Fourth@ is an alias of @Second@.
    357 Programmers cannot make case branches for @Third@ and @Fourth@ because the switch statement matches cases by the enumerator's value.
    358 Case @First@ and @Third@, or @Second@ and @Fourth@, has duplicate case values.
    359 
    360 @enumerate()@ is a pseudo-function that makes the switch statement match by an enumerator instead.
    361 \begin{lstlisting}[label=lst:c_switch_enumerate]
    362 enum(double) C_ENUM { First, Second, Third = First, Fourth };
    363 C_ENUM variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth;
    364 int v(C_ENUM e) {
    365         switch( enumeratate( e ) ) {
    366                 case First: return e; break;
    367                 case Second: return value( e ); break;
    368                 case Third: return label( e ); break;
    369                 case Fourth: return position( e ); break;
    370         };
    371 };
     406@***@int jane_pos = @position@( Names.Jane );   $\C{// 2}$
     407@***@char * jane_value = @value@( Names.Jane ); $\C{// "JANE"}$
     408@***@char * jane_label = @label@( Names.Jane ); $\C{// "Jane"}$
     409sout | @label@( Names.Jane ) | @value@( Names.Jane );
     410\end{lstlisting}
     411Note the ability to print both enumerator label and value.
     412
     413
     414\subsection{Enumerator Position or Value}
     415
     416Enumerators can be used in multiple contexts.
     417In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution).
     418However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts.
     419In these contexts, a programmer's initition assumes an implicit conversion to postion.
     420
     421For example, an intuitive use of enumerations is with the \CFA @switch@/@choose@ statement, where @choose@ performs an implict @break@ rather than a fall-through at the end of a @case@ clause.
     422\begin{cquote}
     423\begin{lstlisting}
     424enum Count { First, Second, Third, Fourth };
     425Count e;
     426\end{lstlisting}
     427\begin{tabular}{ll}
     428\begin{lstlisting}
     429
     430choose( e ) {
     431        case @First@: ...;
     432        case @Second@: ...;
     433        case @Third@: ...;
     434        case @Fourth@: ...;
     435}
     436\end{lstlisting}
     437&
     438\begin{lstlisting}
     439// rewrite
     440choose( @value@( e ) ) {
     441        case @value@( First ): ...;
     442        case @value@( Second ): ...;
     443        case @value@( Third ): ...;
     444        case @value@( Fourth ): ...;
     445}
     446\end{lstlisting}
     447\end{tabular}
     448\end{cquote}
     449Here, the intuitive code on the left is implicitly transformed into the statndard implementation on the right, using the value of the enumeration variable and enumerators.
     450However, this implementation is fragile, e.g., if the enumeration is changed to:
     451\begin{lstlisting}
     452enum Count { First, Second, Third @= First@, Fourth };
     453\end{lstlisting}
     454which make @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicase @case@ clauses.
     455To better match with programmer intuition, \CFA toggles between value and position semantics depneding on the language context.
     456For conditional clauses and switch statments, \CFA uses the robust position implementation.
     457\begin{lstlisting}
     458choose( @position@( e ) ) {
     459        case @position@( First ): ...;
     460        case @position@( Second ): ...;
     461        case @position@( Third ): ...;
     462        case @position@( Fourth ): ...;
     463}
     464\end{lstlisting}
     465
     466\begin{lstlisting}
     467Count variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth;
    372468p(variable_a); // 0
    373469p(variable_b); // 1
     
    10151111If the @aggregation_name@ is identified as a \CFA enumeration, the compiler checks if @field@ presents in the declared \CFA enumeration.
    10161112
    1017 \subsection{\lstinline{with} Clause/Statement}
    1018 
    1019 Instead of qualifying an enumeration expression every time, the @with@ can be used to expose enumerators to the current scope, making them directly accessible.
    1020 \begin{lstlisting}[label=lst:declaration]
    1021 enum Color( char * ) { Red="R", Green="G", Blue="B" };
    1022 enum Animal( int ) { Cat=10, Dog=20 };
    1023 with ( Color, Animal ) {
    1024         char * red_string = Red; // value( Color.Red )
    1025         int cat = Cat; // value( Animal.Cat )
    1026 }
    1027 \end{lstlisting}
    1028 The \lstinline{with} might introduce ambiguity to a scope. Consider the example:
    1029 \begin{lstlisting}[label=lst:declaration]
    1030 enum Color( char * ) { Red="R", Green="G", Blue="B" };
    1031 enum RGB( int ) { Red=0, Green=1, Blue=2 };
    1032 with ( Color, RGB ) {
    1033         // int red = Red;
    1034 }
    1035 \end{lstlisting}
    1036 \CFA will not try to resolve the expression with ambiguity. It would report an error. In this case, it is necessary to qualify @Red@ even inside of the \lstinline{with} clause.
    1037 
    10381113\subsection{Instance Declaration}
    10391114
     
    12181293
    12191294\subsection{\CC}
    1220 
    1221 \CC is backwards compatible with C, so it inherited C's enumerations, except there is no implicit conversion from an integral value to an enumeration;
    1222 hence, the values in a \CC enumeration can only be its enumerators (without a cast).
    1223 There is no mechanism to iterate through an enumeration.
    1224 
    1225 \CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), so the enumerators are local to the enumeration and must be accessed using type qualification, e.g., @Weekday::Monday@.
     1295\label{s:C++RelatedWork}
     1296
     1297\CC is backwards compatible with C, so it inherited C's enumerations.
     1298However, the following non-backwards compatible changes have been made.
     1299\begin{quote}
     13007.2 Change: \CC objects of enumeration type can only be assigned values of the same enumeration type.
     1301In C, objects of enumeration type can be assigned values of any integral type. \\
     1302Example:
     1303\begin{lstlisting}
     1304enum color { red, blue, green };
     1305color c = 1;                                                    $\C{// valid C, invalid C++}$
     1306\end{lstlisting}
     1307\textbf{Rationale}: The type-safe nature of C++. \\
     1308\textbf{Effect on original feature}: Deletion of semantically well-defined feature. \\
     1309\textbf{Difficulty of converting}: Syntactic transformation. (The type error produced by the assignment can be automatically corrected by applying an explicit cast.) \\
     1310\textbf{How widely used}: Common.
     1311\end{quote}
     1312\begin{quote}
     13137.2 Change: In \CC, the type of an enumerator is its enumeration.
     1314In C, the type of an enumerator is @int@. \\
     1315Example:
     1316\begin{lstlisting}
     1317enum e { A };
     1318sizeof(A) == sizeof(int)                                $\C{// in C}$
     1319sizeof(A) == sizeof(e)                                  $\C{// in C++}$
     1320/* and sizeof(int) is not necessary equal to sizeof(e) */
     1321\end{lstlisting}
     1322\textbf{Rationale}: In C++, an enumeration is a distinct type. \\
     1323\textbf{Effect on original feature}: Change to semantics of well-defined feature. \\
     1324\textbf{Difficulty of converting}: Semantic transformation. \\
     1325\textbf{How widely used}: Seldom. The only time this affects existing C code is when the size of an enumerator is taken.
     1326Taking the size of an enumerator is not a common C coding practice.
     1327\end{quote}
     1328Hence, the values in a \CC enumeration can only be its enumerators (without a cast).
     1329While the storage size of an enumerator is up to the compiler, there is still an implicit cast to @int@.
     1330\begin{lstlisting}
     1331enum E { A, B, C };
     1332E e = A;
     1333int i = A;   i = e;                                             $\C{// implicit casts to int}$
     1334\end{lstlisting}
     1335\CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), so the enumerators are local to the enumeration and must be accessed using type qualification.
     1336\begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
     1337enum class E { A, B, C };
     1338E e = @E::@A;                                                   $\C{// qualified enumerator}$
     1339e = B;                                                                  $\C{// B not in scope}$
     1340\end{lstlisting}
    12261341\CC{20} supports unscoped access with a \lstinline[language=c++]{using enum} declaration.
    1227 
    1228 For both unscoped and scoped enumerations, the underlying type is an implementation-defined integral type large enough to hold all enumerated values; it does not have to be the smallest possible type.
    1229 In \CC{11}, the underlying integral type can be explicitly specified:
    12301342\begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
    1231 enum class RGB : @long@ { Red, Green, Blue };
    1232 enum class rgb : @char@ { Red = 'r', Green = 'g', Blue = 'b' };
    1233 enum class srgb : @signed char@ { Red = -1, Green = 0, Blue = 1 };
    1234 RGB colour1 = @RGB::@Red;
    1235 rgb colour2 = @rgb::@Red;
    1236 srgb colour3 = @srgb::@Red;
    1237 \end{lstlisting}
     1343enum class E { A, B, C };
     1344@using enum E;@
     1345E e = A;                                                                $\C{// direct access}$
     1346e = B;                                                                  $\C{// direct access}$
     1347\end{lstlisting}
     1348\CC{11} added the ability to explicitly declare the underlying integral type for \lstinline[language=c++]{enum class}.
     1349\begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
     1350enum class RGB @: long@ { Red, Green, Blue };
     1351enum class rgb @: char@ { Red = 'r', Green = 'g', Blue = 'b' };
     1352enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 };
     1353\end{lstlisting}
     1354There is no implicit conversion from the \lstinline[language=c++]{enum class} type and to its type.
     1355\begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
     1356rgb crgb = rgb::Red;
     1357char ch = rgb::Red;   ch = crgb;                $\C{// disallowed}$
     1358\end{lstlisting}
     1359Finally, there is no mechanism to iterate through an enumeration nor use the enumeration type to declare an array dimension.
     1360
    12381361
    12391362\subsection{Go}
  • doc/theses/jiada_liang_MMath/background.tex

    r0522ebe ra4da45e  
    11\chapter{Background}
     2
     3
     4\section{C-Style Enum}
     5
     6The C-Style enumeration has the following syntax and semantics.
     7\begin{cfa}
     8enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
     9\end{cfa}
     10Enumerators without an explicitly designated constant value are \Newterm{auto-initialized} by the compiler: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
     11For example, @Monday@ to @Wednesday@ are implicitly assigned with constants @0@--@2@, @Thursday@ is explicitly set to constant @10@, and @Friday@ to @Sunday@ are implicitly assigned with constants @11@--@13@.
     12Initialization may occur in any order.
     13\begin{cfa}
     14enum Weekday { Thursday@ = 10@, Friday, Saturday, Sunday, Monday@ = 0@, Tuesday, Wednesday };
     15\end{cfa}
     16Note, the comma in the enumerator list can be a terminator or a separator, allowing the list to end with a dangling comma.
     17\begin{cfa}
     18enum Weekday {
     19        Thursday = 10, Friday, Saturday, Sunday,
     20        Monday = 0, Tuesday, Wednesday@,@ // terminating comma
     21};
     22\end{cfa}
     23This feature allow enumerator lines to be interchanged without moving a comma.\footnote{
     24A terminating comma appears in other C syntax, \eg the initializer list.}
     25Finally, C enumerators are \Newterm{unscoped}, \ie enumerators declared inside of an @enum@ are visible (projected) into the enclosing scope of the @enum@ type.
     26
     27In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values.
     28In practice, since integral constants are used, which have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables.
     29Finally, there is an implicit bidirectional conversion between an enumeration and its integral type.
     30\begin{cfa}
     31{
     32        enum Weekday { /* as above */ };        $\C{// enumerators implicitly projected into local scope}$
     33        Weekday weekday = Monday;                       $\C{// weekday == 0}$
     34        weekday = Friday;                                       $\C{// weekday == 11}$
     35        int i = Sunday;                                         $\C{// implicit conversion to int, i == 13}$
     36        weekday = 10000;                                        $\C{// UNDEFINED! implicit conversion to Weekday}$
     37}
     38int j = Wednesday;                                              $\C{// ERROR! Wednesday is not declared in this scope}$
     39\end{cfa}
     40The implicit conversion from @int@ to an enumeration type is an unnecessary source of error.
     41
     42It is common for C programmers to ``believe'' there are 3 equivalent forms of constant enumeration.
     43\begin{cfa}
     44#define Monday 0
     45static const int Monday = 0;
     46enum { Monday };
     47\end{cfa}
     48For @#define@, the programmer has to play compiler and explicitly manage the enumeration values;
     49furthermore, these are independent constants outside of any language type mechanism.
     50The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{
     51C allows variable-length array-declarations (VLA), so this case does work, but it fails in \CC, which does not support VLAs, unless it is \lstinline{g++}.} and immediate operands of assembler instructions.
     52Only the @enum@ form is managed by the compiler, is part of the language type-system, and works in all C constant-expression locations.
  • doc/theses/jiada_liang_MMath/intro.tex

    r0522ebe ra4da45e  
    11\chapter{Introduction}
    22
    3 Testing glossy abbreviations \gls{foo} and \gls{bar}, and glossy definitions \gls{git} and \gls{gulp}.
     3Naming values is a common practice in mathematics and engineering, \eg $\pi$, $\tau$ (2$\pi$), $\phi$ (golden ratio), MHz (1E6), etc.
     4Naming is also commonly used to represent many other numerical phenomenon, such as days of the week, months of a year, floors of a building (basement), specific times (noon, New Years).
     5Many programming languages capture this important software engineering capability through a mechanism called an \Newterm{enumeration}.
     6An enumeration is similar to other programming-language types by providing a set of constrained values, but adds the ability to name \emph{all} the values in its set.
     7Note, all enumeration names must be unique but different names can represent the same value (eight note, quaver), which are synonyms.
    48
    5 And use the glossy abbreviations \gls{foo} and \gls{bar}, and definitions \gls{git} and \gls{gulp} again.
     9Specifically, an enumerated type restricts its values to a fixed set of named constants.
     10While all types are restricted to a fixed set of values because of the underlying von Neumann architecture, and hence, to a corresponding set of constants, \eg @3@, @3.5@, @3.5+2.1i@, @'c'@, @"abc"@, etc., these values are not named, other than the programming-language supplied constant names.
     11
     12Fundamentally, all enumeration systems have an \Newterm{enumeration} type with an associated set of \Newterm{enumerator} names.
     13An enumeration has three universal attributes, \Newterm{position}, \Newterm{label}, and \Newterm{value}, as shown by this representative enumeration, where position and value can be different.
     14\begin{cquote}
     15\small\sf\setlength{\tabcolsep}{3pt}
     16\begin{tabular}{rccccccccccc}
     17\it\color{red}enumeration & \multicolumn{7}{c}{\it\color{red}enumerators}       \\
     18$\downarrow$\hspace*{25pt} & \multicolumn{7}{c}{$\downarrow$}                           \\
     19@enum@ Weekday \{                               & Monday,       & Tuesday,      & Wednesday,    & Thursday,& Friday,    & Saturday,     & Sunday \}; \\
     20\it\color{red}position                  & 0                     & 1                     & 2                             & 3                             & 4                     & 5                     & 6                     \\
     21\it\color{red}label                             & Monday        & Tuesday       & Wednesday             & Thursday              & Friday        & Saturday      & Sunday        \\
     22\it\color{red}value                             & 0                     & 1                     & 2                             & 3                             & 4                     & 5             & 6
     23\end{tabular}
     24\end{cquote}
     25Here, the \Newterm{enumeration} @Weekday@ defines the ordered \Newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
     26By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constant values.
     27Because an enumerator is a constant, it cannot appear in a mutable context, \eg @Mon = Sun@ is meaningless, and an enumerator has no address, it is an \Newterm{rvalue}\footnote{
     28The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}.
     29
     30\section{Contributions}
  • doc/theses/jiada_liang_MMath/uw-ethesis-frontpgs.tex

    r0522ebe ra4da45e  
    131131\begin{center}\textbf{Abstract}\end{center}
    132132
    133 Enumerated type ...
     133An enumeration is a type defining an ordered set of named constant values, where a name abstracts a value, \eg @PI@ versus @3.145159@.
     134C restrict an enumeration type to the integral type @signed int@, which \CC support , meaning enumeration names bind to integer constants.
     135\CFA extends C enumerations to allow all basic and custom types for the enumeration type, like other modern programming languages.
     136Furthermore, \CFA adds other useful features for enumerations to support better software-engineering practices and simplify program development.
    134137
    135138\cleardoublepage
  • doc/theses/jiada_liang_MMath/uw-ethesis.tex

    r0522ebe ra4da45e  
    9191% cfa macros used in the document
    9292\input{common}
    93 %\usepackageinput{common}
     93%\usepackage{common}
    9494\CFAStyle                                               % CFA code-style
    95 \lstset{language=CFA}                                   % default language
    96 \lstset{basicstyle=\linespread{0.9}\sf}                 % CFA typewriter font
     95\lstset{language=cfa,belowskip=-1pt}                                    % set default language to CFA
     96
    9797\newcommand{\newtermFont}{\emph}
    9898\newcommand{\Newterm}[1]{\newtermFont{#1}}
     
    211211\input{intro}
    212212\input{background}
    213 \input{content1}
    214 \input{content2}
     213\input{CFAenum}
     214\input{implementation}
     215\input{relatedwork}
    215216\input{performance}
    216217\input{conclusion}
  • doc/user/user.tex

    r0522ebe ra4da45e  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Tue Jan 30 09:02:41 2024
    14 %% Update Count     : 6046
     13%% Last Modified On : Mon Feb 12 11:50:26 2024
     14%% Update Count     : 6199
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    41774177The \CFA header file for the I/O library is \Indexc{fstream.hfa}.
    41784178
     4179
     4180\subsubsection{Stream Output}
     4181
    41794182For implicit formatted output, the common case is printing a series of variables separated by whitespace.
    41804183\begin{cquote}
     
    42554258Note, \CFA stream variables ©stdin©, ©stdout©, ©stderr©, ©exit©, and ©abort© overload C variables ©stdin©, ©stdout©, ©stderr©, and functions ©exit© and ©abort©, respectively.
    42564259
     4260
     4261\subsubsection{Stream Input}
     4262
    42574263For implicit formatted input, the common case is reading a sequence of values separated by whitespace, where the type of an input constant must match with the type of the input variable.
    42584264\begin{cquote}
    42594265\begin{lrbox}{\myboxA}
    42604266\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4261 int x;   double y   char z;
     4267char c;   int i;   double d
    42624268\end{cfa}
    42634269\end{lrbox}
     
    42664272\multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}}        & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}}       & \multicolumn{1}{c}{\textbf{Python}}   \\
    42674273\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4268 sin | x | y | z;
     4274sin | c | i | d;
    42694275\end{cfa}
    42704276&
    42714277\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4272 cin >> x >> y >> z;
     4278cin >> c >> i >> d;
    42734279\end{cfa}
    42744280&
    42754281\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4276 x = int(input());  y = float(input());  z = input();
     4282c = input();   i = int(input());   d = float(input());
    42774283\end{cfa}
    42784284\\
    42794285\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4280 ®1® ®2.5® ®A®
     4286®A® ®1® ®2.5®
    42814287
    42824288
     
    42844290&
    42854291\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4286 ®1® ®2.5® ®A®
     4292®A® ®1® ®2.5®
    42874293
    42884294
     
    42904296&
    42914297\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     4298®A®
    42924299®1®
    42934300®2.5®
    4294 ®A®
    42954301\end{cfa}
    42964302\end{tabular}
     
    43094315For floating-point types, any number of decimal digits, optionally preceded by a sign (©+© or ©-©), optionally containing a decimal point, and optionally followed by an exponent, ©e© or ©E©, with signed (optional) decimal digits.
    43104316Floating-point values can also be written in hexadecimal format preceded by ©0x© or ©0X© with hexadecimal digits and exponent denoted by ©p© or ©P©.
    4311 In all cases, all whitespace characters are skipped until an appropriate value is found.
    4312 \Textbf{If an appropriate value is not found, the exception ©missing_data© is raised.}
    4313 
    4314 For the C-string type, there are two input forms: any number of \Textbf{non-whitespace} characters or a quoted sequence containing any characters except the closing quote, \ie there is no escape character supported in the string..
    4315 In both cases, the string is null terminated ©'\0'©.
    4316 For the quoted string, the start and end quote characters can be any character and do not have to match \see{\ref{XXX}}.
    4317 
    4318 \VRef[Figure]{f:IOStreamFunctions} shows the I/O stream operations for interacting with files other than ©cin©, ©cout©, and ©cerr©.
     4317In all cases, whitespace characters are skipped until an appropriate value is found.
     4318\begin{cfa}[belowskip=0pt]
     4319char ch;  int i;  float f; double d;  _Complex double cxd;
     4320sin | ch | i | f | d | cxd;
     4321X   42   1234.5     0xfffp-2    3.5+7.1i
     4322\end{cfa}
     4323It is also possible to scan and ignore specific strings and whitespace using a string format.
     4324\begin{cfa}[belowskip=0pt]
     4325sin | "abc def";                                                §\C{// space matches arbitrary whitespace (2 blanks, 2 tabs)}§
     4326\end{cfa}
     4327\begin{cfa}[showspaces=true,showtabs=true,aboveskip=0pt,belowskip=0pt]
     4328®abc            def®
     4329\end{cfa}
     4330A non-whitespace format character reads the next input character, compares the format and input characters, and if equal, the input character is discarded and the next format character is tested.
     4331Note, a single whitespace in the format string matches \Textbf{any} quantity of whitespace characters from the stream (including none).
     4332
     4333For the C-string type, the default input format is any number of \Textbf{non-whitespace} characters.
     4334There is no escape character supported in an input string, but any Latin-1 character can be typed directly in the input string.
     4335For example, if the following non-whitespace output is redirected into a file by the shell:
     4336\begin{cfa}[belowskip=0pt]
     4337sout | "\n\t\f\0234\x23";
     4338\end{cfa}
     4339it can be read back from the file by redirecting the file as input using:
     4340\begin{cfa}[belowskip=0pt]
     4341char s[64];
     4342sin | wdi( sizeof(s), s );                              §\C{// must specify string size}§
     4343\end{cfa}
     4344The input string is always null terminated ©'\0'© in the input variable.
     4345Because of potential buffer overrun when reading C strings, strings are restricted to work with input manipulators \see{\VRef{s:InputManipulators}}.
     4346As well, there are multiple input-manipulators for scanning complex input string formats, \eg a quoted character or string.
     4347
     4348\Textbf{In all cases, if an invalid data value is not found for a type or format string, the exception ©missing_data© is raised and the input variable is unchanged.}
     4349For example, when reading an integer and the string ©"abc"© is found, the exception ©missing_data© is raised to ensure the program does not proceed erroneously.
     4350If a valid data value is found, but it is larger than the capacity of the input variable, such reads are undefined.
     4351
     4352
     4353\subsubsection{Stream Files}
     4354
     4355\VRef[Figure]{f:IOStreamFunctions} shows the I/O stream operations for interacting with files other than ©sin©, ©sout©, and ©cerr©.
    43194356\begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
    43204357\item
     
    49324969
    49334970\subsection{Input Manipulators}
    4934 
    4935 The following \Index{manipulator}s control scanning of input values (reading), and only affect the format of the argument.
    4936 
    4937 Certain manipulators support a \newterm{scanset}, which is a simple regular expression, where the matching set contains any Latin-1 character (8-bits) or character ranges using minus.
     4971\label{s:InputManipulators}
     4972
     4973A string variable \emph{must} be large enough to contain the input sequence.
     4974To force programmers to consider buffer overruns for C-string input, C-strings may only be read with a width field, which should specify a size less than or equal to the C-string size, \eg:
     4975\begin{cfa}
     4976char line[64];
     4977sin | wdi( ®sizeof(line)®, line );              §\C{// must specify string size}§
     4978\end{cfa}
     4979
     4980Certain input manipulators support a \newterm{scanset}, which is a simple regular expression, where the matching set contains any Latin-1 character (8-bits) or character ranges using minus.
    49384981For example, the scanset \lstinline{"a-zA-Z -/?§"} matches any number of characters between ©'a'© and ©'z'©, between ©'A'© and ©'Z'©, between space and ©'/'©, and characters ©'?'© and (Latin-1) ©'§'©.
    49394982The following string is matched by this scanset:
    49404983\begin{cfa}
    4941 !&%$  abAA () ZZZ  ??  xx§\S\S\S§
    4942 \end{cfa}
    4943 To match a minus, put it as the first character, ©"-0-9"©.
    4944 Other complex forms of regular-expression matching are not supported.
    4945 
    4946 A string variable \emph{must} be large enough to contain the input sequence.
    4947 To force programmers to consider buffer overruns for C-string input, C-strings can only be read with a width field, which should specify a size less than or equal to the C-string size, \eg:
    4948 \begin{cfa}
    4949 char line[64];
    4950 sin | wdi( ®sizeof(line)®, line ); // must specify size
    4951 \end{cfa}
    4952 
    4953 Currently, there is no mechanism to detect if a value read exceeds the capwhen Most types are finite sized, \eg integral types only store value that fit into their corresponding storage, 8, 16, 32, 64, 128 bits.
    4954 Hence, an input value may be too large, and the result of the read is often considered undefined, which leads to difficlt to locate runtime errors.
    4955 All reads in \CFA check if values do not fit into the argument variable's type and raise the exception
    4956 All types are
     4984!&%$  abAA () ZZZ  ??§\S§  xx§\S\S§
     4985\end{cfa}
     4986To match a minus, make it the first character in the set, \eg ©"©{\color{red}\raisebox{-1pt}{\texttt{-}}}©0-9"©.
     4987Other complex forms of regular-expression matching are unsupported.
     4988
     4989The following \Index{manipulator}s control scanning of input values (reading) and only affect the format of the argument.
    49574990
    49584991\begin{enumerate}
    49594992\item
    4960 \Indexc{skip}( scanset )\index{manipulator!skip@©skip©}, ©skip©( $N$ )
    4961 The first form uses a scanset to skip matching characters.
    4962 The second form skips the next $N$ characters, including newline.
    4963 If the match successes, the input characters are discarded, and input continues with the next character.
     4993\Indexc{skip}( \textit{scanset} )\index{manipulator!skip@©skip©}, ©skip©( $N$ )
     4994consumes either the \textit{scanset} or the next $N$ characters, including newlines.
     4995If the match successes, the input characters are ignored, and input continues with the next character.
    49644996If the match fails, the input characters are left unread.
    49654997\begin{cfa}[belowskip=0pt]
    4966 char sk[§\,§] = "abc";
    4967 sin | "abc " | skip( sk ) | skip( 5 ); // match input sequence
     4998char scanset[§\,§] = "abc";
     4999sin | "abc§\textvisiblespace§" | skip( scanset ) | skip( 5 ); §\C{// match and skip input sequence}§
    49685000\end{cfa}
    49695001\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4970 ®abc   ®
    4971 ®abc  ®
    4972 ®xx®
    4973 \end{cfa}
    4974 
    4975 \item
    4976 \Indexc{wdi}( maximum, variable )\index{manipulator!wdi@©wdi©}
    4977 For all types except ©char *©, whitespace is skipped until an appropriate value is found for the specified variable type.
    4978 maximum is the maximum number of characters read for the current operation.
     5002®abc   abc  xxx®
     5003\end{cfa}
     5004Again, the blank in the format string ©"abc©\textvisiblespace©"© matches any number of whitespace characters.
     5005
     5006\item
     5007\Indexc{wdi}( \textit{maximum}, ©T & v© )\index{manipulator!wdi@©wdi©}
     5008For all types except ©char *©, whitespace is skipped and the longest sequence of non-whitespace characters matching an appropriate typed (©T©) value is read, converted into its corresponding internal form, and written into the ©T© variable.
     5009\textit{maximum} is the maximum number of characters read for the current value rather than the longest sequence.
    49795010\begin{cfa}[belowskip=0pt]
    49805011char ch;   char ca[3];   int i;   double d;   
     
    49855016\end{cfa}
    49865017Here, ©ca[0]© is type ©char©, so the width reads 3 characters \Textbf{without} a null terminator.
     5018If an input value is not found for a variable, the exception ©missing_data© is raised, and the input variable is unchanged.
    49875019
    49885020Note, input ©wdi© cannot be overloaded with output ©wd© because both have the same parameters but return different types.
     
    49905022
    49915023\item
    4992 \Indexc{wdi}( maximum size, ©char s[]© )\index{manipulator!wdi@©wdi©}
    4993 For type ©char *©, maximum is the maximum number of characters read for the current operation.
    4994 Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence
     5024\Indexc{wdi}( $maximum\ size$, ©char s[]© )\index{manipulator!wdi@©wdi©}
     5025For type ©char *©, whitespace is skippped and the longest sequence of non-whitespace characters is read, without conversion, and written into the string variable (null terminated).
     5026$maximum\ size$ is the maximum number of characters in the string variable.
     5027If the non-whitespace sequence of input characters is greater than $maximum\ size - 1$ (null termination), the exception ©cstring_length© is raised.
    49955028\begin{cfa}[belowskip=0pt]
    4996 char cstr[10];
    4997 sin | wdi( sizeof(cstr), cstr );
     5029char cs[10];
     5030sin | wdi( sizeof(cs), cs );
    49985031\end{cfa}
    49995032\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5000 ®abcd1233.456E+2®
    5001 \end{cfa}
    5002 
    5003 \item
    5004 \Indexc{wdi}( maximum size, maximum read, ©char s[]© )\index{manipulator!wdi@©wdi©}
    5005 For type ©char *©, maximum is the maximum number of characters read for the current operation.
     5033®012345678®
     5034\end{cfa}
     5035Nine non-whitespace character are read and the null character is added to make ten.
     5036
     5037\item
     5038\Indexc{wdi}( $maximum\ size$, $maximum\ read$, ©char s[]© )\index{manipulator!wdi@©wdi©}
     5039This manipulator is the same as the previous one, except $maximum$ $read$ is the maximum number of characters read for the current value rather than the longest sequence, where $maximum\ read$ $\le$ $maximum\ size$.
    50065040\begin{cfa}[belowskip=0pt]
    5007 char ch;   char ca[3];   int i;   double d;   
    5008 sin | wdi( sizeof(ch), ch ) | wdi( sizeof(ca), ca[0] ) | wdi( 3, i ) | wdi( 8, d );  // c == 'a', ca == "bcd", i == 123, d == 345.6
     5041char cs[10];
     5042sin | wdi( sizeof(cs), 9, cs );
    50095043\end{cfa}
    50105044\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5011 ®abcd1233.456E+2®
    5012 \end{cfa}
    5013 
    5014 \item
    5015 \Indexc{ignore}( reference-value )\index{manipulator!ignore@©ignore©}
    5016 For all types, the data is read from the stream depending on the argument type but ignored, \ie it is not stored in the argument.
     5045®012345678®9
     5046\end{cfa}
     5047The exception ©cstring_length© is not raised, because the read stops reading after nine characters.
     5048
     5049\item
     5050\Indexc{getline}( $wdi\ manipulator$, ©const char delimiter = '\n'© )\index{manipulator!getline@©getline©}
     5051consumes the scanset ©"[^D]D"©, where ©D© is the ©delimiter© character, which reads all characters from the current input position to the delimiter character into the string (null terminated), and consumes and ignores the delimiter.
     5052If the delimiter character is omitted, it defaults to ©'\n'© (newline).
    50175053\begin{cfa}[belowskip=0pt]
    5018 double d;
    5019 sin | ignore( d );  // d is unchanged
     5054char cs[10];
     5055sin | getline( wdi( sizeof(cs), cs ) );
     5056sin | getline( wdi( sizeof(cs), cs ), 'X' ); §\C{// X is the line delimiter}§
    50205057\end{cfa}
    50215058\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5022 ®  -75.35e-4® 25
    5023 \end{cfa}
    5024 
    5025 \item
    5026 \Indexc{incl}( scanset, wdi-input-string )\index{manipulator!incl@©incl©}
    5027 For C-string types only, the scanset matches any number of characters \emph{in} the set.
    5028 Matching characters are read into the C input-string and null terminated.
     5059®abc ?? #@%®
     5060®abc ?? #@%X® w
     5061\end{cfa}
     5062The same value is read for both input strings.
     5063
     5064\item
     5065\Indexc{quoted}( ©char & ch©, ©const char Ldelimiter = '\''©, ©const char Rdelimiter = '\0'© )\index{manipulator!quoted@©quoted©}
     5066consumes the string ©"LCR"©, where ©L© is the left ©delimiter© character, ©C© is the value in ©ch©, and ©R© is the right delimiter character, which skips whitespace, consumes and ignores the left delimiter, reads a single character into ©ch©, and consumes and ignores the right delimiter (3 characters).
     5067If the delimit character is omitted, it defaults to ©'\''© (single quote).
    50295068\begin{cfa}[belowskip=0pt]
    5030 char s[10];
    5031 sin | incl( "abc", s );
     5069char ch;
     5070sin | quoted( ch );   sin | quoted( ch, '"' );   sin | quoted( ch, '[', ']' );
     5071\end{cfa}
     5072\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     5073®   'a'  "a"[a]®
     5074\end{cfa}
     5075
     5076\item
     5077\begin{sloppypar}
     5078\Indexc{quoted}( $wdi\ manipulator$, ©const char Ldelimiter = '\''©, ©const char Rdelimiter = '\0'© )\index{manipulator!quoted@©quoted©}
     5079consumes the scanset ©"L[^R]R"©, where ©L© is the left ©delimiter© character and ©R© is the right delimiter character, which skips whitespace, consumes and ignores the left delimiter, reads characters until the right-delimiter into the string variable (null terminated), and consumes and ignores the right delimiter.
     5080If the delimit character is omitted, it defaults to ©'\''© (single quote).
     5081\end{sloppypar}
     5082\begin{cfa}[belowskip=0pt]
     5083char cs[10];
     5084sin | quoted( wdi( sizeof(cs), cs ) ); §\C[3in]{// " is the start/end delimiter}§
     5085sin | quoted( wdi( sizeof(cs), cs ), '\'' ); §\C{// ' is the start/end delimiter}§
     5086sin | quoted( wdi( sizeof(cs), cs ), '[', ']' ); §\C{// [ is the start and ] is the end delimiter}\CRT§
     5087\end{cfa}
     5088\begin{cfa}[showspaces=true]
     5089®   "abc"  'abc'[abc]®
     5090\end{cfa}
     5091
     5092\item
     5093\Indexc{incl}( scanset, $wdi\ manipulator$ )\index{manipulator!incl@©incl©}
     5094consumes the scanset, which reads all the scanned characters into the string variable (null terminated).
     5095\begin{cfa}[belowskip=0pt]
     5096char cs[10];
     5097sin | incl( "abc", cs );
    50325098\end{cfa}
    50335099\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     
    50365102
    50375103\item
    5038 \Indexc{excl}( scanset, wdi-input-string )\index{manipulator!excl@©excl©}
    5039 For C-string types, the scanset matches any number of characters \emph{not in} the set.
    5040 Non-matching characters are read into the C input-string and null terminated.
     5104\Indexc{excl}( scanset, $wdi\ manipulator$ )\index{manipulator!excl@©excl©}
     5105consumes the \emph{not} scanset, which reads all the scanned characters into the string variable (null terminated).
    50415106\begin{cfa}[belowskip=0pt]
    5042 char s[10];
    5043 sin | excl( "abc", s );
     5107char cs[10];
     5108sin | excl( "abc", cs );
    50445109\end{cfa}
    50455110\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     
    50475112\end{cfa}
    50485113
    5049 \Indexc{quoted}( char delimit, wdi-input-string )\index{manipulator!quoted@©quoted©}
    5050 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character.
    5051 If the delimit character is omitted, it defaults to ©'\n'© (newline).
    5052 
    5053 \item
    5054 \Indexc{getline}( char delimit, wdi-input-string )\index{manipulator!getline@©getline©}
    5055 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character.
    5056 If the delimit character is omitted, it defaults to ©'\n'© (newline).
     5114\item
     5115\Indexc{ignore}( ©T & v© or ©const char cs[]© or $string\ manipulator$ )\index{manipulator!ignore@©ignore©}
     5116consumes the appropriate characters for the type and ignores them, so the input variable is unchanged.
     5117\begin{cfa}
     5118double d;
     5119char cs[10];
     5120sin | ignore( d );                                              §\C{// d is unchanged}§
     5121sin | ignore( cs );                                             §\C{// cs is unchanged, no wdi required}§
     5122sin | ignore( quoted( wdi( sizeof(cs), cs ) ) ); §\C{// cs is unchanged}§
     5123\end{cfa}
     5124\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     5125®  -75.35e-4 25 "abc"®
     5126\end{cfa}
    50575127\end{enumerate}
    50585128
  • libcfa/src/Exception.hfa

    r0522ebe ra4da45e  
    22
    33// TEMPORARY
    4 #define ExceptionDecl( name, fields... ) exception name{ fields }; __attribute__(( cfa_linkonce )) vtable( name ) name ## _vt
    5 #define ExceptionInst( name, values... ) (name){ &name ## _vt, values }
     4#define ExceptionDecl( name, fields... ) exception name{ fields }; \
     5        __attribute__(( cfa_linkonce )) vtable( name ) name ## _vt
     6#define ExceptionArgs( name, args... ) &name ## _vt, args
     7#define ExceptionInst( name, args... ) (name){ ExceptionArgs( name, args ) }
  • libcfa/src/bits/signal.hfa

    r0522ebe ra4da45e  
    3434        act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
    3535        sigemptyset( &act.sa_mask );
    36         sigaddset( &act.sa_mask, SIGALRM );             // disabled during signal handler
     36        sigaddset( &act.sa_mask, SIGALRM );                                     // disabled during signal handler
    3737        sigaddset( &act.sa_mask, SIGUSR1 );
    3838        sigaddset( &act.sa_mask, SIGSEGV );
     
    4040        sigaddset( &act.sa_mask, SIGILL );
    4141        sigaddset( &act.sa_mask, SIGFPE );
    42         sigaddset( &act.sa_mask, SIGHUP );              // revert to default on second delivery
     42        sigaddset( &act.sa_mask, SIGHUP );                                      // revert to default on second delivery
    4343        sigaddset( &act.sa_mask, SIGTERM );
    4444        sigaddset( &act.sa_mask, SIGINT );
  • libcfa/src/collections/string.cfa

    r0522ebe ra4da45e  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 14 12:03:47 2024
    13 // Update Count     : 240
     12// Last Modified On : Wed Feb  7 21:17:06 2024
     13// Update Count     : 259
    1414//
    1515
     
    210210}
    211211
    212 void ?|?( ifstream & in, string & s ) {
    213     in | (*s.inner);
    214 }
     212ifstream & ?|?( ifstream & is, _Istream_Squoted f ) {
     213        _Istream_Rquoted f2 = { { f.sstr.s.inner, (_Istream_str_base)f.sstr } };
     214    return is | f2;
     215} // ?|?
    215216
    216217ifstream & ?|?( ifstream & is, _Istream_Sstr f ) {
     218//      _Istream_Rstr f2 = {f.sstr.s.inner, (_Istream_str_base)f.sstr};
    217219        _Istream_Rstr f2 = {f.s.inner, (_Istream_str_base)f};
    218220    return is | f2;
    219221} // ?|?
    220 
    221 void ?|?( ifstream & in, _Istream_Sstr f ) {
    222     (ifstream &)(in | f);
    223 }
    224222
    225223////////////////////////////////////////////////////////
  • libcfa/src/collections/string.hfa

    r0522ebe ra4da45e  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 14 12:03:46 2024
    13 // Update Count     : 81
     12// Last Modified On : Tue Feb  6 20:59:18 2024
     13// Update Count     : 118
    1414//
    1515
     
    8080void ?|?(ofstream & out, const string & s);
    8181ifstream & ?|?(ifstream & in, string & s);
    82 void ?|?( ifstream & in, string & s );
    8382
    8483static inline {
     
    9695void ?|?( ofstream & os, _Ostream_Manip(string) );
    9796
     97struct _Istream_Swidth {
     98        string & s;
     99        inline _Istream_str_base;
     100}; // _Istream_Swidth
     101
     102struct _Istream_Squoted {
     103        _Istream_Swidth sstr;
     104}; // _Istream_Squoted
     105
    98106struct _Istream_Sstr {
    99107        string & s;
    100108        inline _Istream_str_base;
     109//      _Istream_Swidth sstr;
    101110}; // _Istream_Sstr
    102111
    103112static inline {
    104113        // read width does not include null terminator
    105         _Istream_Sstr wdi( unsigned int rwd, string & s ) { return (_Istream_Sstr)@{ s, {{0p}, rwd, {.flags.rwd : true}} }; }
     114        _Istream_Swidth wdi( unsigned int rwd, string & s ) { return (_Istream_Swidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
    106115        _Istream_Sstr getline( string & s, const char delimiter = '\n' ) {
    107                 return (_Istream_Sstr)@{ s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    108         }
    109         _Istream_Sstr & getline( _Istream_Sstr & fmt, const char delimiter = '\n' ) {
    110                 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;
    111         }
    112         _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : false}} }; }
    113         _Istream_Sstr & incl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
    114         _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : true}} }; }
    115         _Istream_Sstr & excl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
    116         _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.ignore : true}} }; }
    117         _Istream_Sstr & ignore( _Istream_Sstr & fmt ) { fmt.flags.ignore = true; return fmt; }
     116//              return (_Istream_Sstr)@{ { .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } };
     117                return (_Istream_Sstr)@{ .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } };
     118        }
     119        _Istream_Sstr & getline( _Istream_Swidth & f, const char delimiter = '\n' ) {
     120                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Sstr &)f;
     121        }
     122        _Istream_Squoted quoted( string & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) {
     123                return (_Istream_Squoted)@{ { .s : s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } };
     124        }
     125        _Istream_Squoted & quoted( _Istream_Swidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) {
     126                f.delimiters[0] = Ldelimiter;  f.delimiters[1] = Rdelimiter;  f.delimiters[2] = '\0';
     127                return (_Istream_Squoted &)f;
     128        }
     129//      _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } } }; }
     130        _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; }
     131        _Istream_Sstr & incl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Sstr &)f; }
     132//      _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } } }; }
     133        _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; }
     134        _Istream_Sstr & excl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Sstr &)f; }
     135//      _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; }
     136        _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     137        _Istream_Sstr & ignore( _Istream_Swidth & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; }
     138        _Istream_Squoted & ignore( _Istream_Squoted & f ) { f.sstr.flags.ignore = true; return (_Istream_Squoted &)f; }
     139//      _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.sstr.flags.ignore = true; return (_Istream_Sstr &)f; }
     140        _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; }
    118141} // distribution
     142ifstream & ?|?( ifstream & is, _Istream_Squoted f );
    119143ifstream & ?|?( ifstream & is, _Istream_Sstr f );
    120 void ?|?( ifstream & is, _Istream_Sstr t );
     144static inline ifstream & ?|?( ifstream & is, _Istream_Swidth f ) { return is | *(_Istream_Sstr *)&f; }
    121145
    122146// Concatenation
  • libcfa/src/collections/string_res.cfa

    r0522ebe ra4da45e  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Jan 22 23:12:42 2024
    13 // Update Count     : 43
     12// Last Modified On : Sat Feb 10 17:47:22 2024
     13// Update Count     : 83
    1414//
    1515
     
    2525
    2626#include <assert.h>
    27 #include <complex.h>                           // creal, cimag
     27#include <complex.h>                                               // creal, cimag
    2828
    2929//######################### VbyteHeap "header" #########################
     
    3434
    3535struct VbyteHeap {
    36     int NoOfCompactions;                                                // number of compactions of the byte area
    37     int NoOfExtensions;                                                 // number of extensions in the size of the byte area
    38     int NoOfReductions;                                                 // number of reductions in the size of the byte area
    39    
    40     int InitSize;                                                               // initial number of bytes in the byte-string area
    41     int CurrSize;                                                               // current number of bytes in the byte-string area
    42     char *StartVbyte;                                                   // pointer to the `st byte of the start of the byte-string area
    43     char *EndVbyte;                                                             // pointer to the next byte after the end of the currently used portion of byte-string area
    44     void *ExtVbyte;                                                             // pointer to the next byte after the end of the byte-string area
    45 
    46     HandleNode Header;                                                  // header node for handle list
     36        int NoOfCompactions;                                                            // number of compactions of the byte area
     37        int NoOfExtensions;                                                                     // number of extensions in the size of the byte area
     38        int NoOfReductions;                                                                     // number of reductions in the size of the byte area
     39       
     40        int InitSize;                                                                           // initial number of bytes in the byte-string area
     41        int CurrSize;                                                                           // current number of bytes in the byte-string area
     42        char *StartVbyte;                                                                       // pointer to the `st byte of the start of the byte-string area
     43        char *EndVbyte;                                                                         // pointer to the next byte after the end of the currently used portion of byte-string area
     44        void *ExtVbyte;                                                                         // pointer to the next byte after the end of the byte-string area
     45
     46        HandleNode Header;                                                                      // header node for handle list
    4747}; // VbyteHeap
    4848
    49    
    50 static void compaction( VbyteHeap & );                  // compaction of the byte area
    51 static void garbage( VbyteHeap &, int );                // garbage collect the byte area
    52 static void extend( VbyteHeap &, int );                 // extend the size of the byte area
    53 static void reduce( VbyteHeap &, int );                 // reduce the size of the byte area
     49
     50static void compaction( VbyteHeap & );                                  // compaction of the byte area
     51static void garbage( VbyteHeap &, int );                                // garbage collect the byte area
     52static void extend( VbyteHeap &, int );                                 // extend the size of the byte area
     53static void reduce( VbyteHeap &, int );                                 // reduce the size of the byte area
    5454
    5555static void ?{}( VbyteHeap &, size_t = 1000 );
    5656static void ^?{}( VbyteHeap & );
    5757
    58 static int ByteCmp( char *, int, int, char *, int, int );       // compare 2 blocks of bytes
     58static int ByteCmp( char *, int, int, char *, int, int ); // compare 2 blocks of bytes
    5959static char *VbyteAlloc( VbyteHeap &, int );                    // allocate a block bytes in the heap
    6060static char *VbyteTryAdjustLast( VbyteHeap &, int );
     
    6262static void AddThisAfter( HandleNode &, HandleNode & );
    6363static void DeleteNode( HandleNode & );
    64 static void MoveThisAfter( HandleNode &, const HandleNode & );          // move current handle after parameter handle
     64static void MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle
    6565
    6666
     
    6969static void ?{}( VbyteHeap & s, size_t Size ) with(s) {
    7070#ifdef VbyteDebug
    71     serr | "enter:VbyteHeap::VbyteHeap, s:" | &s | " Size:" | Size;
    72 #endif // VbyteDebug
    73     NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
    74     InitSize = CurrSize = Size;
    75     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    76     ExtVbyte = (void *)( StartVbyte + CurrSize );
    77     Header.flink = Header.blink = &Header;
    78     Header.ulink = &s;
    79 #ifdef VbyteDebug
    80     HeaderPtr = &Header;
    81     serr | "exit:VbyteHeap::VbyteHeap, s:" | &s;
     71        serr | "enter:VbyteHeap::VbyteHeap, s:" | &s | " Size:" | Size;
     72#endif // VbyteDebug
     73        NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
     74        InitSize = CurrSize = Size;
     75        StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     76        ExtVbyte = (void *)( StartVbyte + CurrSize );
     77        Header.flink = Header.blink = &Header;
     78        Header.ulink = &s;
     79#ifdef VbyteDebug
     80        HeaderPtr = &Header;
     81        serr | "exit:VbyteHeap::VbyteHeap, s:" | &s;
    8282#endif // VbyteDebug
    8383} // VbyteHeap
     
    8787
    8888static void ^?{}( VbyteHeap & s ) with(s) {
    89     free( StartVbyte );
     89        free( StartVbyte );
    9090} // ~VbyteHeap
    9191
     
    9999static void ?{}( HandleNode & s ) with(s) {
    100100#ifdef VbyteDebug
    101     serr | "enter:HandleNode::HandleNode, s:" | &s;
    102 #endif // VbyteDebug
    103     s = 0;
    104     lnth = 0;
    105 #ifdef VbyteDebug
    106     serr | "exit:HandleNode::HandleNode, s:" | &s;
     101        serr | "enter:HandleNode::HandleNode, s:" | &s;
     102#endif // VbyteDebug
     103        s = 0;
     104        lnth = 0;
     105#ifdef VbyteDebug
     106        serr | "exit:HandleNode::HandleNode, s:" | &s;
    107107#endif // VbyteDebug
    108108} // HandleNode
     
    114114static void ?{}( HandleNode & s, VbyteHeap & vh ) with(s) {
    115115#ifdef VbyteDebug
    116     serr | "enter:HandleNode::HandleNode, s:" | &s;
    117 #endif // VbyteDebug
    118     s = 0;
    119     lnth = 0;
    120     ulink = &vh;
    121     AddThisAfter( s, *vh.Header.blink );
    122 #ifdef VbyteDebug
    123     serr | "exit:HandleNode::HandleNode, s:" | &s;
     116        serr | "enter:HandleNode::HandleNode, s:" | &s;
     117#endif // VbyteDebug
     118        s = 0;
     119        lnth = 0;
     120        ulink = &vh;
     121        AddThisAfter( s, *vh.Header.blink );
     122#ifdef VbyteDebug
     123        serr | "exit:HandleNode::HandleNode, s:" | &s;
    124124#endif // VbyteDebug
    125125} // HandleNode
     
    131131static void ^?{}( HandleNode & s ) with(s) {
    132132#ifdef VbyteDebug
    133     serr | "enter:HandleNode::~HandleNode, s:" | & s;
    134     {
    135         serr | nlOff;
    136         serr | " lnth:" | lnth | " s:" | (void *)s | ",\"";
    137         for ( i; lnth ) {
    138             serr | s[i];
    139         } // for
    140         serr | "\" flink:" | flink | " blink:" | blink | nl;
    141         serr | nlOn;
    142     }
    143 #endif // VbyteDebug
    144     DeleteNode( s );
     133        serr | "enter:HandleNode::~HandleNode, s:" | & s;
     134        {
     135                serr | nlOff;
     136                serr | " lnth:" | lnth | " s:" | (void *)s | ",\"";
     137                for ( i; lnth ) {
     138                        serr | s[i];
     139                } // for
     140                serr | "\" flink:" | flink | " blink:" | blink | nl;
     141                serr | nlOn;
     142        }
     143#endif // VbyteDebug
     144        DeleteNode( s );
    145145} // ~HandleNode
    146146
     
    148148//######################### String Sharing Context #########################
    149149
    150 static string_sharectx * ambient_string_sharectx;               // fickle top of stack
     150static string_sharectx * ambient_string_sharectx;               // fickle top of stack
    151151static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
    152152
    153153void ?{}( string_sharectx & s, StringSharectx_Mode mode ) with( s ) {
    154     (older){ ambient_string_sharectx };
    155     if ( mode == NEW_SHARING ) {
    156         (activeHeap){ new( (size_t) 1000 ) };
    157     } else {
    158         verify( mode == NO_SHARING );
    159         (activeHeap){ 0p };
    160     }
    161     ambient_string_sharectx = & s;
     154        (older){ ambient_string_sharectx };
     155        if ( mode == NEW_SHARING ) {
     156                (activeHeap){ new( (size_t) 1000 ) };
     157        } else {
     158                verify( mode == NO_SHARING );
     159                (activeHeap){ 0p };
     160        }
     161        ambient_string_sharectx = & s;
    162162}
    163163
    164164void ^?{}( string_sharectx & s ) with( s ) {
    165     if ( activeHeap ) delete( activeHeap );
    166 
    167     // unlink s from older-list starting from ambient_string_sharectx
    168     // usually, s==ambient_string_sharectx and the loop runs zero times
    169     string_sharectx *& c = ambient_string_sharectx;
    170     while ( c != &s ) &c = &c->older;              // find s
    171     c = s.older;                                   // unlink
     165        if ( activeHeap ) delete( activeHeap );
     166
     167        // unlink s from older-list starting from ambient_string_sharectx
     168        // usually, s==ambient_string_sharectx and the loop runs zero times
     169        string_sharectx *& c = ambient_string_sharectx;
     170        while ( c != &s ) &c = &c->older;                                       // find s
     171        c = s.older;                                                                            // unlink
    172172}
    173173
     
    176176
    177177VbyteHeap * DEBUG_string_heap() {
    178     assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
    179     return ambient_string_sharectx->activeHeap;
     178        assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
     179        return ambient_string_sharectx->activeHeap;
    180180}
    181181
    182182size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
    183     return ((char *)heap->ExtVbyte) - heap->EndVbyte;
     183        return ((char *)heap->ExtVbyte) - heap->EndVbyte;
    184184}
    185185
    186186size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
    187     return heap->CurrSize;
     187        return heap->CurrSize;
    188188}
    189189
    190190const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
    191     return heap->StartVbyte;
     191        return heap->StartVbyte;
    192192}
    193193
    194194// Returns the size of the string in bytes
    195195size_t size(const string_res & s) with(s) {
    196     return Handle.lnth;
     196        return Handle.lnth;
    197197}
    198198
     
    201201        // CFA string is NOT null terminated, so print exactly lnth characters in a minimum width of 0.
    202202        out | wd( 0, s.Handle.lnth, s.Handle.s ) | nonl;
    203     return out;
     203        return out;
    204204}
    205205
     
    210210// Input operator
    211211ifstream & ?|?(ifstream & in, string_res & s) {
    212     // Reading into a temp before assigning to s is near zero overhead in typical cases because of sharing.
    213     // If s is a substring of something larger, simple assignment takes care of that case correctly.
    214     // But directly reading a variable amount of text into the middle of a larger context is not practical.
    215     string_res temp;
    216 
    217     // Read in chunks.  Often, one chunk is enough.  Keep the string that accumulates chunks last in the heap,
    218     // so available room is rest of heap.  When a chunk fills the heap, force growth then take the next chunk.
    219     for (bool cont = true; cont; ) {
    220         cont = false;
    221 
    222         // Append dummy content to temp, forcing expansion when applicable (occurs always on subsequent loops)
    223         // length 2 ensures room for at least one real char, plus scanf/pipe-cstr's null terminator
    224         temp += "--";
    225         assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );    // last in heap
    226 
    227         // reset, to overwrite the appended "--"
    228         temp.Handle.lnth -= 2;
    229         temp.Handle.ulink->EndVbyte -= 2;
    230 
    231         // rest of heap is available to read into
    232         int lenReadable = (char *)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte;
    233         assert (lenReadable >= 2);
    234 
    235         // get bytes
    236         try {
     212        // Reading into a temp before assigning to s is near zero overhead in typical cases because of sharing.
     213        // If s is a substring of something larger, simple assignment takes care of that case correctly.
     214        // But directly reading a variable amount of text into the middle of a larger context is not practical.
     215        string_res temp;
     216
     217        // Read in chunks.  Often, one chunk is enough.  Keep the string that accumulates chunks last in the heap,
     218        // so available room is rest of heap.  When a chunk fills the heap, force growth then take the next chunk.
     219        for (bool cont = true; cont; ) {
     220                cont = false;
     221
     222                // Append dummy content to temp, forcing expansion when applicable (occurs always on subsequent loops)
     223                // length 2 ensures room for at least one real char, plus scanf/pipe-cstr's null terminator
     224                temp += "--";
     225                assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );      // last in heap
     226
     227                // reset, to overwrite the appended "--"
     228                temp.Handle.lnth -= 2;
     229                temp.Handle.ulink->EndVbyte -= 2;
     230
     231                // rest of heap is available to read into
     232                int lenReadable = (char *)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte;
     233                assert (lenReadable >= 2);
     234
     235                // get bytes
     236                try {
    237237                        *(temp.Handle.ulink->EndVbyte) = '\0';   // pre-assign empty cstring
    238             in | wdi( lenReadable, temp.Handle.ulink->EndVbyte );
    239         } catch (cstring_length *) {
    240             cont = true;
    241         }
    242         int lenWasRead = strlen(temp.Handle.ulink->EndVbyte);
    243 
    244         // update metadata
    245         temp.Handle.lnth += lenWasRead;
    246         temp.Handle.ulink->EndVbyte += lenWasRead;
    247     }
     238                        in | wdi( lenReadable, temp.Handle.ulink->EndVbyte );
     239                } catch (cstring_length *) {
     240                        cont = true;
     241                }
     242                int lenWasRead = strlen(temp.Handle.ulink->EndVbyte);
     243
     244                // update metadata
     245                temp.Handle.lnth += lenWasRead;
     246                temp.Handle.ulink->EndVbyte += lenWasRead;
     247        }
    248248
    249249        if ( temp.Handle.lnth > 0 ) s = temp;
    250     return in;
    251 }
    252 
    253 void ?|?( ifstream & in, string_res & s ) {
    254     (ifstream &)(in | s);
     250        return in;
     251}
     252
     253ifstream & ?|?( ifstream & is, _Istream_Rquoted f ) with( f.rstr ) {
     254        if ( eof( is ) ) throwResume ExceptionInst( missing_data );
     255        int args;
     256  fini: {
     257                char rfmt[5] = { ' ', delimiters[0], '%', 'n', '\0' };
     258                int len = -1;                                                                   // may not be set in fmt
     259                args = fmt( is, rfmt, &len );                                   // remove leading whitespace and quote
     260          if ( eof( is ) || len == -1 ) break fini;
     261
     262                // Change the remainder of the read into a getline by reseting the closing delimiter.
     263                if ( delimiters[1] != '\0' ) {
     264                        delimiters[0] = delimiters[1];
     265                        delimiters[1] = '\0';
     266                } // if
     267                flags.delimiter = true;
     268                return is | *(_Istream_Rstr *)&f;
     269        } // fini
     270        // read failed => no pattern match => set string to null
     271        if ( ! flags.ignore && s != 0p && args == 0 ) s[0] = '\0';
     272        if ( args == 1 && eof( is ) ) {                                         // data but scan ended at EOF
     273                clear( is );                                                                    // => reset EOF => detect again on next read
     274        } // if
     275        return is;
    255276}
    256277
     
    295316} // ?|?
    296317
    297 void ?|?( ifstream & in, _Istream_Rstr f ) {
    298     (ifstream &)(in | f);
    299 }
    300 
    301 
    302318// Empty constructor
    303319void ?{}(string_res & s) with(s) {
    304     if( ambient_string_sharectx->activeHeap ) {
    305         (Handle){ * ambient_string_sharectx->activeHeap };
    306         (shareEditSet_owns_ulink){ false };
    307         verify( Handle.s == 0p && Handle.lnth == 0 );
    308     } else {
    309         (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
    310         (shareEditSet_owns_ulink){ true };
    311         Handle.s = Handle.ulink->StartVbyte;
    312         verify( Handle.lnth == 0 );
    313     }
    314     s.shareEditSet_prev = &s;
    315     s.shareEditSet_next = &s;
    316 }
     320        if( ambient_string_sharectx->activeHeap ) {
     321                (Handle){ * ambient_string_sharectx->activeHeap };
     322                (shareEditSet_owns_ulink){ false };
     323                verify( Handle.s == 0p && Handle.lnth == 0 );
     324        } else {
     325                (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
     326                (shareEditSet_owns_ulink){ true };
     327                Handle.s = Handle.ulink->StartVbyte;
     328                verify( Handle.lnth == 0 );
     329        }
     330        s.shareEditSet_prev = &s;
     331        s.shareEditSet_next = &s;
     332                }
    317333
    318334static void eagerCopyCtorHelper(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    319     if( ambient_string_sharectx->activeHeap ) {
    320         (Handle){ * ambient_string_sharectx->activeHeap };
    321         (shareEditSet_owns_ulink){ false };
    322     } else {
    323         (Handle){ * new( rhslnth ) };
    324         (shareEditSet_owns_ulink){ true };
    325     }
    326     Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    327     Handle.lnth = rhslnth;
    328     memmove( Handle.s, rhs, rhslnth );
    329     s.shareEditSet_prev = &s;
    330     s.shareEditSet_next = &s;
     335        if( ambient_string_sharectx->activeHeap ) {
     336                (Handle){ * ambient_string_sharectx->activeHeap };
     337                (shareEditSet_owns_ulink){ false };
     338        } else {
     339                (Handle){ * new( rhslnth ) };
     340                (shareEditSet_owns_ulink){ true };
     341        }
     342        Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     343        Handle.lnth = rhslnth;
     344        memmove( Handle.s, rhs, rhslnth );
     345        s.shareEditSet_prev = &s;
     346        s.shareEditSet_next = &s;
    331347}
    332348
    333349// Constructor from a raw buffer and size
    334350void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    335     eagerCopyCtorHelper(s, rhs, rhslnth);
     351        eagerCopyCtorHelper(s, rhs, rhslnth);
    336352}
    337353
    338354void ?{}( string_res & s, ssize_t rhs ) {
    339     char buf[64];
    340     int len;
    341     snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
    342     ( s ){ buf, len };
     355        char buf[64];
     356        int len;
     357        snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
     358        ( s ){ buf, len };
    343359}
    344360void ?{}( string_res & s, size_t rhs ) {
    345     char buf[64];
    346     int len;
    347     snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
    348     ( s ){ buf, len };
     361        char buf[64];
     362        int len;
     363        snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
     364        ( s ){ buf, len };
    349365}
    350366void ?{}( string_res & s, double rhs ) {
    351     char buf[64];
    352     int len;
    353     snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
    354     ( s ){ buf, len };
     367        char buf[64];
     368        int len;
     369        snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
     370        ( s ){ buf, len };
    355371}
    356372void ?{}( string_res & s, long double rhs ) {
    357     char buf[64];
    358     int len;
    359     snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
    360     ( s ){ buf, len };
     373        char buf[64];
     374        int len;
     375        snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
     376        ( s ){ buf, len };
    361377}
    362378void ?{}( string_res & s, double _Complex rhs ) {
    363     char buf[64];
    364     int len;
    365     snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );
    366     ( s ){ buf, len };
     379        char buf[64];
     380        int len;
     381        snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );
     382        ( s ){ buf, len };
    367383}
    368384void ?{}( string_res & s, long double _Complex rhs ) {
    369     char buf[64];
    370     int len;
    371     snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );
    372     ( s ){ buf, len };
     385        char buf[64];
     386        int len;
     387        snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );
     388        ( s ){ buf, len };
    373389}
    374390
    375391// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
    376392void ?{}( string_res & s, VbyteHeap & heap, const char * rhs, size_t rhslnth ) with(s) {
    377     (Handle){ heap };
    378     Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    379     Handle.lnth = rhslnth;
    380     (s.shareEditSet_owns_ulink){ false };
    381     memmove( Handle.s, rhs, rhslnth );
    382     s.shareEditSet_prev = &s;
    383     s.shareEditSet_next = &s;
     393        (Handle){ heap };
     394        Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     395        Handle.lnth = rhslnth;
     396        (s.shareEditSet_owns_ulink){ false };
     397        memmove( Handle.s, rhs, rhslnth );
     398        s.shareEditSet_prev = &s;
     399        s.shareEditSet_next = &s;
    384400}
    385401
     
    387403// General copy constructor
    388404void ?{}(string_res & s, const string_res & s2, StrResInitMode mode, size_t start, size_t len ) {
    389 
    390     size_t end = start + len;
    391     verify( start <= end && end <= s2.Handle.lnth );
    392 
    393     if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
    394         // crossing heaps (including private): copy eagerly
    395         eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
    396         verify(s.shareEditSet_prev == &s);
    397         verify(s.shareEditSet_next == &s);
    398     } else {
    399         (s.Handle){};
    400         s.Handle.s = s2.Handle.s + start;
    401         s.Handle.lnth = end - start;
    402         s.Handle.ulink = s2.Handle.ulink;
    403 
    404         AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
    405         // ^ bug?  skip others at early point in string
    406 
    407         if (mode == COPY_VALUE) {
    408             verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
    409             // requested logical copy in same heap: defer copy until write
    410 
    411             (s.shareEditSet_owns_ulink){ false };
    412 
    413             // make s alone in its shareEditSet
    414             s.shareEditSet_prev = &s;
    415             s.shareEditSet_next = &s;
    416         } else {
    417             verify( mode == SHARE_EDITS );
    418             // sharing edits with source forces same heap as source (ignore context)
    419 
    420             (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
    421 
    422             // s2 is logically const but not implementation const
    423             string_res & s2mod = (string_res &) s2;
    424 
    425             // insert s after s2 on shareEditSet
    426             s.shareEditSet_next = s2mod.shareEditSet_next;
    427             s.shareEditSet_prev = &s2mod;
    428             s.shareEditSet_next->shareEditSet_prev = &s;
    429             s.shareEditSet_prev->shareEditSet_next = &s;
    430         }
    431     }
     405        size_t end = start + len;
     406        verify( start <= end && end <= s2.Handle.lnth );
     407
     408        if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
     409                // crossing heaps (including private): copy eagerly
     410                eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
     411                verify(s.shareEditSet_prev == &s);
     412                verify(s.shareEditSet_next == &s);
     413        } else {
     414                (s.Handle){};
     415                s.Handle.s = s2.Handle.s + start;
     416                s.Handle.lnth = end - start;
     417                s.Handle.ulink = s2.Handle.ulink;
     418
     419                AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
     420                // ^ bug?  skip others at early point in string
     421
     422                if (mode == COPY_VALUE) {
     423                        verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
     424                        // requested logical copy in same heap: defer copy until write
     425
     426                        (s.shareEditSet_owns_ulink){ false };
     427
     428                        // make s alone in its shareEditSet
     429                        s.shareEditSet_prev = &s;
     430                        s.shareEditSet_next = &s;
     431                } else {
     432                        verify( mode == SHARE_EDITS );
     433                        // sharing edits with source forces same heap as source (ignore context)
     434
     435                        (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
     436
     437                        // s2 is logically const but not implementation const
     438                        string_res & s2mod = (string_res &) s2;
     439
     440                        // insert s after s2 on shareEditSet
     441                        s.shareEditSet_next = s2mod.shareEditSet_next;
     442                        s.shareEditSet_prev = &s2mod;
     443                        s.shareEditSet_next->shareEditSet_prev = &s;
     444                        s.shareEditSet_prev->shareEditSet_next = &s;
     445                }
     446        }
    432447}
    433448
    434449static void assignEditSet(string_res & s, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
    435     char * resultSesStart,
    436     size_t resultSesLnth,
    437     HandleNode * resultPadPosition, size_t bsize ) {
    438 
    439     char * beforeBegin = shareEditSetStartPeer->Handle.s;
    440     size_t beforeLen = s.Handle.s - beforeBegin;
    441 
    442     char * afterBegin = s.Handle.s + s.Handle.lnth;
    443     size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
    444 
    445     size_t oldLnth = s.Handle.lnth;
    446 
    447     s.Handle.s = resultSesStart + beforeLen;
    448     s.Handle.lnth = bsize;
    449     if (resultPadPosition)
    450         MoveThisAfter( s.Handle, *resultPadPosition );
    451 
    452     // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
    453     char *limit = resultSesStart + resultSesLnth;
    454     for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) {
    455         verify (p->Handle.s >= beforeBegin);
    456         if ( p->Handle.s >= afterBegin ) {
    457             verify ( p->Handle.s <= afterBegin + afterLen );
    458             verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    459             // p starts after the edit
    460             // take start and end as end-anchored
    461             size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
    462             p->Handle.s = limit - startOffsetFromEnd;
    463             // p->Handle.lnth unaffected
    464         } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
    465             // p starts before, or at the start of, the edit
    466             if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
    467                 // p ends before the edit
    468                 // take end as start-anchored too
    469                 // p->Handle.lnth unaffected
    470             } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    471                 // p ends during the edit; p does not include the last character replaced
    472                 // clip end of p to end at start of edit
    473                 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
    474             } else {
    475                 // p ends after the edit
    476                 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    477                 // take end as end-anchored
    478                 // stretch-shrink p according to the edit
    479                 p->Handle.lnth += s.Handle.lnth;
    480                 p->Handle.lnth -= oldLnth;
    481             }
    482             // take start as start-anchored
    483             size_t startOffsetFromStart = p->Handle.s - beforeBegin;
    484             p->Handle.s = resultSesStart + startOffsetFromStart;
    485         } else {
    486             verify ( p->Handle.s < afterBegin );
    487             // p starts during the edit
    488             verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
    489             if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    490                 // p ends during the edit; p does not include the last character replaced
    491                 // set p to empty string at start of edit
    492                 p->Handle.s = s.Handle.s;
    493                 p->Handle.lnth = 0;
    494             } else {
    495                 // p includes the end of the edit
    496                 // clip start of p to start at end of edit
    497                 int charsToClip = afterBegin - p->Handle.s;
    498                 p->Handle.s = s.Handle.s + s.Handle.lnth;
    499                 p->Handle.lnth -= charsToClip;
    500             }
    501         }
    502         if (resultPadPosition)
    503             MoveThisAfter( p->Handle, *resultPadPosition );     // move substring handle to maintain sorted order by string position
    504     }
     450                                                  char * resultSesStart,
     451                                                  size_t resultSesLnth,
     452                                                  HandleNode * resultPadPosition, size_t bsize ) {
     453
     454        char * beforeBegin = shareEditSetStartPeer->Handle.s;
     455        size_t beforeLen = s.Handle.s - beforeBegin;
     456
     457        char * afterBegin = s.Handle.s + s.Handle.lnth;
     458        size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
     459
     460        size_t oldLnth = s.Handle.lnth;
     461
     462        s.Handle.s = resultSesStart + beforeLen;
     463        s.Handle.lnth = bsize;
     464        if (resultPadPosition)
     465                MoveThisAfter( s.Handle, *resultPadPosition );
     466
     467        // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
     468        char *limit = resultSesStart + resultSesLnth;
     469        for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) {
     470                verify (p->Handle.s >= beforeBegin);
     471                if ( p->Handle.s >= afterBegin ) {
     472                        verify ( p->Handle.s <= afterBegin + afterLen );
     473                        verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     474                        // p starts after the edit
     475                        // take start and end as end-anchored
     476                        size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
     477                        p->Handle.s = limit - startOffsetFromEnd;
     478                        // p->Handle.lnth unaffected
     479                } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
     480                        // p starts before, or at the start of, the edit
     481                        if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
     482                                // p ends before the edit
     483                                // take end as start-anchored too
     484                                // p->Handle.lnth unaffected
     485                        } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     486                                // p ends during the edit; p does not include the last character replaced
     487                                // clip end of p to end at start of edit
     488                                p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
     489                        } else {
     490                                // p ends after the edit
     491                                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     492                                // take end as end-anchored
     493                                // stretch-shrink p according to the edit
     494                                p->Handle.lnth += s.Handle.lnth;
     495                                p->Handle.lnth -= oldLnth;
     496                        }
     497                        // take start as start-anchored
     498                        size_t startOffsetFromStart = p->Handle.s - beforeBegin;
     499                        p->Handle.s = resultSesStart + startOffsetFromStart;
     500                } else {
     501                        verify ( p->Handle.s < afterBegin );
     502                        // p starts during the edit
     503                        verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     504                        if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     505                                // p ends during the edit; p does not include the last character replaced
     506                                // set p to empty string at start of edit
     507                                p->Handle.s = s.Handle.s;
     508                                p->Handle.lnth = 0;
     509                        } else {
     510                                // p includes the end of the edit
     511                                // clip start of p to start at end of edit
     512                                int charsToClip = afterBegin - p->Handle.s;
     513                                p->Handle.s = s.Handle.s + s.Handle.lnth;
     514                                p->Handle.lnth -= charsToClip;
     515                        }
     516                }
     517                if (resultPadPosition)
     518                        MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position
     519        }
    505520}
    506521
    507522// traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs
    508523static void locateInShareEditSet( string_res & s, string_res *& shareEditSetStartPeer, string_res *& shareEditSetEndPeer ) {
    509     shareEditSetStartPeer = & s;
    510     shareEditSetEndPeer = & s;
    511     for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {
    512         if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
    513             shareEditSetStartPeer = editPeer;
    514         }
    515         if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
    516             shareEditSetEndPeer = editPeer;
    517         }
    518     }
     524        shareEditSetStartPeer = & s;
     525        shareEditSetEndPeer = & s;
     526        for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {
     527                if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     528                        shareEditSetStartPeer = editPeer;
     529                }
     530                if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     531                        shareEditSetEndPeer = editPeer;
     532                }
     533        }
    519534}
    520535
    521536static string_res & assign_(string_res & s, const char * buffer, size_t bsize, const string_res & valSrc) {
    522 
    523     string_res * shareEditSetStartPeer;
    524     string_res * shareEditSetEndPeer;
    525     locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer );
    526 
    527     verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
    528     size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
    529     verify( origEditSetLength >= s.Handle.lnth );
    530 
    531     if ( s.shareEditSet_owns_ulink ) {                 // assigning to private context
    532         // ok to overwrite old value within LHS
    533         char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
    534         int prefixLen = s.Handle.s - prefixStartOrig;
    535         char * suffixStartOrig = s.Handle.s + s.Handle.lnth;
    536         int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
    537 
    538         int delta = bsize - s.Handle.lnth;
    539         if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) {
    540             // growing: copy from old to new
    541             char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta );
    542             char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
    543             destCursor += prefixLen;  memcpy(destCursor, buffer         , bsize    );
    544             destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
    545             assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    546                 dest,
    547                 origEditSetLength + delta,
    548                 0p, bsize);
    549             free( oldBytes );
    550         } else {
    551             // room is already allocated in-place: bubble suffix and overwite middle
    552             memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
    553             memcpy( s.Handle.s, buffer, bsize );
    554 
    555             assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    556                 shareEditSetStartPeer->Handle.s,
    557                 origEditSetLength + delta,
    558                 0p, bsize);
    559         }
    560 
    561     } else if (                                           // assigning to shared context
    562         s.Handle.lnth == origEditSetLength &&          // overwriting entire run of SES
    563         & valSrc &&                                       // sourcing from a managed string
    564         valSrc.Handle.ulink == s.Handle.ulink  ) {     // sourcing from same heap
    565 
    566         // SES's result will only use characters from the source string => reuse source
    567         assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    568             valSrc.Handle.s,
    569             valSrc.Handle.lnth,
    570             &((string_res&)valSrc).Handle, bsize);
    571        
    572     } else {
    573         // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
    574         // OR we are importing characters: need to copy eagerly (can't refer to source)
    575 
    576         // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
    577         // `s` occurs in the middle of it, to be replaced
    578         // build up the new text in `pasting`
    579 
    580         string_res pasting = {
    581             * s.Handle.ulink,                               // maintain same heap, regardless of context
    582             shareEditSetStartPeer->Handle.s,                   // start of SES
    583             s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s
    584         append( pasting,
    585             buffer,                                            // start of replacement for s
    586             bsize );                                           // length of replacement for s
    587         append( pasting,
    588             s.Handle.s + s.Handle.lnth,                  // start of SES after s
    589             shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
    590             (s.Handle.s + s.Handle.lnth) );              // length of SES, after s
    591 
    592         // The above string building can trigger compaction.
    593         // The reference points (that are arguments of the string building) may move during that building.
    594         // From s point on, they are stable.
    595 
    596         assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    597             pasting.Handle.s,
    598             pasting.Handle.lnth,
    599             &pasting.Handle, bsize);
    600     }
    601 
    602     return s;
     537        string_res * shareEditSetStartPeer;
     538        string_res * shareEditSetEndPeer;
     539        locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer );
     540
     541        verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
     542        size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
     543        verify( origEditSetLength >= s.Handle.lnth );
     544
     545        if ( s.shareEditSet_owns_ulink ) {                               // assigning to private context
     546                // ok to overwrite old value within LHS
     547                char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
     548                int prefixLen = s.Handle.s - prefixStartOrig;
     549                char * suffixStartOrig = s.Handle.s + s.Handle.lnth;
     550                int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
     551
     552                int delta = bsize - s.Handle.lnth;
     553                if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) {
     554                        // growing: copy from old to new
     555                        char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta );
     556                        char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
     557                        destCursor += prefixLen;  memcpy(destCursor, buffer              , bsize        );
     558                        destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
     559                        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     560                                                  dest,
     561                                                  origEditSetLength + delta,
     562                                                  0p, bsize);
     563                        free( oldBytes );
     564                } else {
     565                        // room is already allocated in-place: bubble suffix and overwite middle
     566                        memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
     567                        memcpy( s.Handle.s, buffer, bsize );
     568
     569                        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     570                                                  shareEditSetStartPeer->Handle.s,
     571                                                  origEditSetLength + delta,
     572                                                  0p, bsize);
     573                }
     574
     575        } else if (                                                                                // assigning to shared context
     576                s.Handle.lnth == origEditSetLength &&             // overwriting entire run of SES
     577                & valSrc &&                                                                        // sourcing from a managed string
     578                valSrc.Handle.ulink == s.Handle.ulink  ) {       // sourcing from same heap
     579
     580                // SES's result will only use characters from the source string => reuse source
     581                assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     582                                          valSrc.Handle.s,
     583                                          valSrc.Handle.lnth,
     584                                          &((string_res&)valSrc).Handle, bsize);
     585               
     586        } else {
     587                // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
     588                // OR we are importing characters: need to copy eagerly (can't refer to source)
     589
     590                // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     591                // `s` occurs in the middle of it, to be replaced
     592                // build up the new text in `pasting`
     593
     594                string_res pasting = {
     595                        * s.Handle.ulink,                                                          // maintain same heap, regardless of context
     596                        shareEditSetStartPeer->Handle.s,                                   // start of SES
     597                        s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s
     598                append( pasting,
     599                                buffer,                                                                                 // start of replacement for s
     600                                bsize );                                                                                   // length of replacement for s
     601                append( pasting,
     602                                s.Handle.s + s.Handle.lnth,                               // start of SES after s
     603                                shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     604                                (s.Handle.s + s.Handle.lnth) );                   // length of SES, after s
     605
     606                // The above string building can trigger compaction.
     607                // The reference points (that are arguments of the string building) may move during that building.
     608                // From s point on, they are stable.
     609
     610                assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     611                                          pasting.Handle.s,
     612                                          pasting.Handle.lnth,
     613                                          &pasting.Handle, bsize);
     614        }
     615
     616        return s;
    603617}
    604618
    605619string_res & assign(string_res & s, const string_res & src, size_t maxlen) {
    606     return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
     620        return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
    607621}
    608622
    609623string_res & assign(string_res & s, const char * buffer, size_t bsize) {
    610     return assign_(s, buffer, bsize, *0p);
     624        return assign_(s, buffer, bsize, *0p);
    611625}
    612626
    613627string_res & ?=?(string_res & s, char c) {
    614     return assign(s, &c, 1);
     628        return assign(s, &c, 1);
    615629}
    616630
    617631string_res & ?=?( string_res & s, ssize_t rhs ) {
    618     string_res rhs2 = rhs;
    619     s = rhs2;
    620     return s;
     632        string_res rhs2 = rhs;
     633        s = rhs2;
     634        return s;
    621635}
    622636string_res & ?=?( string_res & s, size_t rhs ) {
    623     string_res rhs2 = rhs;
    624     s = rhs2;
    625     return s;
     637        string_res rhs2 = rhs;
     638        s = rhs2;
     639        return s;
    626640}
    627641string_res & ?=?( string_res & s, double rhs ) {
    628     string_res rhs2 = rhs;
    629     s = rhs2;
    630     return s;
     642        string_res rhs2 = rhs;
     643        s = rhs2;
     644        return s;
    631645}
    632646string_res & ?=?( string_res & s, long double rhs ) {
    633     string_res rhs2 = rhs;
    634     s = rhs2;
    635     return s;
     647        string_res rhs2 = rhs;
     648        s = rhs2;
     649        return s;
    636650}
    637651string_res & ?=?( string_res & s, double _Complex rhs ) {
    638     string_res rhs2 = rhs;
    639     s = rhs2;
    640     return s;
     652        string_res rhs2 = rhs;
     653        s = rhs2;
     654        return s;
    641655}
    642656string_res & ?=?( string_res & s, long double _Complex rhs ) {
    643     string_res rhs2 = rhs;
    644     s = rhs2;
    645     return s;
     657        string_res rhs2 = rhs;
     658        s = rhs2;
     659        return s;
    646660}
    647661
    648662// Copy assignment operator
    649663string_res & ?=?(string_res & s, const string_res & rhs) with( s ) {
    650     return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
     664        return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
    651665}
    652666
    653667string_res & ?=?(string_res & s, string_res & rhs) with( s ) {
    654     const string_res & rhs2 = rhs;
    655     return s = rhs2;
     668        const string_res & rhs2 = rhs;
     669        return s = rhs2;
    656670}
    657671
     
    659673// Destructor
    660674void ^?{}(string_res & s) with(s) {
    661     // much delegated to implied ^VbyteSM
    662 
    663     // sever s from its share-edit peers, if any (four no-ops when already solo)
    664     s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
    665     s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
    666     // s.shareEditSet_next = &s;
    667     // s.shareEditSet_prev = &s;
    668 
    669     if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
    670         delete( s.Handle.ulink );
    671     }
     675        // much delegated to implied ^VbyteSM
     676
     677        // sever s from its share-edit peers, if any (four no-ops when already solo)
     678        s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
     679        s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
     680        // s.shareEditSet_next = &s;
     681        // s.shareEditSet_prev = &s;
     682
     683        if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
     684                delete( s.Handle.ulink );
     685        }
    672686}
    673687
     
    677691// offset from the start of the string.
    678692char ?[?](const string_res & s, size_t index) with(s) {
    679     //TODO: Check if index is valid (no exceptions yet)
    680     return Handle.s[index];
     693        //TODO: Check if index is valid (no exceptions yet)
     694        return Handle.s[index];
    681695}
    682696
    683697void assignAt(const string_res & s, size_t index, char val) {
    684     // caution: not tested (not reachable by string-api-coverage interface)
    685     // equivalent form at string level is `s[index] = val`,
    686     // which uses the overload that returns a length-1 string
    687     string_res editZone = { s, SHARE_EDITS, index, 1 };
    688     assign(editZone, &val, 1);
     698        // caution: not tested (not reachable by string-api-coverage interface)
     699        // equivalent form at string level is `s[index] = val`,
     700        // which uses the overload that returns a length-1 string
     701        string_res editZone = { s, SHARE_EDITS, index, 1 };
     702        assign(editZone, &val, 1);
    689703}
    690704
     
    694708
    695709void append(string_res & str1, const char * buffer, size_t bsize) {
    696     size_t clnth = str1.Handle.lnth + bsize;
    697     if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
    698         // no-op
    699     } else {                                            // must copy some text
    700         if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
    701             VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
    702         } else {                                        // copy the two parts
    703             char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
    704             char * str1oldBuf = str1.Handle.s;  // must read after VbyteAlloc call in case it gs's
    705             str1.Handle.s = str1newBuf;
    706             memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
    707         } // if
    708         memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
    709     } // if
    710     str1.Handle.lnth = clnth;
     710        size_t clnth = str1.Handle.lnth + bsize;
     711        if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
     712                // no-op
     713        } else {                                                                                        // must copy some text
     714                if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
     715                        VbyteAlloc( *str1.Handle.ulink, bsize );        // create room for 2nd part at the end of string area
     716                } else {                                                                                // copy the two parts
     717                        char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
     718                        char * str1oldBuf = str1.Handle.s;                      // must read after VbyteAlloc call in case it gs's
     719                        str1.Handle.s = str1newBuf;
     720                        memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
     721                } // if
     722                memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
     723        } // if
     724        str1.Handle.lnth = clnth;
    711725}
    712726
    713727void ?+=?(string_res & str1, const string_res & str2) {
    714     append( str1, str2.Handle.s, str2.Handle.lnth );
     728        append( str1, str2.Handle.s, str2.Handle.lnth );
    715729}
    716730
    717731void append(string_res & str1, const string_res & str2, size_t maxlen) {
    718     append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
     732        append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
    719733}
    720734
    721735void ?+=?(string_res & s, char c) {
    722     append( s, & c, 1 );
     736        append( s, & c, 1 );
    723737}
    724738void ?+=?(string_res & s, const char * c) {
    725     append( s, c, strlen(c) );
     739        append( s, c, strlen(c) );
    726740}
    727741
     
    730744
    731745void ?*=?(string_res & s, size_t factor) {
    732     string_res s2 = { s, COPY_VALUE };
    733     s = "";
    734     for (factor) s += s2;
     746        string_res s2 = { s, COPY_VALUE };
     747        s = "";
     748        for (factor) s += s2;
    735749}
    736750
     
    739753
    740754int strcmp(const string_res & s1, const string_res & s2) {
    741     // return 0;
    742     int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));
    743     if (ans1 != 0) return ans1;
    744     return s1.Handle.lnth - s2.Handle.lnth;
     755        // return 0;
     756        int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));
     757        if (ans1 != 0) return ans1;
     758        return s1.Handle.lnth - s2.Handle.lnth;
    745759}
    746760
     
    753767
    754768int strcmp (const string_res & s1, const char * s2) {
    755     string_res s2x = s2;
    756     return strcmp(s1, s2x);
     769        string_res s2x = s2;
     770        return strcmp(s1, s2x);
    757771}
    758772
     
    765779
    766780int strcmp (const char * s1, const string_res & s2) {
    767     string_res s1x = s1;
    768     return strcmp(s1x, s2);
     781        string_res s1x = s1;
     782        return strcmp(s1x, s2);
    769783}
    770784
     
    777791
    778792
    779 
    780793//////////////////////////////////////////////////////////
    781794// Search
    782795
    783796bool contains(const string_res & s, char ch) {
    784     for ( i; size(s) ) {
    785         if (s[i] == ch) return true;
    786     }
    787     return false;
     797        for ( i; size(s) ) {
     798                if (s[i] == ch) return true;
     799        }
     800        return false;
    788801}
    789802
    790803int find(const string_res & s, char search) {
    791     return findFrom(s, 0, search);
     804        return findFrom(s, 0, search);
    792805}
    793806
    794807int findFrom(const string_res & s, size_t fromPos, char search) {
    795     // FIXME: This paricular overload (find of single char) is optimized to use memchr.
    796     // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
    797     char * searchFrom = s.Handle.s + fromPos;
    798     size_t searchLnth = s.Handle.lnth - fromPos;
    799     int searchVal = search;
    800     char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
    801     if (foundAt == 0p) return s.Handle.lnth;
    802     else return foundAt - s.Handle.s;
     808        // FIXME: This paricular overload (find of single char) is optimized to use memchr.
     809        // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
     810        char * searchFrom = s.Handle.s + fromPos;
     811        size_t searchLnth = s.Handle.lnth - fromPos;
     812        int searchVal = search;
     813        char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
     814        if (foundAt == 0p) return s.Handle.lnth;
     815        else return foundAt - s.Handle.s;
    803816}
    804817
    805818int find(const string_res & s, const string_res & search) {
    806     return findFrom(s, 0, search);
     819        return findFrom(s, 0, search);
    807820}
    808821
    809822int findFrom(const string_res & s, size_t fromPos, const string_res & search) {
    810     return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     823        return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
    811824}
    812825
    813826int find(const string_res & s, const char * search) {
    814     return findFrom(s, 0, search);
     827        return findFrom(s, 0, search);
    815828}
    816829int findFrom(const string_res & s, size_t fromPos, const char * search) {
    817     return findFrom(s, fromPos, search, strlen(search));
     830        return findFrom(s, fromPos, search, strlen(search));
    818831}
    819832
    820833int find(const string_res & s, const char * search, size_t searchsize) {
    821     return findFrom(s, 0, search, searchsize);
     834        return findFrom(s, 0, search, searchsize);
    822835}
    823836
    824837int findFrom(const string_res & s, size_t fromPos, const char * search, size_t searchsize) {
    825 
    826     /* Remaining implementations essentially ported from Sunjay's work */
    827 
    828 
    829     // FIXME: This is a naive algorithm. We probably want to switch to someting
    830     // like Boyer-Moore in the future.
    831     // https://en.wikipedia.org/wiki/String_searching_algorithm
    832 
    833     // Always find the empty string
    834     if (searchsize == 0) {
    835         return 0;
    836     }
    837 
    838     for ( i; fromPos ~ s.Handle.lnth ) {
    839         size_t remaining = s.Handle.lnth - i;
    840         // Never going to find the search string if the remaining string is
    841         // smaller than search
    842         if (remaining < searchsize) {
    843             break;
    844         }
    845 
    846         bool matched = true;
    847         for ( j; searchsize ) {
    848             if (search[j] != s.Handle.s[i + j]) {
    849                 matched = false;
    850                 break;
    851             }
    852         }
    853         if (matched) {
    854             return i;
    855         }
    856     }
    857 
    858     return s.Handle.lnth;
     838        /* Remaining implementations essentially ported from Sunjay's work */
     839
     840        // FIXME: This is a naive algorithm. We probably want to switch to someting
     841        // like Boyer-Moore in the future.
     842        // https://en.wikipedia.org/wiki/String_searching_algorithm
     843
     844        // Always find the empty string
     845        if (searchsize == 0) {
     846                return 0;
     847        }
     848
     849        for ( i; fromPos ~ s.Handle.lnth ) {
     850                size_t remaining = s.Handle.lnth - i;
     851                // Never going to find the search string if the remaining string is
     852                // smaller than search
     853                if (remaining < searchsize) {
     854                        break;
     855                }
     856
     857                bool matched = true;
     858                for ( j; searchsize ) {
     859                        if (search[j] != s.Handle.s[i + j]) {
     860                                matched = false;
     861                                break;
     862                        }
     863                }
     864                if (matched) {
     865                        return i;
     866                }
     867        }
     868        return s.Handle.lnth;
    859869}
    860870
    861871bool includes(const string_res & s, const string_res & search) {
    862     return includes(s, search.Handle.s, search.Handle.lnth);
     872        return includes(s, search.Handle.s, search.Handle.lnth);
    863873}
    864874
    865875bool includes(const string_res & s, const char * search) {
    866     return includes(s, search, strlen(search));
     876        return includes(s, search, strlen(search));
    867877}
    868878
    869879bool includes(const string_res & s, const char * search, size_t searchsize) {
    870     return find(s, search, searchsize) < s.Handle.lnth;
     880        return find(s, search, searchsize) < s.Handle.lnth;
    871881}
    872882
    873883bool startsWith(const string_res & s, const string_res & prefix) {
    874     return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
     884        return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
    875885}
    876886
    877887bool startsWith(const string_res & s, const char * prefix) {
    878     return startsWith(s, prefix, strlen(prefix));
     888        return startsWith(s, prefix, strlen(prefix));
    879889}
    880890
    881891bool startsWith(const string_res & s, const char * prefix, size_t prefixsize) {
    882     if (s.Handle.lnth < prefixsize) {
    883         return false;
    884     }
    885     return memcmp(s.Handle.s, prefix, prefixsize) == 0;
     892        if (s.Handle.lnth < prefixsize) {
     893                return false;
     894        }
     895        return memcmp(s.Handle.s, prefix, prefixsize) == 0;
    886896}
    887897
    888898bool endsWith(const string_res & s, const string_res & suffix) {
    889     return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
     899        return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
    890900}
    891901
    892902bool endsWith(const string_res & s, const char * suffix) {
    893     return endsWith(s, suffix, strlen(suffix));
     903        return endsWith(s, suffix, strlen(suffix));
    894904}
    895905
    896906bool endsWith(const string_res & s, const char * suffix, size_t suffixsize) {
    897     if (s.Handle.lnth < suffixsize) {
    898         return false;
    899     }
    900     // Amount to offset the bytes pointer so that we are comparing the end of s
    901     // to suffix. s.bytes + offset should be the first byte to compare against suffix
    902     size_t offset = s.Handle.lnth - suffixsize;
    903     return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
    904 }
    905 
    906     /* Back to Mike's work */
    907 
     907        if (s.Handle.lnth < suffixsize) {
     908                return false;
     909        }
     910        // Amount to offset the bytes pointer so that we are comparing the end of s
     911        // to suffix. s.bytes + offset should be the first byte to compare against suffix
     912        size_t offset = s.Handle.lnth - suffixsize;
     913        return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
     914}
     915
     916/* Back to Mike's work */
    908917
    909918///////////////////////////////////////////////////////////////////////////
     
    911920
    912921void ?{}( charclass_res & s, const string_res & chars) {
    913     (s){ chars.Handle.s, chars.Handle.lnth };
     922        (s){ chars.Handle.s, chars.Handle.lnth };
    914923}
    915924
    916925void ?{}( charclass_res & s, const char * chars ) {
    917     (s){ chars, strlen(chars) };
     926        (s){ chars, strlen(chars) };
    918927}
    919928
    920929void ?{}( charclass_res & s, const char * chars, size_t charssize ) {
    921     (s.chars){ chars, charssize };
    922     // now sort it ?
     930        (s.chars){ chars, charssize };
     931        // now sort it ?
    923932}
    924933
    925934void ^?{}( charclass_res & s ) {
    926     ^(s.chars){};
     935        ^(s.chars){};
    927936}
    928937
    929938static bool test( const charclass_res & mask, char c ) {
    930     // instead, use sorted char list?
    931     return contains( mask.chars, c );
     939        // instead, use sorted char list?
     940        return contains( mask.chars, c );
    932941}
    933942
    934943int exclude(const string_res & s, const charclass_res & mask) {
    935     for ( i; size(s) ) {
    936         if ( test(mask, s[i]) ) return i;
    937     }
    938     return size(s);
     944        for ( i; size(s) ) {
     945                if ( test(mask, s[i]) ) return i;
     946        }
     947        return size(s);
    939948}
    940949
    941950int include(const string_res & s, const charclass_res & mask) {
    942     for ( i; size(s) ) {
    943         if ( ! test(mask, s[i]) ) return i;
    944     }
    945     return size(s);
     951        for ( i; size(s) ) {
     952                if ( ! test(mask, s[i]) ) return i;
     953        }
     954        return size(s);
    946955}
    947956
     
    953962static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) {
    954963#ifdef VbyteDebug
    955     serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;
    956 #endif // VbyteDebug
    957     // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be).
    958     verify( n.ulink != 0p );
    959     verify( s.ulink == n.ulink );
    960     flink = n.flink;
    961     blink = &n;
    962     n.flink->blink = &s;
    963     n.flink = &s;
    964 #ifdef VbyteDebug
    965     {
     964        serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;
     965#endif // VbyteDebug
     966        // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be).
     967        verify( n.ulink != 0p );
     968        verify( s.ulink == n.ulink );
     969        flink = n.flink;
     970        blink = &n;
     971        n.flink->blink = &s;
     972        n.flink = &s;
     973#ifdef VbyteDebug
     974        {
    966975                serr | "HandleList:";
    967976                serr | nlOff;
     
    974983                } // for
    975984                serr | nlOn;
    976     }
    977     serr | "exit:AddThisAfter";
     985        }
     986        serr | "exit:AddThisAfter";
    978987#endif // VbyteDebug
    979988} // AddThisAfter
     
    984993static void DeleteNode( HandleNode & s ) with(s) {
    985994#ifdef VbyteDebug
    986     serr | "enter:DeleteNode, s:" | &s;
    987 #endif // VbyteDebug
    988     flink->blink = blink;
    989     blink->flink = flink;
    990 #ifdef VbyteDebug
    991     serr | "exit:DeleteNode";
     995        serr | "enter:DeleteNode, s:" | &s;
     996#endif // VbyteDebug
     997        flink->blink = blink;
     998        blink->flink = flink;
     999#ifdef VbyteDebug
     1000        serr | "exit:DeleteNode";
    9921001#endif // VbyteDebug
    9931002} //  DeleteNode
     
    9991008static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) {
    10001009#ifdef VbyteDebug
    1001     serr | "enter:VbyteAlloc, size:" | size;
    1002 #endif // VbyteDebug
    1003     uintptr_t NoBytes;
    1004     char *r;
    1005 
    1006     NoBytes = ( uintptr_t )EndVbyte + size;
    1007     if ( NoBytes > ( uintptr_t )ExtVbyte ) {                    // enough room for new byte-string ?
     1010        serr | "enter:VbyteAlloc, size:" | size;
     1011#endif // VbyteDebug
     1012        uintptr_t NoBytes;
     1013        char *r;
     1014
     1015        NoBytes = ( uintptr_t )EndVbyte + size;
     1016        if ( NoBytes > ( uintptr_t )ExtVbyte ) {                        // enough room for new byte-string ?
    10081017                garbage( s, size );                                                             // firer up the garbage collector
    10091018                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    1010     } // if
    1011     r = EndVbyte;
    1012     EndVbyte += size;
    1013 #ifdef VbyteDebug
    1014     serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
    1015 #endif // VbyteDebug
    1016     return r;
     1019        } // if
     1020        r = EndVbyte;
     1021        EndVbyte += size;
     1022#ifdef VbyteDebug
     1023        serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
     1024#endif // VbyteDebug
     1025        return r;
    10171026} // VbyteAlloc
    10181027
     
    10271036
    10281037static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) {
    1029     if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
    1030         // room available
    1031         EndVbyte += delta;
    1032         return 0p;
    1033     }
    1034 
    1035     char *oldBytes = StartVbyte;
    1036 
    1037     NoOfExtensions += 1;
    1038     CurrSize *= 2;
    1039     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    1040     ExtVbyte = StartVbyte + CurrSize;
    1041 
    1042     return oldBytes;
     1038        if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
     1039                // room available
     1040                EndVbyte += delta;
     1041                return 0p;
     1042        }
     1043
     1044        char *oldBytes = StartVbyte;
     1045
     1046        NoOfExtensions += 1;
     1047        CurrSize *= 2;
     1048        StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1049        ExtVbyte = StartVbyte + CurrSize;
     1050
     1051        return oldBytes;
    10431052}
    10441053
     
    10491058static void MoveThisAfter( HandleNode & s, const HandleNode  & h ) with(s) {
    10501059#ifdef VbyteDebug
    1051     serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;
    1052 #endif // VbyteDebug
    1053     verify( h.ulink != 0p );
    1054     verify( s.ulink == h.ulink );
    1055     if ( s < h.s ) {                                    // check argument values
     1060        serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;
     1061#endif // VbyteDebug
     1062        verify( h.ulink != 0p );
     1063        verify( s.ulink == h.ulink );
     1064        if ( s < h.s ) {                                        // check argument values
    10561065                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    1057                 //      | ( h->s ) | " and keep handles in ascending order";
     1066                //        | ( h->s ) | " and keep handles in ascending order";
    10581067                // exit(-1 );
    10591068                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    1060     } // if
    1061 
    1062     HandleNode *i;
    1063     for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
    1064     if ( & s != i->blink ) {
     1069        } // if
     1070
     1071        HandleNode *i;
     1072        for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
     1073        if ( & s != i->blink ) {
    10651074                DeleteNode( s );
    10661075                AddThisAfter( s, *i->blink );
    1067     } // if
    1068 #ifdef VbyteDebug
    1069     {
    1070         serr | "HandleList:";
    1071         serr | nlOff;
    1072         for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
    1073             serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
    1074             for ( i; n->lnth ) {
    1075                         serr | n->s[i];
    1076             } // for
    1077             serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
    1078         } // for
    1079         serr | nlOn;
    1080     }
    1081     serr | "exit:MoveThisAfter";
     1076        } // if
     1077#ifdef VbyteDebug
     1078        {
     1079                serr | "HandleList:";
     1080                serr | nlOff;
     1081                for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
     1082                        serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
     1083                        for ( i; n->lnth ) {
     1084                                serr | n->s[i];
     1085                        } // for
     1086                        serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
     1087                } // for
     1088                serr | nlOn;
     1089        }
     1090        serr | "exit:MoveThisAfter";
    10821091#endif // VbyteDebug
    10831092} // MoveThisAfter
    1084 
    1085 
    1086 
    10871093
    10881094
     
    10971103int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  {
    10981104#ifdef VbyteDebug
    1099     serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
    1100 #endif // VbyteDebug
    1101     int cmp;
    1102 
    1103     CharZip: for ( int i = 0; ; i += 1 ) {
    1104         if ( i == Src2Lnth - 1 ) {
    1105             for ( ; ; i += 1 ) {
     1105        serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     1106#endif // VbyteDebug
     1107        int cmp;
     1108
     1109  CharZip: for ( int i = 0; ; i += 1 ) {
     1110                if ( i == Src2Lnth - 1 ) {
     1111                        for ( ; ; i += 1 ) {
     1112                                if ( i == Src1Lnth - 1 ) {
     1113                                        cmp = 0;
     1114                                        break CharZip;
     1115                                } // exit
     1116                                if ( Src1[Src1Start + i] != ' ') {
     1117                                        // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
     1118                                        cmp = 1;
     1119                                        break CharZip;
     1120                                } // exit
     1121                        } // for
     1122                } // exit
    11061123                if ( i == Src1Lnth - 1 ) {
    1107                     cmp = 0;
    1108                     break CharZip;
     1124                        for ( ; ; i += 1 ) {
     1125                                if ( i == Src2Lnth - 1 ) {
     1126                                        cmp = 0;
     1127                                        break CharZip;
     1128                                } // exit
     1129                                if ( Src2[Src2Start + i] != ' ') {
     1130                                        cmp = -1;
     1131                                        break CharZip;
     1132                                } // exit
     1133                        } // for
    11091134                } // exit
    1110                 if ( Src1[Src1Start + i] != ' ') {
    1111                         // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
    1112                     cmp = 1;
    1113                     break CharZip;
     1135                if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
     1136                        cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
     1137                        break CharZip;
    11141138                } // exit
    1115             } // for
    1116         } // exit
    1117         if ( i == Src1Lnth - 1 ) {
    1118             for ( ; ; i += 1 ) {
    1119                 if ( i == Src2Lnth - 1 ) {
    1120                     cmp = 0;
    1121                     break CharZip;
    1122                 } // exit
    1123                 if ( Src2[Src2Start + i] != ' ') {
    1124                     cmp = -1;
    1125                     break CharZip;
    1126                 } // exit
    1127             } // for
    1128         } // exit
    1129       if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
    1130             cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
    1131             break CharZip;
    1132         } // exit
    1133     } // for
    1134 #ifdef VbyteDebug
    1135     serr | "exit:ByteCmp, cmp:" | cmp;
    1136 #endif // VbyteDebug
    1137     return cmp;
     1139        } // for
     1140#ifdef VbyteDebug
     1141        serr | "exit:ByteCmp, cmp:" | cmp;
     1142#endif // VbyteDebug
     1143        return cmp;
    11381144} // ByteCmp
    11391145
     
    11451151
    11461152void compaction(VbyteHeap & s) with(s) {
    1147     HandleNode *h;
    1148     char *obase, *nbase, *limit;
    1149    
    1150     NoOfCompactions += 1;
    1151     EndVbyte = StartVbyte;
    1152     h = Header.flink;                                   // ignore header node
    1153     for () {
     1153        HandleNode *h;
     1154        char *obase, *nbase, *limit;
     1155       
     1156        NoOfCompactions += 1;
     1157        EndVbyte = StartVbyte;
     1158        h = Header.flink;                                       // ignore header node
     1159        for () {
    11541160                memmove( EndVbyte, h->s, h->lnth );
    11551161                obase = h->s;
     
    11691175                } // for
    11701176                if ( h == &Header ) break;                      // end of header list ?
    1171     } // for
     1177        } // for
    11721178} // compaction
    11731179
    11741180
    11751181static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
    1176                                                         // probably an unreasonable default, but need to assess early-round tests on changing it
     1182                                                                                                                // probably an unreasonable default, but need to assess early-round tests on changing it
    11771183
    11781184void TUNING_set_string_heap_liveness_threshold( double val ) {
    1179     heap_expansion_freespace_threshold = 1.0 - val;
     1185        heap_expansion_freespace_threshold = 1.0 - val;
    11801186}
    11811187
     
    11861192void garbage(VbyteHeap & s, int minreq ) with(s) {
    11871193#ifdef VbyteDebug
    1188     serr | "enter:garbage";
    1189     {
     1194        serr | "enter:garbage";
     1195        {
    11901196                serr | "HandleList:";
    11911197                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    11981204                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    11991205                } // for
    1200     }
    1201 #endif // VbyteDebug
    1202     int AmountUsed, AmountFree;
    1203 
    1204     AmountUsed = 0;
    1205     for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
     1206        }
     1207#endif // VbyteDebug
     1208        int AmountUsed, AmountFree;
     1209
     1210        AmountUsed = 0;
     1211        for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
    12061212                AmountUsed += i->lnth;
    1207     } // for
    1208     AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    1209    
    1210     if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {   // free space less than threshold or not enough to serve cur request
     1213        } // for
     1214        AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
     1215       
     1216        if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {       // free space less than threshold or not enough to serve cur request
    12111217
    12121218                extend( s, max( CurrSize, minreq ) );                           // extend the heap
    12131219
    1214                         //  Peter says, "This needs work before it should be used."
    1215                         //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
    1216                         //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
    1217 
    1218         // `extend` implies a `compaction` during the copy
    1219 
    1220     } else {
    1221         compaction(s);                                  // in-place
    1222     }// if
    1223 #ifdef VbyteDebug
    1224     {
     1220                //  Peter says, "This needs work before it should be used."
     1221                //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
     1222                //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
     1223
     1224                // `extend` implies a `compaction` during the copy
     1225
     1226        } else {
     1227                compaction(s);                                  // in-place
     1228        }// if
     1229#ifdef VbyteDebug
     1230        {
    12251231                serr | "HandleList:";
    12261232                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12331239                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12341240                } // for
    1235     }
    1236     serr | "exit:garbage";
     1241        }
     1242        serr | "exit:garbage";
    12371243#endif // VbyteDebug
    12381244} // garbage
     
    12471253void extend( VbyteHeap & s, int size ) with (s) {
    12481254#ifdef VbyteDebug
    1249     serr | "enter:extend, size:" | size;
    1250 #endif // VbyteDebug
    1251     char *OldStartVbyte;
    1252 
    1253     NoOfExtensions += 1;
    1254     OldStartVbyte = StartVbyte;                         // save previous byte area
    1255    
    1256     CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
    1257     StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    1258     ExtVbyte = (void *)( StartVbyte + CurrSize );
    1259     compaction(s);                                      // copy from old heap to new & adjust pointers to new heap
    1260     free( OldStartVbyte );                              // release old heap
    1261 #ifdef VbyteDebug
    1262     serr | "exit:extend, CurrSize:" | CurrSize;
     1255        serr | "enter:extend, size:" | size;
     1256#endif // VbyteDebug
     1257        char *OldStartVbyte;
     1258
     1259        NoOfExtensions += 1;
     1260        OldStartVbyte = StartVbyte;                             // save previous byte area
     1261       
     1262        CurrSize += size > InitSize ? size : InitSize;  // minimum extension, initial size
     1263        StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1264        ExtVbyte = (void *)( StartVbyte + CurrSize );
     1265        compaction(s);                                  // copy from old heap to new & adjust pointers to new heap
     1266        free( OldStartVbyte );                          // release old heap
     1267#ifdef VbyteDebug
     1268        serr | "exit:extend, CurrSize:" | CurrSize;
    12631269#endif // VbyteDebug
    12641270} // extend
     
    12721278void VbyteHeap::reduce( int size ) {
    12731279#ifdef VbyteDebug
    1274     serr | "enter:reduce, size:" | size;
    1275 #endif // VbyteDebug
    1276     char *OldStartVbyte;
    1277 
    1278     NoOfReductions += 1;
    1279     OldStartVbyte = StartVbyte;                         // save previous byte area
    1280    
    1281     CurrSize -= size;
    1282     StartVbyte = EndVbyte = new char[CurrSize];
    1283     ExtVbyte = (void *)( StartVbyte + CurrSize );
    1284     compaction();                                       // copy from old heap to new & adjust pointers to new heap
    1285     delete  OldStartVbyte;                              // release old heap
    1286 #ifdef VbyteDebug
    1287     serr | "exit:reduce, CurrSize:" | CurrSize;
     1280        serr | "enter:reduce, size:" | size;
     1281#endif // VbyteDebug
     1282        char *OldStartVbyte;
     1283
     1284        NoOfReductions += 1;
     1285        OldStartVbyte = StartVbyte;                             // save previous byte area
     1286       
     1287        CurrSize -= size;
     1288        StartVbyte = EndVbyte = new char[CurrSize];
     1289        ExtVbyte = (void *)( StartVbyte + CurrSize );
     1290        compaction();                                   // copy from old heap to new & adjust pointers to new heap
     1291        delete  OldStartVbyte;                          // release old heap
     1292#ifdef VbyteDebug
     1293        !serr | "exit:reduce, CurrSize:" | CurrSize;
    12881294#endif // VbyteDebug
    12891295} // reduce
  • libcfa/src/collections/string_res.hfa

    r0522ebe ra4da45e  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jan  4 11:28:06 2024
    13 // Update Count     : 27
     12// Last Modified On : Wed Feb  7 21:24:40 2024
     13// Update Count     : 59
    1414//
    1515
     
    123123void ?|?(ofstream & out, const string_res & s);
    124124ifstream & ?|?(ifstream & in, string_res & s);
    125 void ?|?( ifstream & in, string_res & s );
     125
     126struct _Istream_Rwidth {
     127        string_res * s;
     128        inline _Istream_str_base;
     129}; // _Istream_Rwidth
     130
     131struct _Istream_Rquoted {
     132        // string_res * s;
     133        // inline _Istream_str_base;
     134        _Istream_Rwidth rstr;
     135}; // _Istream_Rquoted
    126136
    127137struct _Istream_Rstr {
    128138        string_res * s;
    129139        inline _Istream_str_base;
     140//      _Istream_Rwidth rstr;
    130141}; // _Istream_Rstr
    131142
    132143static inline {
    133144        // read width does not include null terminator
    134         _Istream_Rstr wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, rwd, {.flags.rwd : true}} }; }
     145        _Istream_Rwidth wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rwidth)@{ .s : &s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
    135146        _Istream_Rstr getline( string_res & s, const char delimiter = '\n' ) {
    136                 return (_Istream_Rstr)@{ &s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    137         }
    138         _Istream_Rstr & getline( _Istream_Rstr & fmt, const char delimiter = '\n' ) {
    139                 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;
    140         }
    141         _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : false}} }; }
    142         _Istream_Rstr & incl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
    143         _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : true}} }; }
    144         _Istream_Rstr & excl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
    145         _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.ignore : true}} }; }
    146         _Istream_Rstr & ignore( _Istream_Rstr & fmt ) { fmt.flags.ignore = true; return fmt; }
     147//              return (_Istream_Rstr)@{ { .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } };
     148                return (_Istream_Rstr)@{ .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } };
     149        }
     150        _Istream_Rstr & getline( _Istream_Rwidth & f, const char delimiter = '\n' ) {
     151                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Rstr &)f;
     152        }
     153        _Istream_Rquoted quoted( string_res & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) {
     154                return (_Istream_Rquoted)@{ { .s : &s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } };
     155        }
     156        _Istream_Rquoted & quoted( _Istream_Rwidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) {
     157                f.delimiters[0] = Ldelimiter;  f.delimiters[1] = Rdelimiter;  f.delimiters[2] = '\0';
     158                return (_Istream_Rquoted &)f;
     159        }
     160        _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; }
     161        _Istream_Rstr & incl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Rstr &)f; }
     162        _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; }
     163        _Istream_Rstr & excl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Rstr &)f; }
     164        _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     165        _Istream_Rstr & ignore( _Istream_Rwidth & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; }
     166        _Istream_Rquoted & ignore( _Istream_Rquoted & f ) { f.rstr.flags.ignore = true; return (_Istream_Rquoted &)f; }
     167        _Istream_Rstr & ignore( _Istream_Rstr & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; }
    147168} // distribution
     169ifstream & ?|?( ifstream & is, _Istream_Rquoted f );
    148170ifstream & ?|?( ifstream & is, _Istream_Rstr f );
    149 void ?|?( ifstream & is, _Istream_Rstr t );
     171static inline ifstream & ?|?( ifstream & is, _Istream_Rwidth f ) { return is | *(_Istream_Rstr *)&f; }
    150172
    151173// Concatenation
  • libcfa/src/fstream.cfa

    r0522ebe ra4da45e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 09:56:08 2024
    13 // Update Count     : 554
     12// Last Modified On : Sun Feb 11 20:55:45 2024
     13// Update Count     : 580
    1414//
    1515
     
    121121    } // for
    122122        if ( file == 0p ) {
    123                 throw (open_failure){ os };
     123                throwResume (open_failure){ os };
    124124                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    125125        } // if
     
    241241    } // for
    242242        if ( file == 0p ) {
    243                 throw (open_failure){ is };
     243                throwResume (open_failure){ is };
    244244                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    245245        } // if
     
    295295        va_start( args, format );
    296296
    297         int nargs;
     297        int nargs, tmperrno;
    298298    for () {                                                                                    // no check for EINTR limit waiting for keyboard input
    299                 errno = 0;
    300299                disable_interrupts();
     300                errno = 0;
    301301                nargs = vfscanf( (FILE *)(is.file$), format, args );
     302                tmperrno = errno;
    302303                enable_interrupts();
    303304          if ( nargs != EOF || errno != EINTR ) break;          // timer interrupt ?
     
    308309                } // if
    309310        } // if
     311        if ( tmperrno == ERANGE ) throwResume ExceptionInst( data_range );
    310312        va_end( args );
    311313        return nargs;
     
    318320// *********************************** exceptions ***********************************
    319321
    320 
    321 static vtable(open_failure) open_failure_vt;
    322322
    323323// exception I/O constructors
     
    335335
    336336
    337 static vtable(close_failure) close_failure_vt;
    338 
    339337// exception I/O constructors
    340338void ?{}( close_failure & ex, ofstream & ostream ) with( ex ) {
     
    351349
    352350
    353 static vtable(write_failure) write_failure_vt;
    354 
    355351// exception I/O constructors
    356352void ?{}( write_failure & ex, ofstream & ostream ) with( ex ) {
     
    367363
    368364
    369 static vtable(read_failure) read_failure_vt;
    370 
    371365// exception I/O constructors
    372366void ?{}( read_failure & ex, ofstream & ostream ) with( ex ) {
  • libcfa/src/fstream.hfa

    r0522ebe ra4da45e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Oct 18 20:30:12 2023
    13 // Update Count     : 261
     12// Last Modified On : Sun Feb 11 20:35:00 2024
     13// Update Count     : 274
    1414//
    1515
     
    139139
    140140
    141 exception open_failure {
     141ExceptionDecl( open_failure,
    142142        union {
    143143                ofstream * ostream;
     
    146146        // TEMPORARY: need polymorphic exceptions
    147147        int tag;                                                                                        // 1 => ostream; 0 => istream
    148 };
     148);
    149149
    150150void ?{}( open_failure & this, ofstream & );
    151151void ?{}( open_failure & this, ifstream & );
    152152
    153 exception close_failure {
     153ExceptionDecl( close_failure,
    154154        union {
    155155                ofstream * ostream;
     
    158158        // TEMPORARY: need polymorphic exceptions
    159159        int tag;                                                                                        // 1 => ostream; 0 => istream
    160 };
     160);
    161161
    162162void ?{}( close_failure & this, ofstream & );
    163163void ?{}( close_failure & this, ifstream & );
    164164
    165 exception write_failure {
     165ExceptionDecl( write_failure,
    166166        union {
    167167                ofstream * ostream;
     
    170170        // TEMPORARY: need polymorphic exceptions
    171171        int tag;                                                                                        // 1 => ostream; 0 => istream
    172 };
     172);
    173173
    174174void ?{}( write_failure & this, ofstream & );
    175175void ?{}( write_failure & this, ifstream & );
    176176
    177 exception read_failure {
     177ExceptionDecl( read_failure,
    178178        union {
    179179                ofstream * ostream;
     
    182182        // TEMPORARY: need polymorphic exceptions
    183183        int tag;                                                                                        // 1 => ostream; 0 => istream
    184 };
     184);
    185185
    186186void ?{}( read_failure & this, ofstream & );
  • libcfa/src/iostream.cfa

    r0522ebe ra4da45e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 11:58:54 2024
    13 // Update Count     : 1917
     12// Last Modified On : Mon Feb 12 09:26:05 2024
     13// Update Count     : 1966
    1414//
    1515
     
    774774forall( istype & | basic_istream( istype ) ) {
    775775        istype & ?|?( istype & is, bool & b ) {
     776                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    776777                char val[6];
    777778                int args = fmt( is, "%5s", val );
    778                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     779                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    779780                if ( strcmp( val, "true" ) == 0 ) b = true;
    780781                else if ( strcmp( val, "false" ) == 0 ) b = false;
     
    787788
    788789        istype & ?|?( istype & is, char & c ) {
     790                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    789791                char temp;
    790792                for () {
    791793                        int args = fmt( is, "%c", &temp );
    792                         if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     794                        if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    793795                        // do not overwrite parameter with newline unless appropriate
    794796                        if ( temp != '\n' || getANL$( is ) ) { c = temp; break; }
     
    799801
    800802        istype & ?|?( istype & is, signed char & sc ) {
     803                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    801804                int args = fmt( is, "%hhi", &sc );
    802                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     805                if ( args != 1 ) throwResume ExceptionInst( missing_data );
    803806                return is;
    804807        } // ?|?
    805808
    806809        istype & ?|?( istype & is, unsigned char & usc ) {
     810                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    807811                int args = fmt( is, "%hhi", &usc );
    808                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     812                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    809813                return is;
    810814        } // ?|?
    811815
    812816        istype & ?|?( istype & is, short int & si ) {
     817                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    813818                int args = fmt( is, "%hi", &si );
    814                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     819                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    815820                return is;
    816821        } // ?|?
    817822
    818823        istype & ?|?( istype & is, unsigned short int & usi ) {
     824                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    819825                int args = fmt( is, "%hi", &usi );
    820                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     826                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    821827                return is;
    822828        } // ?|?
    823829
    824830        istype & ?|?( istype & is, int & i ) {
     831                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    825832                int args = fmt( is, "%i", &i );
    826                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     833                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    827834                return is;
    828835        } // ?|?
    829836
    830837        istype & ?|?( istype & is, unsigned int & ui ) {
     838                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    831839                int args = fmt( is, "%i", &ui );
    832                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     840                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    833841                return is;
    834842        } // ?|?
    835843
    836844        istype & ?|?( istype & is, long int & li ) {
     845                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    837846                int args = fmt( is, "%li", &li );
    838                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     847                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    839848                return is;
    840849        } // ?|?
    841850
    842851        istype & ?|?( istype & is, unsigned long int & ulli ) {
     852                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    843853                int args = fmt( is, "%li", &ulli );
    844                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     854                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    845855                return is;
    846856        } // ?|?
    847857
    848858        istype & ?|?( istype & is, long long int & lli ) {
     859                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    849860                int args = fmt( is, "%lli", &lli );
    850                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     861                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    851862                return is;
    852863        } // ?|?
    853864
    854865        istype & ?|?( istype & is, unsigned long long int & ulli ) {
     866                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    855867                int args = fmt( is, "%lli", &ulli );
    856                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     868                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    857869                return is;
    858870        } // ?|?
     
    881893
    882894        istype & ?|?( istype & is, float & f ) {
     895                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    883896                int args = fmt( is, "%f", &f );
    884                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     897                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    885898                return is;
    886899        } // ?|?
    887900
    888901        istype & ?|?( istype & is, double & d ) {
     902                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    889903                int args = fmt( is, "%lf", &d );
    890                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     904                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    891905                return is;
    892906        } // ?|?
    893907
    894908        istype & ?|?( istype & is, long double & ld ) {
     909                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    895910                int args = fmt( is, "%Lf", &ld );
    896                 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
     911                if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
    897912                return is;
    898913        } // ?|?
    899914
    900915        istype & ?|?( istype & is, float _Complex & fc ) {
     916                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    901917                float re, im;
    902918                int args = fmt( is, "%f%fi", &re, &im );
    903                 if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
     919                if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
    904920                fc = re + im * _Complex_I;
    905921                return is;
     
    907923
    908924        istype & ?|?( istype & is, double _Complex & dc ) {
     925                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    909926                double re, im;
    910927                int args = fmt( is, "%lf%lfi", &re, &im );
    911                 if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
     928                if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
    912929                dc = re + im * _Complex_I;
    913930                return is;
     
    915932
    916933        istype & ?|?( istype & is, long double _Complex & ldc ) {
     934                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    917935                long double re, im;
    918936                int args = fmt( is, "%Lf%Lfi", &re, &im );
    919                 if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
     937                if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
    920938                ldc = re + im * _Complex_I;
    921939                return is;
     
    923941
    924942        istype & ?|?( istype & is, const char fmt[] ) {
     943                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    925944                size_t len = strlen( fmt );
    926                 char fmt2[len + 16];
    927                 strcpy( fmt2, fmt );                                                    // copy format and add %n
    928                 strcpy( &fmt2[len], "%n" );
     945                char fmtstr[len + 16];
     946                strcpy( fmtstr, fmt );                                                  // copy format and add %n
     947                strcpy( &fmtstr[len], "%n" );
    929948                int len2 = -1;
    930                 int args = fmt( is, fmt2, &len2 );
    931                 if ( args != -1 && len2 == -1 ) throwResume ExceptionInst( missing_data );
     949                fmt( is, fmtstr, &len2 );
     950                if ( ! eof( is ) && len2 == -1 ) throwResume ExceptionInst( missing_data );
    932951                return is;
    933952        } // ?|?
     
    963982forall( istype & | basic_istream( istype ) ) {
    964983        istype & ?|?( istype & is, _Istream_Cskip f ) {
    965                 // printf( "skip %s %d\n", f.scanset, f.wd );
     984                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    966985                if ( f.scanset ) {
    967986                        int nscanset = strlen(f.scanset);
     
    971990                        strcpy( &fmtstr[pos], f.scanset );  pos += nscanset;
    972991                        strcpy( &fmtstr[pos], "]" );
    973                         fmt( is, fmtstr, "" );                                          // skip scanset
     992                        fmt( is, fmtstr, "" );                                          // skip scanset, zero or more
    974993                } else {
    975994                        char ch;
    976                         // fprintf( stderr, "skip " );
    977995                        for ( f.wd ) {                                                          // skip N characters
    978                           if ( eof( is ) ) break;
    979                                 fmt( is, "%c", &ch );
    980                                 // fprintf( stderr, "`%c' ", ch );
     996                                int args = fmt( is, "%c", &ch );
     997                                if ( args != 1 ) throwResume ExceptionInst( missing_data );
    981998                        } // for
    982999                } // if
     
    9851002
    9861003        istype & ?|?( istype & is, _Istream_Cquoted f ) with( f.cstr ) {
     1004                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    9871005                int args;
    9881006          fini: {
    989                         args = fmt( is, "%*[ \f\n\r\t\v]" );            // remove leading whitespace
    990                         if ( eof( is ) ) break fini;
    991                         char rfmt[4] = { delimiters[0], '%', 'n', '\0' };
    992                         int len = 0;                                                            // may not be set in fmt
    993                         args = fmt( is, rfmt, &len );                           // remove leading quote
    994                         if ( len == 0 || eof( is ) ) break fini;
     1007                        char rfmt[5] = { ' ', delimiters[0], '%', 'n', '\0' };
     1008                        int len = -1;                                                           // may not be set in fmt
     1009                        args = fmt( is, rfmt, &len );                           // remove leading whitespace and quote
     1010                        if ( eof( is ) || len == -1 ) break fini;
    9951011
    9961012                        // Change the remainder of the read into a getline by reseting the closing delimiter.
     
    10111027
    10121028        istype & ?|?( istype & is, _Istream_Cstr f ) with( f.cstr ) {
     1029                if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    10131030                const char * scanset;
    10141031                size_t nscanset = 0;
     
    10411058                        if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
    10421059                        else args = fmt( is, fmtstr, s, &len );
    1043                         // fprintf( stderr, "cstr %s %d %d %d %s\n", fmtstr, args, len, wd, s );
     1060                        // fprintf( stderr, "cstr %s %d %d %d\n", fmtstr, args, len, f.cstr.wd );
    10441061                        if ( check && len >= rwd && ! eof( is ) ) {     // might not fit
    10451062                                char peek;
    10461063                                fmt( is, "%c", &peek );                                 // check for whitespace terminator
    10471064                                // fprintf( stderr, "peek %d '%c'\n", args, peek );
    1048                                 if ( ! eof( is ) ) {
     1065                                if ( ! eof( is ) ) {                                    // can only fail at eof
    10491066                                        ungetc( is, peek );
    10501067                                        if ( ! isspace( peek ) ) throwResume ExceptionInst( cstring_length );
     
    10521069                        } // if
    10531070                        // FIX ME: CFA strings need to be modified to NOT change the argument for this case, then this can be removed.
    1054                         if ( ! flags.ignore && args == 0 ) s[0]= '\0'; // read failed => no pattern match => set string to null
     1071                        //fprintf( stderr, "cstr %d %d %d %d '%s'\n", flags.ignore, args, len, eof( is ), s );
     1072                        //if ( ! flags.ignore && args == 0 ) s[0]= '\0'; // read failed => no pattern match => set string to null
    10551073                } else {
    10561074                        if ( flags.delimiter ) {                                        // getline
    10571075                                int len = 0;                                                    // may not be set in fmt
    1058                                 if ( delimiters[2] != '\0' ) {                  // read single character ?
     1076                                if ( delimiters[2] != '\0' ) {                  // (quoted) read single character ?
    10591077                                        sprintf( &fmtstr[pos], "c%%n" );
    10601078                                } else {
     
    10631081                                if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
    10641082                                else args = fmt( is, fmtstr, s, &len );
     1083
    10651084                                if ( check && len == rwd && ! eof( is ) ) {     // might not fit
    10661085                                        char peek;
     
    10961115                } // if
    10971116                if ( args == 1 && eof( is ) ) {                                 // data but scan ended at EOF
    1098                         // fprintf( stderr, "clear\n" );
    10991117                        clear( is );                                                            // => reset EOF => detect again on next read
    11001118                } // if
  • libcfa/src/iostream.hfa

    r0522ebe ra4da45e  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 11:56:29 2024
    13 // Update Count     : 733
     12// Last Modified On : Tue Feb  6 18:35:54 2024
     13// Update Count     : 743
    1414//
    1515
     
    370370// *********************************** exceptions ***********************************
    371371
    372 ExceptionDecl( cstring_length );
    373 ExceptionDecl( missing_data );
     372ExceptionDecl( missing_data );                                                  // read finds no appropriate data
     373ExceptionDecl( cstring_length );                                                // character string size exceeded
     374ExceptionDecl( data_range );                                                    // value too large for numerical type
    374375
    375376// *********************************** manipulators ***********************************
     
    406407        char * s;
    407408        inline _Istream_str_base;
    408 }; // _Istream_Cstr
     409}; // _Istream_Cwidth
    409410
    410411// Restrict nesting of input manipulators to those combinations that make sense.
     412
     413struct _Istream_Cquoted {
     414        _Istream_Cwidth cstr;
     415}; // _Istream_Cquoted
    411416
    412417struct _Istream_Cstr {
     
    414419}; // _Istream_Cstr
    415420
    416 struct _Istream_Cquoted {
    417         _Istream_Cwidth cstr;
    418 }; // _Istream_Cquoted
    419 
    420421static inline {
    421422        // width must include room for null terminator, (gcc) scanf does not allow a 0 width => wd > 1 (1 char and null) and rd > 0 (1 char);
    422423        _Istream_Cwidth wdi( unsigned int wd, char s[] ) {
    423                 if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator
     424                if ( wd <= 1 ) throwResume ExceptionInst( cstring_length ); // minimum 1 character and null terminator
    424425                return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } };
    425426        }
    426427        _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) {
    427                 if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset
     428                if ( wd <= 1 || wd <= rwd ) throwResume ExceptionInst( cstring_length ); // minimum 1 character, null terminator, plus subset
    428429                return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } };
     430        }
     431        _Istream_Cstr & getline( _Istream_Cwidth & f, const char delimiter = '\n' ) {
     432                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Cstr &)f;
    429433        }
    430434        _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) {
     
    435439                return (_Istream_Cquoted &)f;
    436440        }
    437         _Istream_Cstr & getline( _Istream_Cwidth & f, const char delimiter = '\n' ) {
    438                 f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Cstr &)f;
    439         }
    440441        _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; }
    441442        _Istream_Cstr & excl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Cstr &)f; }
    442         _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cwidth)@{ .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     443        _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cstr)@{ { .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; }
    443444        _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; }
    444445        _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; }
     
    450451        istype & ?|?( istype & is, _Istream_Cquoted f );
    451452        istype & ?|?( istype & is, _Istream_Cstr f );
    452         static inline {
    453                 istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
    454         } // distribution
     453        static inline istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
    455454} // distribution
    456455
  • src/AST/Attribute.hpp

    r0522ebe ra4da45e  
    99// Author           : Aaron B. Moss
    1010// Created On       : Fri May 10 10:30:00 2019
    11 // Last Modified By : Aaron B. Moss
     11// Last Modified By : Peter A. Buhr
    1212// Created On       : Fri May 10 10:30:00 2019
    13 // Update Count     : 1
     13// Update Count     : 2
    1414//
    1515
     
    3434
    3535        Attribute( const std::string & name = "", std::vector<ptr<Expr>> && params = {})
    36         : name( name ), params( params ) {}
     36                : name( name ), params( params ) {}
    3737        virtual ~Attribute() = default;
    3838
  • src/GenPoly/Box.cpp

    r0522ebe ra4da45e  
    366366
    367367        /// Passes extra layout arguments for sized polymorphic type parameters.
    368         ast::vector<ast::Expr>::iterator passTypeVars(
     368        void passTypeVars(
    369369                ast::ApplicationExpr * expr,
     370                ast::vector<ast::Expr> & extraArgs,
    370371                ast::FunctionType const * funcType );
    371372        /// Wraps a function application with a new temporary for the
     
    387388        /// parameter list. arg should point into expr's argument list.
    388389        void boxParams(
    389                 ast::ApplicationExpr const * expr,
    390                 ast::vector<ast::Expr>::iterator arg,
     390                ast::ApplicationExpr * expr,
     391                ast::Type const * polyRetType,
    391392                ast::FunctionType const * function,
    392393                const TypeVarMap & typeVars );
     
    395396        void addInferredParams(
    396397                ast::ApplicationExpr * expr,
    397                 ast::vector<ast::Expr>::iterator arg,
     398                ast::vector<ast::Expr> & extraArgs,
    398399                ast::FunctionType const * functionType,
    399400                const TypeVarMap & typeVars );
     
    636637        ast::Expr const * ret = expr;
    637638
    638         // TODO: This entire section should probably be refactored to do less
    639         // pushing to the front/middle of a vector.
    640         ptrdiff_t initArgCount = mutExpr->args.size();
    641 
    642639        TypeVarMap exprTypeVars;
    643640        makeTypeVarMap( function, exprTypeVars );
     
    662659        }
    663660
    664         assert( typeSubs );
    665         ast::vector<ast::Expr>::iterator argIt =
    666                 passTypeVars( mutExpr, function );
    667         addInferredParams( mutExpr, argIt, function, exprTypeVars );
    668 
    669         argIt = mutExpr->args.begin();
    670         std::advance( argIt, ( mutExpr->args.size() - initArgCount ) );
    671 
    672         boxParams( mutExpr, argIt, function, exprTypeVars );
     661        ast::vector<ast::Expr> prependArgs;
     662        passTypeVars( mutExpr, prependArgs, function );
     663        addInferredParams( mutExpr, prependArgs, function, exprTypeVars );
     664
     665        boxParams( mutExpr, dynRetType, function, exprTypeVars );
     666        spliceBegin( mutExpr->args, prependArgs );
    673667        passAdapters( mutExpr, function, exprTypeVars );
    674668
     
    766760}
    767761
    768 ast::vector<ast::Expr>::iterator CallAdapter::passTypeVars(
     762void CallAdapter::passTypeVars(
    769763                ast::ApplicationExpr * expr,
     764                ast::vector<ast::Expr> & extraArgs,
    770765                ast::FunctionType const * function ) {
    771766        assert( typeSubs );
    772         ast::vector<ast::Expr>::iterator arg = expr->args.begin();
    773767        // Pass size/align for type variables.
    774768        for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) {
     
    780774                                                   toString( typeSubs ).c_str(), typeVar->typeString().c_str() );
    781775                }
    782                 arg = expr->args.insert( arg,
     776                extraArgs.emplace_back(
    783777                        new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) );
    784                 arg++;
    785                 arg = expr->args.insert( arg,
     778                extraArgs.emplace_back(
    786779                        new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) );
    787                 arg++;
    788         }
    789         return arg;
     780        }
    790781}
    791782
     
    913904
    914905void CallAdapter::boxParams(
    915                 ast::ApplicationExpr const * expr,
    916                 ast::vector<ast::Expr>::iterator arg,
     906                ast::ApplicationExpr * expr,
     907                ast::Type const * polyRetType,
    917908                ast::FunctionType const * function,
    918909                const TypeVarMap & typeVars ) {
     910        // Start at the beginning, but the return argument may have been added.
     911        auto arg = expr->args.begin();
     912        if ( polyRetType ) ++arg;
     913
    919914        for ( auto param : function->params ) {
    920915                assertf( arg != expr->args.end(),
     
    928923void CallAdapter::addInferredParams(
    929924                ast::ApplicationExpr * expr,
    930                 ast::vector<ast::Expr>::iterator arg,
     925                ast::vector<ast::Expr> & extraArgs,
    931926                ast::FunctionType const * functionType,
    932927                TypeVarMap const & typeVars ) {
    933         ast::vector<ast::Expr>::iterator cur = arg;
    934928        for ( auto assertion : functionType->assertions ) {
    935929                auto inferParam = expr->inferred.inferParams().find(
     
    940934                ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr );
    941935                boxParam( newExpr, assertion->result, typeVars );
    942                 cur = expr->args.insert( cur, newExpr.release() );
    943                 ++cur;
     936                extraArgs.emplace_back( newExpr.release() );
    944937        }
    945938}
     
    11161109        );
    11171110
    1118         for ( auto group : group_iterate( realType->assertions,
    1119                         adapterType->assertions, adaptee->assertions ) ) {
    1120                 auto assertArg = std::get<0>( group );
    1121                 auto assertParam = std::get<1>( group );
    1122                 auto assertReal = std::get<2>( group );
     1111        for ( auto const & [assertArg, assertParam, assertReal] : group_iterate(
     1112                        realType->assertions, adapterType->assertions, adaptee->assertions ) ) {
    11231113                adapteeApp->args.push_back( makeAdapterArg(
    11241114                        assertParam->var, assertArg->var->get_type(),
     
    19771967        bool hasDynamicLayout = false;
    19781968
    1979         for ( auto pair : group_iterate( baseParams, typeParams ) ) {
    1980                 auto baseParam = std::get<0>( pair );
    1981                 auto typeParam = std::get<1>( pair );
     1969        for ( auto const & [baseParam, typeParam] : group_iterate(
     1970                        baseParams, typeParams ) ) {
    19821971                if ( !baseParam->isComplete() ) continue;
    19831972                ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>();
  • src/InitTweak/FixInit.cpp

    r0522ebe ra4da45e  
    11341134                        ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences());
    11351135                        ast::Expr * memberDest = new ast::MemberExpr(funcDecl->location, field, thisExpr );
    1136                         ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
     1136                        const ast::Stmt * callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
    11371137
    11381138                        if ( callStmt ) {
  • src/InitTweak/GenInit.cc

    r0522ebe ra4da45e  
    239239                                        if ( varExpr->var == retVal ) return stmt;
    240240                                }
    241                                 ast::ptr<ast::Stmt> ctorStmt = genCtorDtor(
     241                                const ast::Stmt * ctorStmt = genCtorDtor(
    242242                                        retVal->location, "?{}", retVal, stmt->expr );
    243243                                assertf( ctorStmt,
     
    327327void ManagedTypes::endScope() { managedTypes.endScope(); }
    328328
    329 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) {
     329const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg ) {
    330330        assertf(objDecl, "genCtorDtor passed null objDecl");
    331331        InitExpander srcParam(arg);
  • src/InitTweak/GenInit.h

    r0522ebe ra4da45e  
    3333
    3434/// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
    35 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr);
     35const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr );
    3636
    3737/// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer
  • src/Parser/DeclarationNode.cc

    r0522ebe ra4da45e  
    1010// Created On       : Sat May 16 12:34:05 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Dec 14 19:05:17 2023
    13 // Update Count     : 1407
     12// Last Modified On : Fri Feb 23 18:25:57 2024
     13// Update Count     : 1533
    1414//
    1515
     
    159159
    160160        if ( ! attributes.empty() ) {
    161                 os << string( indent + 2, ' ' ) << "with attributes " << endl;
     161                os << string( indent + 2, ' ' ) << "with attributes" << endl;
    162162                for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( attributes ) ) {
    163                         os << string( indent + 4, ' ' ) << attr->name.c_str() << endl;
     163                        os << string( indent + 4, ' ' );
     164                        ast::print( os, attr, indent + 2 );
    164165                } // for
    165166        } // if
     
    537538} // DeclarationNode::checkSpecifiers
    538539
    539 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q ) {
     540DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q, bool copyattr ) {
    540541        funcSpecs |= q->funcSpecs;
    541542        storageClasses |= q->storageClasses;
    542543
    543         std::vector<ast::ptr<ast::Attribute>> tmp;
    544         tmp.reserve( q->attributes.size() );
    545         for ( auto const & attr : q->attributes ) {
    546                 tmp.emplace_back( ast::shallowCopy( attr.get() ) );
    547         }
    548         spliceBegin( attributes, tmp );
     544        if ( copyattr ) {
     545                std::vector<ast::ptr<ast::Attribute>> tmp;
     546                tmp.reserve( q->attributes.size() );
     547                for ( auto const & attr : q->attributes ) {
     548                        tmp.emplace_back( ast::shallowCopy( attr.get() ) );
     549                } // for
     550                spliceBegin( attributes, tmp );
     551        } // if
    549552
    550553        return this;
     
    681684}
    682685
    683 DeclarationNode * DeclarationNode::addType( DeclarationNode * o ) {
     686DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) {
    684687        if ( o ) {
    685688                checkSpecifiers( o );
    686                 copySpecifiers( o );
     689                copySpecifiers( o, copyattr );
    687690                if ( o->type ) {
    688691                        if ( ! type ) {
    689692                                if ( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) {
     693                                        // Hide type information aggregate instances.
    690694                                        type = new TypeData( TypeData::AggregateInst );
    691                                         type->aggInst.aggregate = o->type;
     695                                        type->aggInst.aggregate = o->type;      // change ownership
     696                                        type->aggInst.aggregate->aggregate.attributes.swap( o->attributes ); // change ownership                                       
    692697                                        if ( o->type->kind == TypeData::Aggregate ) {
    693698                                                type->aggInst.hoistType = o->type->aggregate.body;
     
    700705                                        type = o->type;
    701706                                } // if
    702                                 o->type = nullptr;
     707                                o->type = nullptr;                                              // change ownership
    703708                        } else {
    704709                                addTypeToType( o->type, type );
     
    953958}
    954959
    955 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o ) {
     960DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) {
    956961        if ( ! o ) return nullptr;
    957962
    958         o->copySpecifiers( this );
     963        o->copySpecifiers( this, copyattr );
    959964        if ( type ) {
    960965                TypeData * srcType = type;
     
    9991004                        DeclarationNode * newnode = new DeclarationNode;
    10001005                        newnode->type = ret;
     1006                        if ( ret->kind == TypeData::Aggregate ) {
     1007                                newnode->attributes.swap( ret->aggregate.attributes );
     1008                        } // if
    10011009                        return newnode;
    10021010                } // if
     
    11101118                                        if ( extr->type->kind == TypeData::Aggregate ) {
    11111119                                                // typedef struct { int A } B is the only case?
    1112                                                 extracted_named = !extr->type->aggregate.anon;
     1120                                                extracted_named = ! extr->type->aggregate.anon;
    11131121                                        } else if ( extr->type->kind == TypeData::Enum ) {
    11141122                                                // typedef enum { A } B is the only case?
    1115                                                 extracted_named = !extr->type->enumeration.anon;
     1123                                                extracted_named = ! extr->type->enumeration.anon;
    11161124                                        } else {
    11171125                                                extracted_named = true;
  • src/Parser/DeclarationNode.h

    r0522ebe ra4da45e  
    99// Author           : Andrew Beach
    1010// Created On       : Wed Apr  5 11:38:00 2023
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Apr  5 11:55:00 2023
    13 // Update Count     : 0
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sat Feb 17 09:24:12 2024
     13// Update Count     : 4
    1414//
    1515
     
    8383        void checkQualifiers( const TypeData *, const TypeData * );
    8484        void checkSpecifiers( DeclarationNode * );
    85         DeclarationNode * copySpecifiers( DeclarationNode * );
    86         DeclarationNode * addType( DeclarationNode * );
     85        DeclarationNode * copySpecifiers( DeclarationNode *, bool = true );
     86        DeclarationNode * addType( DeclarationNode *, bool = true );
    8787        DeclarationNode * addTypedef();
    8888        DeclarationNode * addEnumBase( DeclarationNode * );
     
    106106
    107107        DeclarationNode * cloneType( std::string * newName );
    108         DeclarationNode * cloneBaseType( DeclarationNode * newdecl );
     108        DeclarationNode * cloneBaseType( DeclarationNode * newdecl, bool = true );
    109109
    110110        DeclarationNode * appendList( DeclarationNode * node ) {
  • src/Parser/TypeData.cc

    r0522ebe ra4da45e  
    1010// Created On       : Sat May 16 15:12:51 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Dec 14 18:59:12 2023
    13 // Update Count     : 684
     12// Last Modified On : Fri Feb 23 08:58:30 2024
     13// Update Count     : 734
    1414//
    1515
     
    2020
    2121#include "AST/Decl.hpp"            // for AggregateDecl, ObjectDecl, TypeDe...
     22#include "AST/Attribute.hpp"       // for Attribute
    2223#include "AST/Init.hpp"            // for SingleInit, ListInit
    2324#include "AST/Print.hpp"           // for print
    2425#include "Common/SemanticError.h"  // for SemanticError
    2526#include "Common/utility.h"        // for splice, spliceBegin
     27#include "Common/Iterate.hpp"      // for reverseIterate
    2628#include "Parser/ExpressionNode.h" // for ExpressionNode
    2729#include "Parser/StatementNode.h"  // for StatementNode
     
    199201                newtype->aggregate.kind = aggregate.kind;
    200202                newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr;
     203                newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;
    201204                newtype->aggregate.params = maybeCopy( aggregate.params );
    202205                newtype->aggregate.actuals = maybeCopy( aggregate.actuals );
    203206                newtype->aggregate.fields = maybeCopy( aggregate.fields );
     207                newtype->aggregate.attributes = aggregate.attributes;
    204208                newtype->aggregate.body = aggregate.body;
    205209                newtype->aggregate.anon = aggregate.anon;
    206210                newtype->aggregate.tagged = aggregate.tagged;
    207                 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;
    208211                break;
    209212        case AggregateInst:
     
    336339                } // if
    337340                if ( aggregate.body ) {
    338                         os << string( indent + 2, ' ' ) << " with body" << endl;
     341                        os << string( indent + 2, ' ' ) << "with body" << endl;
     342                } // if
     343                if ( ! aggregate.attributes.empty() ) {
     344                        os << string( indent + 2, ' ' ) << "with attributes" << endl;
     345                        for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( aggregate.attributes ) ) {
     346                                os << string( indent + 4, ' ' );
     347                                ast::print( os, attr, indent + 2 );
     348                        } // for
    339349                } // if
    340350                break;
     
    358368                } // if
    359369                if ( enumeration.body ) {
    360                         os << string( indent + 2, ' ' ) << " with body" << endl;
     370                        os << string( indent + 2, ' ' ) << "with body" << endl;
    361371                } // if
    362372                if ( base ) {
     
    10881098
    10891099ast::BaseInstType * buildComAggInst(
    1090                 const TypeData * type,
     1100                const TypeData * td,
    10911101                std::vector<ast::ptr<ast::Attribute>> && attributes,
    10921102                ast::Linkage::Spec linkage ) {
    1093         switch ( type->kind ) {
     1103        switch ( td->kind ) {
    10941104        case TypeData::Enum:
    1095                 if ( type->enumeration.body ) {
     1105                if ( td->enumeration.body ) {
    10961106                        ast::EnumDecl * typedecl =
    1097                                 buildEnum( type, std::move( attributes ), linkage );
     1107                                buildEnum( td, std::move( attributes ), linkage );
    10981108                        return new ast::EnumInstType(
    10991109                                typedecl,
    1100                                 buildQualifiers( type )
     1110                                buildQualifiers( td )
    11011111                        );
    11021112                } else {
    11031113                        return new ast::EnumInstType(
    1104                                 *type->enumeration.name,
    1105                                 buildQualifiers( type )
     1114                                *td->enumeration.name,
     1115                                buildQualifiers( td )
    11061116                        );
    11071117                } // if
    11081118                break;
    11091119        case TypeData::Aggregate:
    1110                 if ( type->aggregate.body ) {
     1120                if ( td->aggregate.body ) {
    11111121                        ast::AggregateDecl * typedecl =
    1112                                 buildAggregate( type, std::move( attributes ), linkage );
    1113                         switch ( type->aggregate.kind ) {
     1122                                buildAggregate( td, std::move( attributes ), linkage );
     1123                        switch ( td->aggregate.kind ) {
    11141124                        case ast::AggregateDecl::Struct:
    11151125                        case ast::AggregateDecl::Coroutine:
     
    11181128                                return new ast::StructInstType(
    11191129                                        strict_dynamic_cast<ast::StructDecl *>( typedecl ),
    1120                                         buildQualifiers( type )
     1130                                        buildQualifiers( td )
    11211131                                );
    11221132                        case ast::AggregateDecl::Union:
    11231133                                return new ast::UnionInstType(
    11241134                                        strict_dynamic_cast<ast::UnionDecl *>( typedecl ),
    1125                                         buildQualifiers( type )
     1135                                        buildQualifiers( td )
    11261136                                );
    11271137                        case ast::AggregateDecl::Trait:
     
    11321142                        } // switch
    11331143                } else {
    1134                         switch ( type->aggregate.kind ) {
     1144                        switch ( td->aggregate.kind ) {
    11351145                        case ast::AggregateDecl::Struct:
    11361146                        case ast::AggregateDecl::Coroutine:
     
    11381148                        case ast::AggregateDecl::Thread:
    11391149                                return new ast::StructInstType(
    1140                                         *type->aggregate.name,
    1141                                         buildQualifiers( type )
     1150                                        *td->aggregate.name,
     1151                                        buildQualifiers( td )
    11421152                                );
    11431153                        case ast::AggregateDecl::Union:
    11441154                                return new ast::UnionInstType(
    1145                                         *type->aggregate.name,
    1146                                         buildQualifiers( type )
     1155                                        *td->aggregate.name,
     1156                                        buildQualifiers( td )
    11471157                                );
    11481158                        case ast::AggregateDecl::Trait:
    11491159                                return new ast::TraitInstType(
    1150                                         *type->aggregate.name,
    1151                                         buildQualifiers( type )
     1160                                        *td->aggregate.name,
     1161                                        buildQualifiers( td )
    11521162                                );
    11531163                        default:
  • src/Parser/TypeData.h

    r0522ebe ra4da45e  
    99// Author           : Peter A. Buhr
    1010// Created On       : Sat May 16 15:18:36 2015
    11 // Last Modified By : Andrew Beach
    12 // Last Modified On : Wed Mar  1 10:44:00 2023
    13 // Update Count     : 206
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Thu Feb 22 16:30:31 2024
     13// Update Count     : 210
    1414//
    1515
     
    3030                ast::AggregateDecl::Aggregate kind;
    3131                const std::string * name = nullptr;
     32                const std::string * parent = nullptr;
    3233                DeclarationNode * params = nullptr;
    3334                ExpressionNode * actuals = nullptr;                             // holds actual parameters later applied to AggInst
    3435                DeclarationNode * fields = nullptr;
     36                std::vector<ast::ptr<ast::Attribute>> attributes;
    3537                bool body;
    3638                bool anon;
    3739                bool tagged;
    38                 const std::string * parent = nullptr;
    3940        };
    4041
  • src/Parser/parser.yy

    r0522ebe ra4da45e  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Nov 26 13:18:06 2023
    13 // Update Count     : 6398
     12// Last Modified On : Fri Feb 23 18:25:46 2024
     13// Update Count     : 6484
    1414//
    1515
     
    102102
    103103DeclarationNode * distAttr( DeclarationNode * typeSpec, DeclarationNode * declList ) {
    104         // distribute declaration_specifier across all declared variables, e.g., static, const, but not __attribute__.
     104        // Distribute type specifier across all declared variables, e.g., static, const, __attribute__.
    105105        assert( declList );
    106         // printf( "distAttr1 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout );
    107         DeclarationNode * cl = (new DeclarationNode)->addType( typeSpec );
    108         // printf( "distAttr2 cl %p\n", cl ); cl->type->print( std::cout );
    109         // cl->type->aggregate.name = cl->type->aggInst.aggregate->aggregate.name;
    110 
     106
     107        // Do not distribute attributes for aggregates because the attributes surrounding the aggregate belong it not the
     108        // variables in the declaration list, e.g.,
     109        //
     110        //   struct __attribute__(( aligned(128) )) S { ...
     111        //   } v1 __attribute__(( aligned(64) )), v2 __attribute__(( aligned(32) )), v3;
     112        //   struct S v4;
     113        //
     114        // v1 => 64, v2 =>32, v3 => 128, v2 => 128
     115        //
     116        // Anonymous aggregates are a special case because there is no aggregate to bind the attribute to; hence it floats
     117        // to the declaration list.
     118        //
     119        //   struct __attribute__(( aligned(128) )) /*anonymous */ { ... } v1;
     120        //
     121        // v1 => 128
     122
     123        bool copyattr = ! (typeSpec->type && typeSpec->type->kind == TypeData::Aggregate && ! typeSpec->type->aggregate.anon );
     124
     125        // addType copies the type information for the aggregate instances from typeSpec into cl's aggInst.aggregate.
     126        DeclarationNode * cl = (new DeclarationNode)->addType( typeSpec ); // typeSpec IS DELETED!!!
     127
     128        // Start at second variable in declaration list and clone the type specifiers for each variable..
    111129        for ( DeclarationNode * cur = dynamic_cast<DeclarationNode *>( declList->get_next() ); cur != nullptr; cur = dynamic_cast<DeclarationNode *>( cur->get_next() ) ) {
    112                 cl->cloneBaseType( cur );
     130                cl->cloneBaseType( cur, copyattr );                             // cur is modified
    113131        } // for
    114         declList->addType( cl );
    115         // printf( "distAttr3 declList %p\n", declList ); declList->print( std::cout, 0 );
     132
     133        // Add first variable in declaration list with hidden type information in aggInst.aggregate, which is used by
     134        // extractType to recover the type for the aggregate instances.
     135        declList->addType( cl, copyattr );                                      // cl IS DELETED!!!
    116136        return declList;
    117137} // distAttr
     
    192212                fieldList = DeclarationNode::newName( nullptr );
    193213        } // if
    194 //      return distAttr( typeSpec, fieldList );                         // mark all fields in list
    195214
    196215        // printf( "fieldDecl3 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout, 0 );
    197         DeclarationNode * temp = distAttr( typeSpec, fieldList );                               // mark all fields in list
     216        DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list
    198217        // printf( "fieldDecl4 temp %p\n", temp ); temp->print( std::cout, 0 );
    199218        return temp;
     
    761780        | string_literal '`' identifier                                         // CFA, postfix call
    762781                { $$ = new ExpressionNode( build_func( yylloc, new ExpressionNode( build_varref( yylloc, build_postfix_name( $3 ) ) ), $1 ) ); }
     782
     783                // SKULLDUGGERY: The typedef table used for parsing does not store fields in structures. To parse a qualified
     784                // name, it is assumed all name-tokens after the first are identifiers, regardless of how the lexer identifies
     785        // them. For example:
     786                //   
     787                //   struct S;
     788                //   forall(T) struct T;
     789                //   union U;
     790                //   enum E { S, T, E };
     791                //   struct Z { int S, T, Z, E, U; };
     792                //   void fred () {
     793                //       Z z;
     794                //       z.S;  // lexer returns S is TYPEDEFname
     795                //       z.T;  // lexer returns T is TYPEGENname
     796                //       z.Z;  // lexer returns Z is TYPEDEFname
     797                //       z.U;  // lexer returns U is TYPEDEFname
     798                //       z.E;  // lexer returns E is TYPEDEFname
     799                //   }
    763800        | postfix_expression '.' identifier
    764801                { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); }
     802        | postfix_expression '.' TYPEDEFname                            // CFA, SKULLDUGGERY
     803                { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); }
     804        | postfix_expression '.' TYPEGENname                            // CFA, SKULLDUGGERY
     805                { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); }
     806
    765807        | postfix_expression '.' INTEGERconstant                        // CFA, tuple index
    766808                { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_constantInteger( yylloc, *$3 ) ) ); }
     
    10391081        | logical_OR_expression '?' comma_expression ':' conditional_expression
    10401082                { $$ = new ExpressionNode( build_cond( yylloc, $1, $3, $5 ) ); }
    1041                 // FIX ME: computes $1 twice
    10421083        | logical_OR_expression '?' /* empty */ ':' conditional_expression // GCC, omitted first operand
    1043                 { $$ = new ExpressionNode( build_cond( yylloc, $1, $1->clone(), $4 ) ); }
     1084                { $$ = new ExpressionNode( build_cond( yylloc, $1, nullptr, $4 ) ); }
    10441085        ;
    10451086
     
    18561897declaration_list:
    18571898        declaration
    1858         | declaration_list declaration
    1859                 { $$ = $1->appendList( $2 ); }
     1899        | declaration_list declaration          { $$ = $1->appendList( $2 ); }
    18601900        ;
    18611901
     
    18901930declaration:                                                                                    // old & new style declarations
    18911931        c_declaration ';'
    1892                 {
    1893                         // printf( "C_DECLARATION1 %p %s\n", $$, $$->name ? $$->name->c_str() : "(nil)" );
    1894                         // for ( Attribute * attr: reverseIterate( $$->attributes ) ) {
    1895                         //   printf( "\tattr %s\n", attr->name.c_str() );
    1896                         // } // for
    1897                 }
    18981932        | cfa_declaration ';'                                                           // CFA
    18991933        | static_assert                                                                         // C11
     
    23482382sue_declaration_specifier:                                                              // struct, union, enum + storage class + type specifier
    23492383        sue_type_specifier
    2350                 {
    2351                         // printf( "sue_declaration_specifier %p %s\n", $$, $$->type->aggregate.name ? $$->type->aggregate.name->c_str() : "(nil)" );
    2352                         // for ( Attribute * attr: reverseIterate( $$->attributes ) ) {
    2353                         //   printf( "\tattr %s\n", attr->name.c_str() );
    2354                         // } // for
    2355                 }
    23562384        | declaration_qualifier_list sue_type_specifier
    23572385                { $$ = $2->addQualifiers( $1 ); }
     
    23642392sue_type_specifier:                                                                             // struct, union, enum + type specifier
    23652393        elaborated_type
    2366                 {
    2367                         // printf( "sue_type_specifier %p %s\n", $$, $$->type->aggregate.name ? $$->type->aggregate.name->c_str() : "(nil)" );
    2368                         // for ( Attribute * attr: reverseIterate( $$->attributes ) ) {
    2369                         //   printf( "\tattr %s\n", attr->name.c_str() );
    2370                         // } // for
    2371                 }
    23722394        | type_qualifier_list
    23732395                { if ( $1->type != nullptr && $1->type->forall ) forall = true; } // remember generic type
     
    24422464elaborated_type:                                                                                // struct, union, enum
    24432465        aggregate_type
    2444                 {
    2445                         // printf( "elaborated_type %p %s\n", $$, $$->type->aggregate.name ? $$->type->aggregate.name->c_str() : "(nil)" );
    2446                         // for ( Attribute * attr: reverseIterate( $$->attributes ) ) {
    2447                         //   printf( "\tattr %s\n", attr->name.c_str() );
    2448                         // } // for
    2449                 }
    24502466        | enum_type
    24512467        ;
     
    26792695                { $$ = DeclarationNode::newEnum( nullptr, $4, true, false )->addQualifiers( $2 ); }
    26802696        | ENUM attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule
    2681                 { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2697                { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    26822698        | ENUM attribute_list_opt identifier
    26832699                { typedefTable.makeTypedef( *$3, "enum_type 1" ); }
     
    26942710                }
    26952711        | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // unqualified type name
    2696                 { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2712                { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    26972713        | ENUM '(' ')' attribute_list_opt '{' enumerator_list comma_opt '}'
    26982714                {
     
    27002716                }
    27012717        | ENUM '(' ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule
    2702                 { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2718                { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    27032719        | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt identifier attribute_list_opt
    27042720                {
     
    31773193                        // unit, which is a dubious task, especially because C uses name rather than structural typing; hence it is
    31783194                        // disallowed at the moment.
    3179                         if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static && $1->type && $1->type->kind == TypeData::AggregateInst ) {
     3195                        if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static &&
     3196                                 $1->type && $1->type->kind == TypeData::AggregateInst ) {
    31803197                                if ( $1->type->aggInst.aggregate->kind == TypeData::Enum && $1->type->aggInst.aggregate->enumeration.anon ) {
    31813198                                        SemanticError( yylloc, "extern anonymous enumeration is currently unimplemented." ); $$ = nullptr;
  • src/ResolvExpr/CandidateFinder.cpp

    r0522ebe ra4da45e  
    10181018                                        }
    10191019
    1020                                         if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);                                             
     1020                                        if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer);
    10211021                                        else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type));
    10221022                                }
     
    12831283                                                restructureCast( cand->expr, toType, castExpr->isGenerated ),
    12841284                                                copy( cand->env ), std::move( open ), std::move( need ), cand->cost + thisCost);
    1285                                         // currently assertions are always resolved immediately so this should have no effect. 
     1285                                        // currently assertions are always resolved immediately so this should have no effect.
    12861286                                        // if this somehow changes in the future (e.g. delayed by indeterminate return type)
    12871287                                        // we may need to revisit the logic.
     
    16181618        void Finder::postvisit( const ast::ConditionalExpr * conditionalExpr ) {
    16191619                // candidates for condition
     1620                ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );
    16201621                CandidateFinder finder1( context, tenv );
    1621                 ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );
    16221622                finder1.find( arg1, ResolveMode::withAdjustment() );
    16231623                if ( finder1.candidates.empty() ) return;
    16241624
    16251625                // candidates for true result
     1626                // FIX ME: resolves and runs arg1 twice when arg2 is missing.
     1627                ast::Expr const * arg2 = conditionalExpr->arg2;
     1628                arg2 = arg2 ? arg2 : conditionalExpr->arg1.get();
    16261629                CandidateFinder finder2( context, tenv );
    16271630                finder2.allowVoid = true;
    1628                 finder2.find( conditionalExpr->arg2, ResolveMode::withAdjustment() );
     1631                finder2.find( arg2, ResolveMode::withAdjustment() );
    16291632                if ( finder2.candidates.empty() ) return;
    16301633
     
    18971900                                        CandidateRef newCand =
    18981901                                                std::make_shared<Candidate>(
    1899                                                         newExpr, copy( tenv ), ast::OpenVarSet{}, 
     1902                                                        newExpr, copy( tenv ), ast::OpenVarSet{},
    19001903                                                        ast::AssertionSet{}, Cost::zero, cost
    19011904                                                );
    1902                                        
     1905
    19031906                                        if (newCand->expr->env) {
    19041907                                                newCand->env.add(*newCand->expr->env);
  • src/ResolvExpr/ResolveTypeof.cc

    r0522ebe ra4da45e  
    3535
    3636struct ResolveTypeof : public ast::WithShortCircuiting {
    37     const ResolveContext & context;
     37        const ResolveContext & context;
    3838
    39                 ResolveTypeof( const ResolveContext & context ) :
    40                         context( context ) {}
     39        ResolveTypeof( const ResolveContext & context ) : context( context ) {}
    4140
    42                 void previsit( const ast::TypeofType * ) { visit_children = false; }
     41        void previsit( const ast::TypeofType * ) { visit_children = false; }
    4342
    44         const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
    45         // pass on null expression
    46             if ( ! typeofType->expr ) return typeofType;
     43        const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
     44                // pass on null expression
     45                if ( ! typeofType->expr ) return typeofType;
    4746
    48             ast::ptr< ast::Type > newType;
    49             if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {
    50             // typeof wrapping type
    51             newType = tyExpr->type;
    52         } else {
    53             // typeof wrapping expression
    54             ast::TypeEnvironment dummy;
    55             ast::ptr< ast::Expr > newExpr =
    56                 resolveInVoidContext( typeofType->expr, context, dummy );
    57             assert( newExpr->result && ! newExpr->result->isVoid() );
    58             newType = newExpr->result;
    59         }
     47                ast::ptr< ast::Type > newType;
     48                if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {
     49                        // typeof wrapping type
     50                        newType = tyExpr->type;
     51                } else {
     52                        // typeof wrapping expression
     53                        ast::TypeEnvironment dummy;
     54                        ast::ptr< ast::Expr > newExpr =
     55                                resolveInVoidContext( typeofType->expr, context, dummy );
     56                        assert( newExpr->result && ! newExpr->result->isVoid() );
     57                        newType = newExpr->result;
     58                }
    6059
    61         // clear qualifiers for base, combine with typeoftype quals regardless
    62         if ( typeofType->kind == ast::TypeofType::Basetypeof ) {
    63             // replace basetypeof(<enum>) by int
    64                                 if ( newType.as< ast::EnumInstType >() ) {
    65                                         newType = new ast::BasicType{
    66                                                 ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) };
    67             }
    68                                 reset_qualifiers(
    69                                         newType,
    70                                         ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
    71         } else {
    72                                 add_qualifiers( newType, typeofType->qualifiers );
    73         }
     60                // clear qualifiers for base, combine with typeoftype quals regardless
     61                if ( typeofType->kind == ast::TypeofType::Basetypeof ) {
     62                        // replace basetypeof(<enum>) by int
     63                        if ( newType.as< ast::EnumInstType >() ) {
     64                                newType = new ast::BasicType(
     65                                        ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) );
     66                        }
     67                        reset_qualifiers(
     68                                newType,
     69                                ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );
     70                } else {
     71                        add_qualifiers( newType, typeofType->qualifiers );
     72                }
    7473
    75         return newType.release();
    76     }
     74                return newType.release();
     75        }
    7776};
    7877
     
    111110
    112111const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) {
    113     if (decl->isTypeFixed) {
    114         return decl;
    115     }
     112        if ( decl->isTypeFixed ) {
     113                return decl;
     114        }
    116115
    117     auto mutDecl = mutate(decl);
    118     fixObjectInit(decl, context);
    119     {
    120         auto resolvedType = resolveTypeof(decl->type, context);
    121         resolvedType = fixArrayType(resolvedType, context);
    122         mutDecl->type = resolvedType;
    123     }
     116        auto mutDecl = mutate(decl);
     117        fixObjectInit(decl, context);
     118        {
     119                auto resolvedType = resolveTypeof(decl->type, context);
     120                resolvedType = fixArrayType(resolvedType, context);
     121                mutDecl->type = resolvedType;
     122        }
    124123
    125     // Do not mangle unnamed variables.
    126     if (!mutDecl->name.empty()) {
    127         mutDecl->mangleName = Mangle::mangle(mutDecl);
    128     }
     124        // Do not mangle unnamed variables.
     125        if ( !mutDecl->name.empty() ) {
     126                mutDecl->mangleName = Mangle::mangle(mutDecl);
     127        }
    129128
    130     mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
    131     mutDecl->isTypeFixed = true;
    132     return mutDecl;
     129        mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
     130        mutDecl->isTypeFixed = true;
     131        return mutDecl;
    133132}
    134133
    135 const ast::ObjectDecl *fixObjectInit(const ast::ObjectDecl *decl,
    136                                     const ResolveContext &context) {
    137     if (decl->isTypeFixed) {
    138         return decl;
    139     }
     134const ast::ObjectDecl *fixObjectInit(
     135                const ast::ObjectDecl *decl, const ResolveContext &context) {
     136        if ( decl->isTypeFixed ) {
     137                return decl;
     138        }
    140139
    141     auto mutDecl = mutate(decl);
     140        if ( auto listInit = decl->init.as<ast::ListInit>() ) {
     141                for ( size_t k = 0; k < listInit->designations.size(); k++ ) {
     142                        const ast::Designation *des = listInit->designations[k].get();
     143                        // Desination here
     144                        ast::Designation * newDesignation = new ast::Designation(des->location);
     145                        std::deque<ast::ptr<ast::Expr>> newDesignators;
    142146
    143     if ( auto mutListInit = mutDecl->init.as<ast::ListInit>() ) {
    144         // std::list<ast::Designation *> newDesignations;       
    145 
    146         for ( size_t k = 0; k < mutListInit->designations.size(); k++ ) {
    147             const ast::Designation *des = mutListInit->designations[k].get();
    148             // Desination here
    149             ast::Designation * newDesignation = new ast::Designation(des->location);
    150             std::deque<ast::ptr<ast::Expr>> newDesignators;
    151 
    152             for ( ast::ptr<ast::Expr> designator : des->designators ) {
    153                 // Stupid flag variable for development, to be removed
    154                 // bool mutated = false;
    155                 if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) {
    156                     auto candidates = context.symtab.lookupId(designatorName->name);
    157                     // Does not work for the overloading case currently
    158                     // assert( candidates.size() == 1 );
    159                     if ( candidates.size() != 1 ) return mutDecl;
    160                     auto candidate = candidates.at(0);
    161                     if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) {
    162                         // determine that is an enumInst, swap it with its const value
    163                         assert( candidates.size() == 1 );
    164                         const ast::EnumDecl * baseEnum = enumInst->base;
    165                         // Need to iterate over all enum value to find the initializer to swap
    166                         for ( size_t m = 0; m < baseEnum->members.size(); ++m ) {
    167                             const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>();
    168                             if ( baseEnum->members.at(m)->name == designatorName->name ) {
    169                                 assert(mem);
    170                                 newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) );
    171                                 // mutated = true;
    172                                 break;
    173                             }
    174                         }
    175                     } else {
    176                         newDesignators.push_back( des->designators.at(0) );
    177                     }
    178                 } else {
    179                     newDesignators.push_back( des->designators.at(0) );
    180                 }
    181             }           
    182            
    183             newDesignation->designators = newDesignators;
    184             mutListInit = ast::mutate_field_index(mutListInit, &ast::ListInit::designations, k, newDesignation);
    185            
    186         }
    187     }
    188     return mutDecl;
     147                        for ( ast::ptr<ast::Expr> designator : des->designators ) {
     148                                // Stupid flag variable for development, to be removed
     149                                if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) {
     150                                        auto candidates = context.symtab.lookupId(designatorName->name);
     151                                        // Does not work for the overloading case currently
     152                                        // assert( candidates.size() == 1 );
     153                                        if ( candidates.size() != 1 ) return decl;
     154                                        auto candidate = candidates.at(0);
     155                                        if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) {
     156                                                // determine that is an enumInst, swap it with its const value
     157                                                assert( candidates.size() == 1 );
     158                                                const ast::EnumDecl * baseEnum = enumInst->base;
     159                                                // Need to iterate over all enum value to find the initializer to swap
     160                                                for ( size_t m = 0; m < baseEnum->members.size(); ++m ) {
     161                                                        const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>();
     162                                                        if ( baseEnum->members.at(m)->name == designatorName->name ) {
     163                                                                assert( mem );
     164                                                                newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) );
     165                                                                break;
     166                                                        }
     167                                                }
     168                                        } else {
     169                                                newDesignators.push_back( des->designators.at(0) );
     170                                        }
     171                                } else {
     172                                        newDesignators.push_back( des->designators.at(0) );
     173                                }
     174                        }
     175                        newDesignation->designators = newDesignators;
     176                        listInit = ast::mutate_field_index(listInit, &ast::ListInit::designations, k, newDesignation);
     177                }
     178        }
     179        return decl;
    189180}
    190181
    191 }  // namespace ResolvExpr
     182} // namespace ResolvExpr
    192183
    193184// Local Variables: //
  • src/ResolvExpr/Resolver.cc

    r0522ebe ra4da45e  
    5050
    5151namespace ResolvExpr {
    52         namespace {
    53                 /// Finds deleted expressions in an expression tree
    54                 struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> {
    55                         const ast::DeletedExpr * result = nullptr;
    56 
    57                         void previsit( const ast::DeletedExpr * expr ) {
    58                                 if ( result ) { visit_children = false; }
    59                                 else { result = expr; }
     52
     53namespace {
     54        /// Finds deleted expressions in an expression tree
     55        struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> {
     56                const ast::DeletedExpr * result = nullptr;
     57
     58                void previsit( const ast::DeletedExpr * expr ) {
     59                        if ( result ) { visit_children = false; }
     60                        else { result = expr; }
     61                }
     62
     63                void previsit( const ast::Expr * expr ) {
     64                        if ( result ) { visit_children = false; }
     65                        if (expr->inferred.hasParams()) {
     66                                for (auto & imp : expr->inferred.inferParams() ) {
     67                                        imp.second.expr->accept(*visitor);
     68                                }
    6069                        }
    61 
    62                         void previsit( const ast::Expr * expr ) {
    63                                 if ( result ) { visit_children = false; }
    64                                 if (expr->inferred.hasParams()) {
    65                                         for (auto & imp : expr->inferred.inferParams() ) {
    66                                                 imp.second.expr->accept(*visitor);
     70                }
     71        };
     72
     73        struct ResolveDesignators final : public ast::WithShortCircuiting {
     74                ResolveContext& context;
     75                bool result = false;
     76
     77                ResolveDesignators( ResolveContext& _context ): context(_context) {};
     78
     79                void previsit( const ast::Node * ) {
     80                        // short circuit if we already know there are designations
     81                        if ( result ) visit_children = false;
     82                }
     83
     84                void previsit( const ast::Designation * des ) {
     85                        if ( result ) visit_children = false;
     86                        else if ( ! des->designators.empty() ) {
     87                                if ( (des->designators.size() == 1) ) {
     88                                        const ast::Expr * designator = des->designators.at(0);
     89                                        if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) {
     90                                                auto candidates = context.symtab.lookupId(designatorName->name);
     91                                                for ( auto candidate : candidates ) {
     92                                                        if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) {
     93                                                                result = true;
     94                                                                break;
     95                                                        }
     96                                                }
     97                                        }
     98                                }
     99                                visit_children = false;
     100                        }
     101                }
     102        };
     103} // anonymous namespace
     104
     105/// Check if this expression is or includes a deleted expression
     106const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
     107        return ast::Pass<DeleteFinder>::read( expr );
     108}
     109
     110namespace {
     111        /// always-accept candidate filter
     112        bool anyCandidate( const Candidate & ) { return true; }
     113
     114        /// Calls the CandidateFinder and finds the single best candidate
     115        CandidateRef findUnfinishedKindExpression(
     116                const ast::Expr * untyped, const ResolveContext & context, const std::string & kind,
     117                std::function<bool(const Candidate &)> pred = anyCandidate, ResolveMode mode = {}
     118        ) {
     119                if ( ! untyped ) return nullptr;
     120
     121                // xxx - this isn't thread-safe, but should work until we parallelize the resolver
     122                static unsigned recursion_level = 0;
     123
     124                ++recursion_level;
     125                ast::TypeEnvironment env;
     126                CandidateFinder finder( context, env );
     127                finder.allowVoid = true;
     128                finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
     129                --recursion_level;
     130
     131                // produce a filtered list of candidates
     132                CandidateList candidates;
     133                for ( auto & cand : finder.candidates ) {
     134                        if ( pred( *cand ) ) { candidates.emplace_back( cand ); }
     135                }
     136
     137                // produce invalid error if no candidates
     138                if ( candidates.empty() ) {
     139                        SemanticError( untyped,
     140                                toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""),
     141                                "expression: ") );
     142                }
     143
     144                // search for cheapest candidate
     145                CandidateList winners;
     146                bool seen_undeleted = false;
     147                for ( CandidateRef & cand : candidates ) {
     148                        int c = winners.empty() ? -1 : cand->cost.compare( winners.front()->cost );
     149
     150                        if ( c > 0 ) continue;  // skip more expensive than winner
     151
     152                        if ( c < 0 ) {
     153                                // reset on new cheapest
     154                                seen_undeleted = ! findDeletedExpr( cand->expr );
     155                                winners.clear();
     156                        } else /* if ( c == 0 ) */ {
     157                                if ( findDeletedExpr( cand->expr ) ) {
     158                                        // skip deleted expression if already seen one equivalent-cost not
     159                                        if ( seen_undeleted ) continue;
     160                                } else if ( ! seen_undeleted ) {
     161                                        // replace list of equivalent-cost deleted expressions with one non-deleted
     162                                        winners.clear();
     163                                        seen_undeleted = true;
     164                                }
     165                        }
     166
     167                        winners.emplace_back( std::move( cand ) );
     168                }
     169
     170                // promote candidate.cvtCost to .cost
     171                // promoteCvtCost( winners );
     172
     173                // produce ambiguous errors, if applicable
     174                if ( winners.size() != 1 ) {
     175                        std::ostringstream stream;
     176                        stream << "Cannot choose between " << winners.size() << " alternatives for "
     177                                << kind << (kind != "" ? " " : "") << "expression\n";
     178                        ast::print( stream, untyped );
     179                        stream << " Alternatives are:\n";
     180                        print( stream, winners, 1 );
     181                        SemanticError( untyped->location, stream.str() );
     182                }
     183
     184                // single selected choice
     185                CandidateRef & choice = winners.front();
     186
     187                // fail on only expression deleted
     188                if ( ! seen_undeleted ) {
     189                        SemanticError( untyped->location, choice->expr.get(), "Unique best alternative "
     190                        "includes deleted identifier in " );
     191                }
     192
     193                return std::move( choice );
     194        }
     195
     196        /// Strips extraneous casts out of an expression
     197        struct StripCasts final {
     198                const ast::Expr * postvisit( const ast::CastExpr * castExpr ) {
     199                        if (
     200                                castExpr->isGenerated == ast::GeneratedCast
     201                                && typesCompatible( castExpr->arg->result, castExpr->result )
     202                        ) {
     203                                // generated cast is the same type as its argument, remove it after keeping env
     204                                return ast::mutate_field(
     205                                        castExpr->arg.get(), &ast::Expr::env, castExpr->env );
     206                        }
     207                        return castExpr;
     208                }
     209
     210                static void strip( ast::ptr< ast::Expr > & expr ) {
     211                        ast::Pass< StripCasts > stripper;
     212                        expr = expr->accept( stripper );
     213                }
     214        };
     215
     216        /// Swaps argument into expression pointer, saving original environment
     217        void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) {
     218                ast::ptr< ast::TypeSubstitution > env = expr->env;
     219                expr.set_and_mutate( newExpr )->env = env;
     220        }
     221
     222        /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
     223        void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) {
     224                if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) {
     225                        if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) {
     226                                // cast is to the same type as its argument, remove it
     227                                swap_and_save_env( expr, castExpr->arg );
     228                        }
     229                }
     230        }
     231
     232} // anonymous namespace
     233
     234/// Establish post-resolver invariants for expressions
     235void finishExpr(
     236        ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
     237        const ast::TypeSubstitution * oldenv = nullptr
     238) {
     239        // set up new type substitution for expression
     240        ast::ptr< ast::TypeSubstitution > newenv =
     241                 oldenv ? oldenv : new ast::TypeSubstitution{};
     242        env.writeToSubstitution( *newenv.get_and_mutate() );
     243        expr.get_and_mutate()->env = std::move( newenv );
     244        // remove unncecessary casts
     245        StripCasts::strip( expr );
     246}
     247
     248ast::ptr< ast::Expr > resolveInVoidContext(
     249        const ast::Expr * expr, const ResolveContext & context,
     250        ast::TypeEnvironment & env
     251) {
     252        assertf( expr, "expected a non-null expression" );
     253
     254        // set up and resolve expression cast to void
     255        ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
     256        CandidateRef choice = findUnfinishedKindExpression(
     257                untyped, context, "", anyCandidate, ResolveMode::withAdjustment() );
     258
     259        // a cast expression has either 0 or 1 interpretations (by language rules);
     260        // if 0, an exception has already been thrown, and this code will not run
     261        const ast::CastExpr * castExpr = choice->expr.strict_as< ast::CastExpr >();
     262        env = std::move( choice->env );
     263
     264        return castExpr->arg;
     265}
     266
     267/// Resolve `untyped` to the expression whose candidate is the best match for a `void`
     268/// context.
     269ast::ptr< ast::Expr > findVoidExpression(
     270        const ast::Expr * untyped, const ResolveContext & context
     271) {
     272        ast::TypeEnvironment env;
     273        ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env );
     274        finishExpr( newExpr, env, untyped->env );
     275        return newExpr;
     276}
     277
     278namespace {
     279        /// resolve `untyped` to the expression whose candidate satisfies `pred` with the
     280        /// lowest cost, returning the resolved version
     281        ast::ptr< ast::Expr > findKindExpression(
     282                const ast::Expr * untyped, const ResolveContext & context,
     283                std::function<bool(const Candidate &)> pred = anyCandidate,
     284                const std::string & kind = "", ResolveMode mode = {}
     285        ) {
     286                if ( ! untyped ) return {};
     287                CandidateRef choice =
     288                        findUnfinishedKindExpression( untyped, context, kind, pred, mode );
     289                ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
     290                return std::move( choice->expr );
     291        }
     292
     293        /// Resolve `untyped` to the single expression whose candidate is the best match
     294        ast::ptr< ast::Expr > findSingleExpression(
     295                const ast::Expr * untyped, const ResolveContext & context
     296        ) {
     297                Stats::ResolveTime::start( untyped );
     298                auto res = findKindExpression( untyped, context );
     299                Stats::ResolveTime::stop();
     300                return res;
     301        }
     302} // anonymous namespace
     303
     304ast::ptr< ast::Expr > findSingleExpression(
     305        const ast::Expr * untyped, const ast::Type * type,
     306        const ResolveContext & context
     307) {
     308        assert( untyped && type );
     309        ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
     310        ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context );
     311        removeExtraneousCast( newExpr );
     312        return newExpr;
     313}
     314
     315namespace {
     316        bool structOrUnion( const Candidate & i ) {
     317                const ast::Type * t = i.expr->result->stripReferences();
     318                return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );
     319        }
     320        /// Predicate for "Candidate has integral type"
     321        bool hasIntegralType( const Candidate & i ) {
     322                const ast::Type * type = i.expr->result;
     323
     324                if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) {
     325                        return bt->isInteger();
     326                } else if (
     327                        dynamic_cast< const ast::EnumInstType * >( type )
     328                        || dynamic_cast< const ast::ZeroType * >( type )
     329                        || dynamic_cast< const ast::OneType * >( type )
     330                ) {
     331                        return true;
     332                } else return false;
     333        }
     334
     335        /// Resolve `untyped` as an integral expression, returning the resolved version
     336        ast::ptr< ast::Expr > findIntegralExpression(
     337                const ast::Expr * untyped, const ResolveContext & context
     338        ) {
     339                return findKindExpression( untyped, context, hasIntegralType, "condition" );
     340        }
     341
     342        /// check if a type is a character type
     343        bool isCharType( const ast::Type * t ) {
     344                if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) {
     345                        return bt->kind == ast::BasicType::Char
     346                                || bt->kind == ast::BasicType::SignedChar
     347                                || bt->kind == ast::BasicType::UnsignedChar;
     348                }
     349                return false;
     350        }
     351
     352        /// Advance a type itertor to the next mutex parameter
     353        template<typename Iter>
     354        inline bool nextMutex( Iter & it, const Iter & end ) {
     355                while ( it != end && ! (*it)->is_mutex() ) { ++it; }
     356                return it != end;
     357        }
     358}
     359
     360class Resolver final
     361: public ast::WithSymbolTable, public ast::WithGuards,
     362  public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting,
     363  public ast::WithStmtsToAdd<> {
     364
     365        ast::ptr< ast::Type > functionReturn = nullptr;
     366        ast::CurrentObject currentObject;
     367        // for work previously in GenInit
     368        static InitTweak::ManagedTypes managedTypes;
     369        ResolveContext context;
     370
     371        bool inEnumDecl = false;
     372
     373public:
     374        static size_t traceId;
     375        Resolver( const ast::TranslationGlobal & global ) :
     376                ast::WithSymbolTable(ast::SymbolTable::ErrorDetection::ValidateOnAdd),
     377                context{ symtab, global } {}
     378        Resolver( const ResolveContext & context ) :
     379                ast::WithSymbolTable{ context.symtab },
     380                context{ symtab, context.global } {}
     381
     382        const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
     383        const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
     384        const ast::ObjectDecl * previsit( const ast::ObjectDecl * );
     385        void previsit( const ast::AggregateDecl * );
     386        void previsit( const ast::StructDecl * );
     387        void previsit( const ast::EnumDecl * );
     388        const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * );
     389
     390        const ast::ArrayType * previsit( const ast::ArrayType * );
     391        const ast::PointerType * previsit( const ast::PointerType * );
     392
     393        const ast::ExprStmt *        previsit( const ast::ExprStmt * );
     394        const ast::AsmExpr *         previsit( const ast::AsmExpr * );
     395        const ast::AsmStmt *         previsit( const ast::AsmStmt * );
     396        const ast::IfStmt *          previsit( const ast::IfStmt * );
     397        const ast::WhileDoStmt *     previsit( const ast::WhileDoStmt * );
     398        const ast::ForStmt *         previsit( const ast::ForStmt * );
     399        const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
     400        const ast::CaseClause *      previsit( const ast::CaseClause * );
     401        const ast::BranchStmt *      previsit( const ast::BranchStmt * );
     402        const ast::ReturnStmt *      previsit( const ast::ReturnStmt * );
     403        const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
     404        const ast::CatchClause *     previsit( const ast::CatchClause * );
     405        const ast::CatchClause *     postvisit( const ast::CatchClause * );
     406        const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
     407        const ast::WithStmt *        previsit( const ast::WithStmt * );
     408
     409        const ast::SingleInit *      previsit( const ast::SingleInit * );
     410        const ast::ListInit *        previsit( const ast::ListInit * );
     411        const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
     412
     413        void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);
     414
     415        void beginScope() { managedTypes.beginScope(); }
     416        void endScope() { managedTypes.endScope(); }
     417        bool on_error(ast::ptr<ast::Decl> & decl);
     418};
     419// size_t Resolver::traceId = Stats::Heap::new_stacktrace_id("Resolver");
     420
     421InitTweak::ManagedTypes Resolver::managedTypes;
     422
     423void resolve( ast::TranslationUnit& translationUnit ) {
     424        ast::Pass< Resolver >::run( translationUnit, translationUnit.global );
     425}
     426
     427ast::ptr< ast::Init > resolveCtorInit(
     428        const ast::ConstructorInit * ctorInit, const ResolveContext & context
     429) {
     430        assert( ctorInit );
     431        ast::Pass< Resolver > resolver( context );
     432        return ctorInit->accept( resolver );
     433}
     434
     435const ast::Expr * resolveStmtExpr(
     436        const ast::StmtExpr * stmtExpr, const ResolveContext & context
     437) {
     438        assert( stmtExpr );
     439        ast::Pass< Resolver > resolver( context );
     440        auto ret = mutate(stmtExpr->accept(resolver));
     441        strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
     442        return ret;
     443}
     444
     445namespace {
     446        const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) {
     447                std::string name = attr->normalizedName();
     448                if (name == "constructor" || name == "destructor") {
     449                        if (attr->params.size() == 1) {
     450                                auto arg = attr->params.front();
     451                                auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), context );
     452                                auto result = eval(arg);
     453
     454                                auto mutAttr = mutate(attr);
     455                                mutAttr->params.front() = resolved;
     456                                if (! result.hasKnownValue) {
     457                                        SemanticWarning(loc, Warning::GccAttributes,
     458                                                toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
     459                                }
     460                                else {
     461                                        auto priority = result.knownValue;
     462                                        if (priority < 101) {
     463                                                SemanticWarning(loc, Warning::GccAttributes,
     464                                                        toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) );
     465                                        } else if (priority < 201 && ! buildingLibrary()) {
     466                                                SemanticWarning(loc, Warning::GccAttributes,
     467                                                        toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) );
     468                                        }
     469                                }
     470                                return mutAttr;
     471                        } else if (attr->params.size() > 1) {
     472                                SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
     473                        } else {
     474                                SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) );
     475                        }
     476                }
     477                return attr;
     478        }
     479}
     480
     481const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) {
     482        GuardValue( functionReturn );
     483
     484        assert (functionDecl->unique());
     485        if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {
     486                SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");
     487        }
     488
     489        if (!functionDecl->isTypeFixed) {
     490                auto mutDecl = mutate(functionDecl);
     491                auto mutType = mutDecl->type.get_and_mutate();
     492
     493                for (auto & attr: mutDecl->attributes) {
     494                        attr = handleAttribute(mutDecl->location, attr, context );
     495                }
     496
     497                // handle assertions
     498
     499                symtab.enterScope();
     500                mutType->forall.clear();
     501                mutType->assertions.clear();
     502                for (auto & typeParam : mutDecl->type_params) {
     503                        symtab.addType(typeParam);
     504                        mutType->forall.emplace_back(new ast::TypeInstType(typeParam));
     505                }
     506                for (auto & asst : mutDecl->assertions) {
     507                        asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context);
     508                        symtab.addId(asst);
     509                        mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
     510                }
     511
     512                // temporarily adds params to symbol table.
     513                // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)
     514
     515                std::vector<ast::ptr<ast::Type>> paramTypes;
     516                std::vector<ast::ptr<ast::Type>> returnTypes;
     517
     518                for (auto & param : mutDecl->params) {
     519                        param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context);
     520                        symtab.addId(param);
     521                        paramTypes.emplace_back(param->get_type());
     522                }
     523                for (auto & ret : mutDecl->returns) {
     524                        ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context);
     525                        returnTypes.emplace_back(ret->get_type());
     526                }
     527                // since function type in decl is just a view of param types, need to update that as well
     528                mutType->params = std::move(paramTypes);
     529                mutType->returns = std::move(returnTypes);
     530
     531                auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
     532
     533                std::list<ast::ptr<ast::Stmt>> newStmts;
     534                resolveWithExprs (mutDecl->withExprs, newStmts);
     535
     536                if (mutDecl->stmts) {
     537                        auto mutStmt = mutDecl->stmts.get_and_mutate();
     538                        mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));
     539                        mutDecl->stmts = mutStmt;
     540                }
     541
     542                symtab.leaveScope();
     543
     544                mutDecl->type = renamedType;
     545                mutDecl->mangleName = Mangle::mangle(mutDecl);
     546                mutDecl->isTypeFixed = true;
     547                functionDecl = mutDecl;
     548        }
     549        managedTypes.handleDWT(functionDecl);
     550
     551        functionReturn = extractResultType( functionDecl->type );
     552        return functionDecl;
     553}
     554
     555const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) {
     556        // default value expressions have an environment which shouldn't be there and trips up
     557        // later passes.
     558        assert( functionDecl->unique() );
     559        ast::FunctionType * mutType = mutate( functionDecl->type.get() );
     560
     561        for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) {
     562                if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {
     563                        if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) {
     564                                if ( init->value->env == nullptr ) continue;
     565                                // clone initializer minus the initializer environment
     566                                auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() );
     567                                auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() );
     568                                auto mutValue = mutate( mutInit->value.get() );
     569
     570                                mutValue->env = nullptr;
     571                                mutInit->value = mutValue;
     572                                mutParam->init = mutInit;
     573                                mutType->params[i] = mutParam;
     574
     575                                assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env);
     576                        }
     577                }
     578        }
     579        mutate_field(functionDecl, &ast::FunctionDecl::type, mutType);
     580        return functionDecl;
     581}
     582
     583const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) {
     584        // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()],
     585        // class-variable `initContext` is changed multiple times because the LHS is analyzed
     586        // twice. The second analysis changes `initContext` because a function type can contain
     587        // object declarations in the return and parameter types. Therefore each value of
     588        // `initContext` is retained so the type on the first analysis is preserved and used for
     589        // selecting the RHS.
     590        GuardValue( currentObject );
     591
     592        if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) {
     593                // enumerator initializers should not use the enum type to initialize, since the
     594                // enum type is still incomplete at this point. Use `int` instead.
     595
     596                if ( auto enumBase = dynamic_cast< const ast::EnumInstType * >
     597                        ( objectDecl->get_type() )->base->base ) {
     598                        objectDecl = fixObjectType( objectDecl, context );
     599                        currentObject = ast::CurrentObject{
     600                                objectDecl->location,
     601                                enumBase
     602                        };
     603                } else {
     604                        objectDecl = fixObjectType( objectDecl, context );
     605                        currentObject = ast::CurrentObject{
     606                                objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } };
     607                }
     608        } else {
     609                if ( !objectDecl->isTypeFixed ) {
     610                        auto newDecl = fixObjectType(objectDecl, context);
     611                        auto mutDecl = mutate(newDecl);
     612
     613                        // generate CtorInit wrapper when necessary.
     614                        // in certain cases, fixObjectType is called before reaching
     615                        // this object in visitor pass, thus disabling CtorInit codegen.
     616                        // this happens on aggregate members and function parameters.
     617                        if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) {
     618                                // constructed objects cannot be designated
     619                                if ( InitTweak::isDesignated( mutDecl->init ) ) {
     620                                        ast::Pass<ResolveDesignators> res( context );
     621                                        maybe_accept( mutDecl->init.get(), res );
     622                                        if ( !res.core.result ) {
     623                                                SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n"
     624                                                                           "If this is really what you want, initialize with @=." );
     625                                        }
     626                                }
     627                                // constructed objects should not have initializers nested too deeply
     628                                if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );
     629
     630                                mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );
     631                        }
     632
     633                        objectDecl = mutDecl;
     634                }
     635                currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
     636        }
     637
     638        return objectDecl;
     639}
     640
     641void Resolver::previsit( const ast::AggregateDecl * _aggDecl ) {
     642        auto aggDecl = mutate(_aggDecl);
     643        assertf(aggDecl == _aggDecl, "type declarations must be unique");
     644
     645        for (auto & member: aggDecl->members) {
     646                // nested type decls are hoisted already. no need to do anything
     647                if (auto obj = member.as<ast::ObjectDecl>()) {
     648                        member = fixObjectType(obj, context);
     649                }
     650        }
     651}
     652
     653void Resolver::previsit( const ast::StructDecl * structDecl ) {
     654        previsit(static_cast<const ast::AggregateDecl *>(structDecl));
     655        managedTypes.handleStruct(structDecl);
     656}
     657
     658void Resolver::previsit( const ast::EnumDecl * ) {
     659        // in case we decide to allow nested enums
     660        GuardValue( inEnumDecl );
     661        inEnumDecl = true;
     662        // don't need to fix types for enum fields
     663}
     664
     665const ast::StaticAssertDecl * Resolver::previsit(
     666        const ast::StaticAssertDecl * assertDecl
     667) {
     668        return ast::mutate_field(
     669                assertDecl, &ast::StaticAssertDecl::cond,
     670                findIntegralExpression( assertDecl->cond, context ) );
     671}
     672
     673template< typename PtrType >
     674const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) {
     675        if ( type->dimension ) {
     676                const ast::Type * sizeType = context.global.sizeType.get();
     677                ast::ptr< ast::Expr > dimension = findSingleExpression( type->dimension, sizeType, context );
     678                assertf(dimension->env->empty(), "array dimension expr has nonempty env");
     679                dimension.get_and_mutate()->env = nullptr;
     680                ast::mutate_field( type, &PtrType::dimension, dimension );
     681        }
     682        return type;
     683}
     684
     685const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) {
     686        return handlePtrType( at, context );
     687}
     688
     689const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) {
     690        return handlePtrType( pt, context );
     691}
     692
     693const ast::ExprStmt * Resolver::previsit( const ast::ExprStmt * exprStmt ) {
     694        visit_children = false;
     695        assertf( exprStmt->expr, "ExprStmt has null expression in resolver" );
     696
     697        return ast::mutate_field(
     698                exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) );
     699}
     700
     701const ast::AsmExpr * Resolver::previsit( const ast::AsmExpr * asmExpr ) {
     702        visit_children = false;
     703
     704        asmExpr = ast::mutate_field(
     705                asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) );
     706
     707        return asmExpr;
     708}
     709
     710const ast::AsmStmt * Resolver::previsit( const ast::AsmStmt * asmStmt ) {
     711        visitor->maybe_accept( asmStmt, &ast::AsmStmt::input );
     712        visitor->maybe_accept( asmStmt, &ast::AsmStmt::output );
     713        visit_children = false;
     714        return asmStmt;
     715}
     716
     717const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
     718        return ast::mutate_field(
     719                ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) );
     720}
     721
     722const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
     723        return ast::mutate_field(
     724                whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) );
     725}
     726
     727const ast::ForStmt * Resolver::previsit( const ast::ForStmt * forStmt ) {
     728        if ( forStmt->cond ) {
     729                forStmt = ast::mutate_field(
     730                        forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) );
     731        }
     732
     733        if ( forStmt->inc ) {
     734                forStmt = ast::mutate_field(
     735                        forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) );
     736        }
     737
     738        return forStmt;
     739}
     740
     741const ast::SwitchStmt * Resolver::previsit( const ast::SwitchStmt * switchStmt ) {
     742        GuardValue( currentObject );
     743        switchStmt = ast::mutate_field(
     744                switchStmt, &ast::SwitchStmt::cond,
     745                findIntegralExpression( switchStmt->cond, context ) );
     746        currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result };
     747        return switchStmt;
     748}
     749
     750const ast::CaseClause * Resolver::previsit( const ast::CaseClause * caseStmt ) {
     751        if ( caseStmt->cond ) {
     752                std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
     753                assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
     754                        "expression." );
     755
     756                ast::ptr< ast::Expr > untyped =
     757                        new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type };
     758                ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context );
     759
     760                // case condition cannot have a cast in C, so it must be removed here, regardless of
     761                // whether it would perform a conversion.
     762                if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
     763                        swap_and_save_env( newExpr, castExpr->arg );
     764                }
     765
     766                caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr );
     767        }
     768        return caseStmt;
     769}
     770
     771const ast::BranchStmt * Resolver::previsit( const ast::BranchStmt * branchStmt ) {
     772        visit_children = false;
     773        // must resolve the argument of a computed goto
     774        if ( branchStmt->kind == ast::BranchStmt::Goto && branchStmt->computedTarget ) {
     775                // computed goto argument is void*
     776                ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} };
     777                branchStmt = ast::mutate_field(
     778                        branchStmt, &ast::BranchStmt::computedTarget,
     779                        findSingleExpression( branchStmt->computedTarget, target, context ) );
     780        }
     781        return branchStmt;
     782}
     783
     784const ast::ReturnStmt * Resolver::previsit( const ast::ReturnStmt * returnStmt ) {
     785        visit_children = false;
     786        if ( returnStmt->expr ) {
     787                returnStmt = ast::mutate_field(
     788                        returnStmt, &ast::ReturnStmt::expr,
     789                        findSingleExpression( returnStmt->expr, functionReturn, context ) );
     790        }
     791        return returnStmt;
     792}
     793
     794const ast::ThrowStmt * Resolver::previsit( const ast::ThrowStmt * throwStmt ) {
     795        visit_children = false;
     796        if ( throwStmt->expr ) {
     797                const ast::StructDecl * exceptionDecl =
     798                        symtab.lookupStruct( "__cfaehm_base_exception_t" );
     799                assert( exceptionDecl );
     800                ast::ptr< ast::Type > exceptType =
     801                        new ast::PointerType{ new ast::StructInstType{ exceptionDecl } };
     802                throwStmt = ast::mutate_field(
     803                        throwStmt, &ast::ThrowStmt::expr,
     804                        findSingleExpression( throwStmt->expr, exceptType, context ) );
     805        }
     806        return throwStmt;
     807}
     808
     809const ast::CatchClause * Resolver::previsit( const ast::CatchClause * catchClause ) {
     810        // Until we are very sure this invarent (ifs that move between passes have then)
     811        // holds, check it. This allows a check for when to decode the mangling.
     812        if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) {
     813                assert( ifStmt->then );
     814        }
     815        // Encode the catchStmt so the condition can see the declaration.
     816        if ( catchClause->cond ) {
     817                ast::CatchClause * clause = mutate( catchClause );
     818                clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body );
     819                clause->cond = nullptr;
     820                return clause;
     821        }
     822        return catchClause;
     823}
     824
     825const ast::CatchClause * Resolver::postvisit( const ast::CatchClause * catchClause ) {
     826        // Decode the catchStmt so everything is stored properly.
     827        const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>();
     828        if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
     829                assert( ifStmt->cond );
     830                assert( ifStmt->else_ );
     831                ast::CatchClause * clause = ast::mutate( catchClause );
     832                clause->cond = ifStmt->cond;
     833                clause->body = ifStmt->else_;
     834                // ifStmt should be implicately deleted here.
     835                return clause;
     836        }
     837        return catchClause;
     838}
     839
     840const ast::WaitForStmt * Resolver::previsit( const ast::WaitForStmt * stmt ) {
     841        visit_children = false;
     842
     843        // Resolve all clauses first
     844        for ( unsigned i = 0; i < stmt->clauses.size(); ++i ) {
     845                const ast::WaitForClause & clause = *stmt->clauses[i];
     846
     847                ast::TypeEnvironment env;
     848                CandidateFinder funcFinder( context, env );
     849
     850                // Find all candidates for a function in canonical form
     851                funcFinder.find( clause.target, ResolveMode::withAdjustment() );
     852
     853                if ( funcFinder.candidates.empty() ) {
     854                        stringstream ss;
     855                        ss << "Use of undeclared indentifier '";
     856                        ss << clause.target.strict_as< ast::NameExpr >()->name;
     857                        ss << "' in call to waitfor";
     858                        SemanticError( stmt->location, ss.str() );
     859                }
     860
     861                if ( clause.target_args.empty() ) {
     862                        SemanticError( stmt->location,
     863                                "Waitfor clause must have at least one mutex parameter");
     864                }
     865
     866                // Find all alternatives for all arguments in canonical form
     867                std::vector< CandidateFinder > argFinders =
     868                        funcFinder.findSubExprs( clause.target_args );
     869
     870                // List all combinations of arguments
     871                std::vector< CandidateList > possibilities;
     872                combos( argFinders.begin(), argFinders.end(), back_inserter( possibilities ) );
     873
     874                // For every possible function:
     875                // * try matching the arguments to the parameters, not the other way around because
     876                //   more arguments than parameters
     877                CandidateList funcCandidates;
     878                std::vector< CandidateList > argsCandidates;
     879                SemanticErrorException errors;
     880                for ( CandidateRef & func : funcFinder.candidates ) {
     881                        try {
     882                                auto pointerType = dynamic_cast< const ast::PointerType * >(
     883                                        func->expr->result->stripReferences() );
     884                                if ( ! pointerType ) {
     885                                        SemanticError( stmt->location, func->expr->result.get(),
     886                                                "candidate not viable: not a pointer type\n" );
     887                                }
     888
     889                                auto funcType = pointerType->base.as< ast::FunctionType >();
     890                                if ( ! funcType ) {
     891                                        SemanticError( stmt->location, func->expr->result.get(),
     892                                                "candidate not viable: not a function type\n" );
     893                                }
     894
     895                                {
     896                                        auto param    = funcType->params.begin();
     897                                        auto paramEnd = funcType->params.end();
     898
     899                                        if( ! nextMutex( param, paramEnd ) ) {
     900                                                SemanticError( stmt->location, funcType,
     901                                                        "candidate function not viable: no mutex parameters\n");
     902                                        }
     903                                }
     904
     905                                CandidateRef func2{ new Candidate{ *func } };
     906                                // strip reference from function
     907                                func2->expr = referenceToRvalueConversion( func->expr, func2->cost );
     908
     909                                // Each argument must be matched with a parameter of the current candidate
     910                                for ( auto & argsList : possibilities ) {
     911                                        try {
     912                                                // Declare data structures needed for resolution
     913                                                ast::OpenVarSet open;
     914                                                ast::AssertionSet need, have;
     915                                                ast::TypeEnvironment resultEnv{ func->env };
     916                                                // Add all type variables as open so that those not used in the
     917                                                // parameter list are still considered open
     918                                                resultEnv.add( funcType->forall );
     919
     920                                                // load type variables from arguments into one shared space
     921                                                for ( auto & arg : argsList ) {
     922                                                        resultEnv.simpleCombine( arg->env );
     923                                                }
     924
     925                                                // Make sure we don't widen any existing bindings
     926                                                resultEnv.forbidWidening();
     927
     928                                                // Find any unbound type variables
     929                                                resultEnv.extractOpenVars( open );
     930
     931                                                auto param = funcType->params.begin();
     932                                                auto paramEnd = funcType->params.end();
     933
     934                                                unsigned n_mutex_param = 0;
     935
     936                                                // For every argument of its set, check if it matches one of the
     937                                                // parameters. The order is important
     938                                                for ( auto & arg : argsList ) {
     939                                                        // Ignore non-mutex arguments
     940                                                        if ( ! nextMutex( param, paramEnd ) ) {
     941                                                                // We ran out of parameters but still have arguments.
     942                                                                // This function doesn't match
     943                                                                SemanticError( stmt->location, funcType,
     944                                                                        toString("candidate function not viable: too many mutex "
     945                                                                        "arguments, expected ", n_mutex_param, "\n" ) );
     946                                                        }
     947
     948                                                        ++n_mutex_param;
     949
     950                                                        // Check if the argument matches the parameter type in the current scope.
     951                                                        // ast::ptr< ast::Type > paramType = (*param)->get_type();
     952
     953                                                        if (
     954                                                                ! unify(
     955                                                                        arg->expr->result, *param, resultEnv, need, have, open )
     956                                                        ) {
     957                                                                // Type doesn't match
     958                                                                stringstream ss;
     959                                                                ss << "candidate function not viable: no known conversion "
     960                                                                        "from '";
     961                                                                ast::print( ss, *param );
     962                                                                ss << "' to '";
     963                                                                ast::print( ss, arg->expr->result );
     964                                                                ss << "' with env '";
     965                                                                ast::print( ss, resultEnv );
     966                                                                ss << "'\n";
     967                                                                SemanticError( stmt->location, funcType, ss.str() );
     968                                                        }
     969
     970                                                        ++param;
     971                                                }
     972
     973                                                // All arguments match!
     974
     975                                                // Check if parameters are missing
     976                                                if ( nextMutex( param, paramEnd ) ) {
     977                                                        do {
     978                                                                ++n_mutex_param;
     979                                                                ++param;
     980                                                        } while ( nextMutex( param, paramEnd ) );
     981
     982                                                        // We ran out of arguments but still have parameters left; this
     983                                                        // function doesn't match
     984                                                        SemanticError( stmt->location, funcType,
     985                                                                toString( "candidate function not viable: too few mutex "
     986                                                                "arguments, expected ", n_mutex_param, "\n" ) );
     987                                                }
     988
     989                                                // All parameters match!
     990
     991                                                // Finish the expressions to tie in proper environments
     992                                                finishExpr( func2->expr, resultEnv );
     993                                                for ( CandidateRef & arg : argsList ) {
     994                                                        finishExpr( arg->expr, resultEnv );
     995                                                }
     996
     997                                                // This is a match, store it and save it for later
     998                                                funcCandidates.emplace_back( std::move( func2 ) );
     999                                                argsCandidates.emplace_back( std::move( argsList ) );
     1000
     1001                                        } catch ( SemanticErrorException & e ) {
     1002                                                errors.append( e );
     1003                                        }
     1004                                }
     1005                        } catch ( SemanticErrorException & e ) {
     1006                                errors.append( e );
     1007                        }
     1008                }
     1009
     1010                // Make sure correct number of arguments
     1011                if( funcCandidates.empty() ) {
     1012                        SemanticErrorException top( stmt->location,
     1013                                "No alternatives for function in call to waitfor" );
     1014                        top.append( errors );
     1015                        throw top;
     1016                }
     1017
     1018                if( argsCandidates.empty() ) {
     1019                        SemanticErrorException top( stmt->location,
     1020                                "No alternatives for arguments in call to waitfor" );
     1021                        top.append( errors );
     1022                        throw top;
     1023                }
     1024
     1025                if( funcCandidates.size() > 1 ) {
     1026                        SemanticErrorException top( stmt->location,
     1027                                "Ambiguous function in call to waitfor" );
     1028                        top.append( errors );
     1029                        throw top;
     1030                }
     1031                if( argsCandidates.size() > 1 ) {
     1032                        SemanticErrorException top( stmt->location,
     1033                                "Ambiguous arguments in call to waitfor" );
     1034                        top.append( errors );
     1035                        throw top;
     1036                }
     1037                // TODO: need to use findDeletedExpr to ensure no deleted identifiers are used.
     1038
     1039                // build new clause
     1040                auto clause2 = new ast::WaitForClause( clause.location );
     1041
     1042                clause2->target = funcCandidates.front()->expr;
     1043
     1044                clause2->target_args.reserve( clause.target_args.size() );
     1045                const ast::StructDecl * decl_monitor = symtab.lookupStruct( "monitor$" );
     1046                for ( auto arg : argsCandidates.front() ) {
     1047                        const auto & loc = stmt->location;
     1048
     1049                        ast::Expr * init = new ast::CastExpr( loc,
     1050                                new ast::UntypedExpr( loc,
     1051                                        new ast::NameExpr( loc, "get_monitor" ),
     1052                                        { arg->expr }
     1053                                ),
     1054                                new ast::PointerType(
     1055                                        new ast::StructInstType(
     1056                                                decl_monitor
     1057                                        )
     1058                                )
     1059                        );
     1060
     1061                        clause2->target_args.emplace_back( findSingleExpression( init, context ) );
     1062                }
     1063
     1064                // Resolve the conditions as if it were an IfStmt, statements normally
     1065                clause2->when_cond = findSingleExpression( clause.when_cond, context );
     1066                clause2->stmt = clause.stmt->accept( *visitor );
     1067
     1068                // set results into stmt
     1069                auto n = mutate( stmt );
     1070                n->clauses[i] = clause2;
     1071                stmt = n;
     1072        }
     1073
     1074        if ( stmt->timeout_stmt ) {
     1075                // resolve the timeout as a size_t, the conditions like IfStmt, and stmts normally
     1076                ast::ptr< ast::Type > target =
     1077                        new ast::BasicType{ ast::BasicType::LongLongUnsignedInt };
     1078                auto timeout_time = findSingleExpression( stmt->timeout_time, target, context );
     1079                auto timeout_cond = findSingleExpression( stmt->timeout_cond, context );
     1080                auto timeout_stmt = stmt->timeout_stmt->accept( *visitor );
     1081
     1082                // set results into stmt
     1083                auto n = mutate( stmt );
     1084                n->timeout_time = std::move( timeout_time );
     1085                n->timeout_cond = std::move( timeout_cond );
     1086                n->timeout_stmt = std::move( timeout_stmt );
     1087                stmt = n;
     1088        }
     1089
     1090        if ( stmt->else_stmt ) {
     1091                // resolve the condition like IfStmt, stmts normally
     1092                auto else_cond = findSingleExpression( stmt->else_cond, context );
     1093                auto else_stmt = stmt->else_stmt->accept( *visitor );
     1094
     1095                // set results into stmt
     1096                auto n = mutate( stmt );
     1097                n->else_cond = std::move( else_cond );
     1098                n->else_stmt = std::move( else_stmt );
     1099                stmt = n;
     1100        }
     1101
     1102        return stmt;
     1103}
     1104
     1105const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) {
     1106        auto mutStmt = mutate(withStmt);
     1107        resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
     1108        return mutStmt;
     1109}
     1110
     1111void Resolver::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {
     1112        for (auto & expr : exprs) {
     1113                // only struct- and union-typed expressions are viable candidates
     1114                expr = findKindExpression( expr, context, structOrUnion, "with expression" );
     1115
     1116                // if with expression might be impure, create a temporary so that it is evaluated once
     1117                if ( Tuples::maybeImpure( expr ) ) {
     1118                        static UniqueName tmpNamer( "_with_tmp_" );
     1119                        const CodeLocation loc = expr->location;
     1120                        auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );
     1121                        expr = new ast::VariableExpr( loc, tmp );
     1122                        stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );
     1123                        if ( InitTweak::isConstructable( tmp->type ) ) {
     1124                                // generate ctor/dtor and resolve them
     1125                                tmp->init = InitTweak::genCtorInit( loc, tmp );
     1126                        }
     1127                        // since tmp is freshly created, this should modify tmp in-place
     1128                        tmp->accept( *visitor );
     1129                } else if (expr->env && expr->env->empty()) {
     1130                        expr = ast::mutate_field(expr.get(), &ast::Expr::env, nullptr);
     1131                }
     1132        }
     1133}
     1134
     1135const ast::SingleInit * Resolver::previsit( const ast::SingleInit * singleInit ) {
     1136        visit_children = false;
     1137        // resolve initialization using the possibilities as determined by the `currentObject`
     1138        // cursor.
     1139        ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{
     1140                singleInit->location, singleInit->value, currentObject.getOptions() };
     1141        ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context );
     1142        const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
     1143
     1144        // move cursor to the object that is actually initialized
     1145        currentObject.setNext( initExpr->designation );
     1146
     1147        // discard InitExpr wrapper and retain relevant pieces.
     1148        // `initExpr` may have inferred params in the case where the expression specialized a
     1149        // function pointer, and newExpr may already have inferParams of its own, so a simple
     1150        // swap is not sufficient
     1151        ast::Expr::InferUnion inferred = initExpr->inferred;
     1152        swap_and_save_env( newExpr, initExpr->expr );
     1153        newExpr.get_and_mutate()->inferred.splice( std::move(inferred) );
     1154
     1155        // get the actual object's type (may not exactly match what comes back from the resolver
     1156        // due to conversions)
     1157        const ast::Type * initContext = currentObject.getCurrentType();
     1158
     1159        removeExtraneousCast( newExpr );
     1160
     1161        // check if actual object's type is char[]
     1162        if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) {
     1163                if ( isCharType( at->base ) ) {
     1164                        // check if the resolved type is char*
     1165                        if ( auto pt = newExpr->result.as< ast::PointerType >() ) {
     1166                                if ( isCharType( pt->base ) ) {
     1167                                        // strip cast if we're initializing a char[] with a char*
     1168                                        // e.g. char x[] = "hello"
     1169                                        if ( auto ce = newExpr.as< ast::CastExpr >() ) {
     1170                                                swap_and_save_env( newExpr, ce->arg );
    671171                                        }
    681172                                }
    691173                        }
    70                 };
    71 
    72                 struct ResolveDesignators final : public ast::WithShortCircuiting {
    73                         ResolveContext& context;
    74                         bool result = false;
    75 
    76                         ResolveDesignators( ResolveContext& _context ): context{_context} {};
    77 
    78                         void previsit( const ast::Node * ) {
    79                                 // short circuit if we already know there are designations
    80                                 if ( result ) visit_children = false;
    81                         }
    82 
    83                         void previsit( const ast::Designation * des ) {
    84                                 if ( result ) visit_children = false;
    85                                 else if ( ! des->designators.empty() ) {
    86                                         if ( (des->designators.size() == 1) ) {
    87                                                 const ast::Expr * designator = des->designators.at(0);
    88                                                 if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) {
    89                                                         auto candidates = context.symtab.lookupId(designatorName->name);
    90                                                         for ( auto candidate : candidates ) {
    91                                                                 if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) {
    92                                                                         result = true;
    93                                                                         break;
    94                                                                 }
    95                                                         }
    96                                                 }
    97                                         }
    98                                         visit_children = false;
    99                                 }
    100                         }
    101                 };
    102         } // anonymous namespace
    103         /// Check if this expression is or includes a deleted expression
    104         const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
    105                 return ast::Pass<DeleteFinder>::read( expr );
    106         }
    107 
    108         namespace {
    109                 /// always-accept candidate filter
    110                 bool anyCandidate( const Candidate & ) { return true; }
    111 
    112                 /// Calls the CandidateFinder and finds the single best candidate
    113                 CandidateRef findUnfinishedKindExpression(
    114                         const ast::Expr * untyped, const ResolveContext & context, const std::string & kind,
    115                         std::function<bool(const Candidate &)> pred = anyCandidate, ResolveMode mode = {}
    116                 ) {
    117                         if ( ! untyped ) return nullptr;
    118 
    119                         // xxx - this isn't thread-safe, but should work until we parallelize the resolver
    120                         static unsigned recursion_level = 0;
    121 
    122                         ++recursion_level;
    123                         ast::TypeEnvironment env;
    124                         CandidateFinder finder( context, env );
    125                         finder.allowVoid = true;
    126                         finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode );
    127                         --recursion_level;
    128 
    129                         // produce a filtered list of candidates
    130                         CandidateList candidates;
    131                         for ( auto & cand : finder.candidates ) {
    132                                 if ( pred( *cand ) ) { candidates.emplace_back( cand ); }
    133                         }
    134 
    135                         // produce invalid error if no candidates
    136                         if ( candidates.empty() ) {
    137                                 SemanticError( untyped,
    138                                         toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""),
    139                                         "expression: ") );
    140                         }
    141 
    142                         // search for cheapest candidate
    143                         CandidateList winners;
    144                         bool seen_undeleted = false;
    145                         for ( CandidateRef & cand : candidates ) {
    146                                 int c = winners.empty() ? -1 : cand->cost.compare( winners.front()->cost );
    147 
    148                                 if ( c > 0 ) continue;  // skip more expensive than winner
    149 
    150                                 if ( c < 0 ) {
    151                                         // reset on new cheapest
    152                                         seen_undeleted = ! findDeletedExpr( cand->expr );
    153                                         winners.clear();
    154                                 } else /* if ( c == 0 ) */ {
    155                                         if ( findDeletedExpr( cand->expr ) ) {
    156                                                 // skip deleted expression if already seen one equivalent-cost not
    157                                                 if ( seen_undeleted ) continue;
    158                                         } else if ( ! seen_undeleted ) {
    159                                                 // replace list of equivalent-cost deleted expressions with one non-deleted
    160                                                 winners.clear();
    161                                                 seen_undeleted = true;
    162                                         }
    163                                 }
    164 
    165                                 winners.emplace_back( std::move( cand ) );
    166                         }
    167 
    168                         // promote candidate.cvtCost to .cost
    169                         // promoteCvtCost( winners );
    170 
    171                         // produce ambiguous errors, if applicable
    172                         if ( winners.size() != 1 ) {
    173                                 std::ostringstream stream;
    174                                 stream << "Cannot choose between " << winners.size() << " alternatives for "
    175                                         << kind << (kind != "" ? " " : "") << "expression\n";
    176                                 ast::print( stream, untyped );
    177                                 stream << " Alternatives are:\n";
    178                                 print( stream, winners, 1 );
    179                                 SemanticError( untyped->location, stream.str() );
    180                         }
    181 
    182                         // single selected choice
    183                         CandidateRef & choice = winners.front();
    184 
    185                         // fail on only expression deleted
    186                         if ( ! seen_undeleted ) {
    187                                 SemanticError( untyped->location, choice->expr.get(), "Unique best alternative "
    188                                 "includes deleted identifier in " );
    189                         }
    190 
    191                         return std::move( choice );
    192                 }
    193 
    194                 /// Strips extraneous casts out of an expression
    195                 struct StripCasts final {
    196                         const ast::Expr * postvisit( const ast::CastExpr * castExpr ) {
    197                                 if (
    198                                         castExpr->isGenerated == ast::GeneratedCast
    199                                         && typesCompatible( castExpr->arg->result, castExpr->result )
    200                                 ) {
    201                                         // generated cast is the same type as its argument, remove it after keeping env
    202                                         return ast::mutate_field(
    203                                                 castExpr->arg.get(), &ast::Expr::env, castExpr->env );
    204                                 }
    205                                 return castExpr;
    206                         }
    207 
    208                         static void strip( ast::ptr< ast::Expr > & expr ) {
    209                                 ast::Pass< StripCasts > stripper;
    210                                 expr = expr->accept( stripper );
    211                         }
    212                 };
    213 
    214                 /// Swaps argument into expression pointer, saving original environment
    215                 void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) {
    216                         ast::ptr< ast::TypeSubstitution > env = expr->env;
    217                         expr.set_and_mutate( newExpr )->env = env;
    218                 }
    219 
    220                 /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts)
    221                 void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) {
    222                         if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) {
    223                                 if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) {
    224                                         // cast is to the same type as its argument, remove it
    225                                         swap_and_save_env( expr, castExpr->arg );
    226                                 }
    227                         }
    228                 }
    229 
    230 
    231         } // anonymous namespace
    232 /// Establish post-resolver invariants for expressions
    233                 void finishExpr(
    234                         ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env,
    235                         const ast::TypeSubstitution * oldenv = nullptr
    236                 ) {
    237                         // set up new type substitution for expression
    238                         ast::ptr< ast::TypeSubstitution > newenv =
    239                                  oldenv ? oldenv : new ast::TypeSubstitution{};
    240                         env.writeToSubstitution( *newenv.get_and_mutate() );
    241                         expr.get_and_mutate()->env = std::move( newenv );
    242                         // remove unncecessary casts
    243                         StripCasts::strip( expr );
    244                 }
    245 
    246         ast::ptr< ast::Expr > resolveInVoidContext(
    247                 const ast::Expr * expr, const ResolveContext & context,
    248                 ast::TypeEnvironment & env
    249         ) {
    250                 assertf( expr, "expected a non-null expression" );
    251 
    252                 // set up and resolve expression cast to void
    253                 ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr };
    254                 CandidateRef choice = findUnfinishedKindExpression(
    255                         untyped, context, "", anyCandidate, ResolveMode::withAdjustment() );
    256 
    257                 // a cast expression has either 0 or 1 interpretations (by language rules);
    258                 // if 0, an exception has already been thrown, and this code will not run
    259                 const ast::CastExpr * castExpr = choice->expr.strict_as< ast::CastExpr >();
    260                 env = std::move( choice->env );
    261 
    262                 return castExpr->arg;
    263         }
    264 
    265         /// Resolve `untyped` to the expression whose candidate is the best match for a `void`
    266                 /// context.
    267                 ast::ptr< ast::Expr > findVoidExpression(
    268                         const ast::Expr * untyped, const ResolveContext & context
    269                 ) {
    270                         ast::TypeEnvironment env;
    271                         ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env );
    272                         finishExpr( newExpr, env, untyped->env );
    273                         return newExpr;
    274                 }
    275 
    276         namespace {
    277 
    278 
    279                 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the
    280                 /// lowest cost, returning the resolved version
    281                 ast::ptr< ast::Expr > findKindExpression(
    282                         const ast::Expr * untyped, const ResolveContext & context,
    283                         std::function<bool(const Candidate &)> pred = anyCandidate,
    284                         const std::string & kind = "", ResolveMode mode = {}
    285                 ) {
    286                         if ( ! untyped ) return {};
    287                         CandidateRef choice =
    288                                 findUnfinishedKindExpression( untyped, context, kind, pred, mode );
    289                         ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env );
    290                         return std::move( choice->expr );
    291                 }
    292 
    293                 /// Resolve `untyped` to the single expression whose candidate is the best match
    294                 ast::ptr< ast::Expr > findSingleExpression(
    295                         const ast::Expr * untyped, const ResolveContext & context
    296                 ) {
    297                         Stats::ResolveTime::start( untyped );
    298                         auto res = findKindExpression( untyped, context );
    299                         Stats::ResolveTime::stop();
    300                         return res;
    301                 }
    302         } // anonymous namespace
    303 
    304         ast::ptr< ast::Expr > findSingleExpression(
    305                 const ast::Expr * untyped, const ast::Type * type,
    306                 const ResolveContext & context
    307         ) {
    308                 assert( untyped && type );
    309                 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type };
    310                 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context );
    311                 removeExtraneousCast( newExpr );
    312                 return newExpr;
    313         }
    314 
    315         namespace {
    316                 bool structOrUnion( const Candidate & i ) {
    317                         const ast::Type * t = i.expr->result->stripReferences();
    318                         return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t );
    319                 }
    320                 /// Predicate for "Candidate has integral type"
    321                 bool hasIntegralType( const Candidate & i ) {
    322                         const ast::Type * type = i.expr->result;
    323 
    324                         if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) {
    325                                 return bt->isInteger();
    326                         } else if (
    327                                 dynamic_cast< const ast::EnumInstType * >( type )
    328                                 || dynamic_cast< const ast::ZeroType * >( type )
    329                                 || dynamic_cast< const ast::OneType * >( type )
    330                         ) {
    331                                 return true;
    332                         } else return false;
    333                 }
    334 
    335                 /// Resolve `untyped` as an integral expression, returning the resolved version
    336                 ast::ptr< ast::Expr > findIntegralExpression(
    337                         const ast::Expr * untyped, const ResolveContext & context
    338                 ) {
    339                         return findKindExpression( untyped, context, hasIntegralType, "condition" );
    340                 }
    341 
    342                 /// check if a type is a character type
    343                 bool isCharType( const ast::Type * t ) {
    344                         if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) {
    345                                 return bt->kind == ast::BasicType::Char
    346                                         || bt->kind == ast::BasicType::SignedChar
    347                                         || bt->kind == ast::BasicType::UnsignedChar;
    348                         }
     1174                }
     1175        }
     1176
     1177        // move cursor to next object in preparation for next initializer
     1178        currentObject.increment();
     1179
     1180        // set initializer expression to resolved expression
     1181        return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) );
     1182}
     1183
     1184const ast::ListInit * Resolver::previsit( const ast::ListInit * listInit ) {
     1185        // move cursor into brace-enclosed initializer-list
     1186        currentObject.enterListInit( listInit->location );
     1187
     1188        assert( listInit->designations.size() == listInit->initializers.size() );
     1189        for ( unsigned i = 0; i < listInit->designations.size(); ++i ) {
     1190                // iterate designations and initializers in pairs, moving the cursor to the current
     1191                // designated object and resolving the initializer against that object
     1192                listInit = ast::mutate_field_index(
     1193                        listInit, &ast::ListInit::designations, i,
     1194                        currentObject.findNext( listInit->designations[i] ) );
     1195                listInit = ast::mutate_field_index(
     1196                        listInit, &ast::ListInit::initializers, i,
     1197                        listInit->initializers[i]->accept( *visitor ) );
     1198        }
     1199
     1200        // move cursor out of brace-enclosed initializer-list
     1201        currentObject.exitListInit();
     1202
     1203        visit_children = false;
     1204        return listInit;
     1205}
     1206
     1207const ast::ConstructorInit * Resolver::previsit( const ast::ConstructorInit * ctorInit ) {
     1208        visitor->maybe_accept( ctorInit, &ast::ConstructorInit::ctor );
     1209        visitor->maybe_accept( ctorInit, &ast::ConstructorInit::dtor );
     1210
     1211        // found a constructor - can get rid of C-style initializer
     1212        // xxx - Rob suggests this field is dead code
     1213        ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr );
     1214
     1215        // intrinsic single-parameter constructors and destructors do nothing. Since this was
     1216        // implicitly generated, there's no way for it to have side effects, so get rid of it to
     1217        // clean up generated code
     1218        if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) {
     1219                ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::ctor, nullptr );
     1220        }
     1221        if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->dtor ) ) {
     1222                ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::dtor, nullptr );
     1223        }
     1224
     1225        return ctorInit;
     1226}
     1227
     1228// suppress error on autogen functions and mark invalid autogen as deleted.
     1229bool Resolver::on_error(ast::ptr<ast::Decl> & decl) {
     1230        if (auto functionDecl = decl.as<ast::FunctionDecl>()) {
     1231                // xxx - can intrinsic gen ever fail?
     1232                if (functionDecl->linkage == ast::Linkage::AutoGen) {
     1233                        auto mutDecl = mutate(functionDecl);
     1234                        mutDecl->isDeleted = true;
     1235                        mutDecl->stmts = nullptr;
     1236                        decl = mutDecl;
    3491237                        return false;
    3501238                }
    351 
    352                 /// Advance a type itertor to the next mutex parameter
    353                 template<typename Iter>
    354                 inline bool nextMutex( Iter & it, const Iter & end ) {
    355                         while ( it != end && ! (*it)->is_mutex() ) { ++it; }
    356                         return it != end;
    357                 }
    358         }
    359 
    360         class Resolver final
    361         : public ast::WithSymbolTable, public ast::WithGuards,
    362           public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting,
    363           public ast::WithStmtsToAdd<> {
    364 
    365                 ast::ptr< ast::Type > functionReturn = nullptr;
    366                 ast::CurrentObject currentObject;
    367                 // for work previously in GenInit
    368                 static InitTweak::ManagedTypes managedTypes;
    369                 ResolveContext context;
    370 
    371                 bool inEnumDecl = false;
    372 
    373         public:
    374                 static size_t traceId;
    375                 Resolver( const ast::TranslationGlobal & global ) :
    376                         ast::WithSymbolTable(ast::SymbolTable::ErrorDetection::ValidateOnAdd),
    377                         context{ symtab, global } {}
    378                 Resolver( const ResolveContext & context ) :
    379                         ast::WithSymbolTable{ context.symtab },
    380                         context{ symtab, context.global } {}
    381 
    382                 const ast::FunctionDecl * previsit( const ast::FunctionDecl * );
    383                 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
    384                 const ast::ObjectDecl * previsit( const ast::ObjectDecl * );
    385                 void previsit( const ast::AggregateDecl * );
    386                 void previsit( const ast::StructDecl * );
    387                 void previsit( const ast::EnumDecl * );
    388                 const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * );
    389 
    390                 const ast::ArrayType * previsit( const ast::ArrayType * );
    391                 const ast::PointerType * previsit( const ast::PointerType * );
    392 
    393                 const ast::ExprStmt *        previsit( const ast::ExprStmt * );
    394                 const ast::AsmExpr *         previsit( const ast::AsmExpr * );
    395                 const ast::AsmStmt *         previsit( const ast::AsmStmt * );
    396                 const ast::IfStmt *          previsit( const ast::IfStmt * );
    397                 const ast::WhileDoStmt *     previsit( const ast::WhileDoStmt * );
    398                 const ast::ForStmt *         previsit( const ast::ForStmt * );
    399                 const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
    400                 const ast::CaseClause *      previsit( const ast::CaseClause * );
    401                 const ast::BranchStmt *      previsit( const ast::BranchStmt * );
    402                 const ast::ReturnStmt *      previsit( const ast::ReturnStmt * );
    403                 const ast::ThrowStmt *       previsit( const ast::ThrowStmt * );
    404                 const ast::CatchClause *     previsit( const ast::CatchClause * );
    405                 const ast::CatchClause *     postvisit( const ast::CatchClause * );
    406                 const ast::WaitForStmt *     previsit( const ast::WaitForStmt * );
    407                 const ast::WithStmt *        previsit( const ast::WithStmt * );
    408 
    409                 const ast::SingleInit *      previsit( const ast::SingleInit * );
    410                 const ast::ListInit *        previsit( const ast::ListInit * );
    411                 const ast::ConstructorInit * previsit( const ast::ConstructorInit * );
    412 
    413                 void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd);
    414 
    415                 void beginScope() { managedTypes.beginScope(); }
    416                 void endScope() { managedTypes.endScope(); }
    417                 bool on_error(ast::ptr<ast::Decl> & decl);
    418         };
    419         // size_t Resolver::traceId = Stats::Heap::new_stacktrace_id("Resolver");
    420 
    421         InitTweak::ManagedTypes Resolver::managedTypes;
    422 
    423         void resolve( ast::TranslationUnit& translationUnit ) {
    424                 ast::Pass< Resolver >::run( translationUnit, translationUnit.global );
    425         }
    426 
    427         ast::ptr< ast::Init > resolveCtorInit(
    428                 const ast::ConstructorInit * ctorInit, const ResolveContext & context
    429         ) {
    430                 assert( ctorInit );
    431                 ast::Pass< Resolver > resolver( context );
    432                 return ctorInit->accept( resolver );
    433         }
    434 
    435         const ast::Expr * resolveStmtExpr(
    436                 const ast::StmtExpr * stmtExpr, const ResolveContext & context
    437         ) {
    438                 assert( stmtExpr );
    439                 ast::Pass< Resolver > resolver( context );
    440                 auto ret = mutate(stmtExpr->accept(resolver));
    441                 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult();
    442                 return ret;
    443         }
    444 
    445         namespace {
    446                 const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) {
    447                         std::string name = attr->normalizedName();
    448                         if (name == "constructor" || name == "destructor") {
    449                                 if (attr->params.size() == 1) {
    450                                         auto arg = attr->params.front();
    451                                         auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), context );
    452                                         auto result = eval(arg);
    453 
    454                                         auto mutAttr = mutate(attr);
    455                                         mutAttr->params.front() = resolved;
    456                                         if (! result.hasKnownValue) {
    457                                                 SemanticWarning(loc, Warning::GccAttributes,
    458                                                         toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) );
    459                                         }
    460                                         else {
    461                                                 auto priority = result.knownValue;
    462                                                 if (priority < 101) {
    463                                                         SemanticWarning(loc, Warning::GccAttributes,
    464                                                                 toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) );
    465                                                 } else if (priority < 201 && ! buildingLibrary()) {
    466                                                         SemanticWarning(loc, Warning::GccAttributes,
    467                                                                 toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) );
    468                                                 }
    469                                         }
    470                                         return mutAttr;
    471                                 } else if (attr->params.size() > 1) {
    472                                         SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
    473                                 } else {
    474                                         SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) );
    475                                 }
    476                         }
    477                         return attr;
    478                 }
    479         }
    480 
    481         const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) {
    482                 GuardValue( functionReturn );
    483 
    484                 assert (functionDecl->unique());
    485                 if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) {
    486                         SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations");
    487                 }
    488 
    489                 if (!functionDecl->isTypeFixed) {
    490                         auto mutDecl = mutate(functionDecl);
    491                         auto mutType = mutDecl->type.get_and_mutate();
    492 
    493                         for (auto & attr: mutDecl->attributes) {
    494                                 attr = handleAttribute(mutDecl->location, attr, context );
    495                         }
    496 
    497                         // handle assertions
    498 
    499                         symtab.enterScope();
    500                         mutType->forall.clear();
    501                         mutType->assertions.clear();
    502                         for (auto & typeParam : mutDecl->type_params) {
    503                                 symtab.addType(typeParam);
    504                                 mutType->forall.emplace_back(new ast::TypeInstType(typeParam));
    505                         }
    506                         for (auto & asst : mutDecl->assertions) {
    507                                 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context);
    508                                 symtab.addId(asst);
    509                                 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst));
    510                         }
    511 
    512                         // temporarily adds params to symbol table.
    513                         // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl)
    514 
    515                         std::vector<ast::ptr<ast::Type>> paramTypes;
    516                         std::vector<ast::ptr<ast::Type>> returnTypes;
    517 
    518                         for (auto & param : mutDecl->params) {
    519                                 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context);
    520                                 symtab.addId(param);
    521                                 paramTypes.emplace_back(param->get_type());
    522                         }
    523                         for (auto & ret : mutDecl->returns) {
    524                                 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context);
    525                                 returnTypes.emplace_back(ret->get_type());
    526                         }
    527                         // since function type in decl is just a view of param types, need to update that as well
    528                         mutType->params = std::move(paramTypes);
    529                         mutType->returns = std::move(returnTypes);
    530 
    531                         auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID));
    532 
    533                         std::list<ast::ptr<ast::Stmt>> newStmts;
    534                         resolveWithExprs (mutDecl->withExprs, newStmts);
    535 
    536                         if (mutDecl->stmts) {
    537                                 auto mutStmt = mutDecl->stmts.get_and_mutate();
    538                                 mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts));
    539                                 mutDecl->stmts = mutStmt;
    540                         }
    541 
    542                         symtab.leaveScope();
    543 
    544                         mutDecl->type = renamedType;
    545                         mutDecl->mangleName = Mangle::mangle(mutDecl);
    546                         mutDecl->isTypeFixed = true;
    547                         functionDecl = mutDecl;
    548                 }
    549                 managedTypes.handleDWT(functionDecl);
    550 
    551                 functionReturn = extractResultType( functionDecl->type );
    552                 return functionDecl;
    553         }
    554 
    555         const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) {
    556                 // default value expressions have an environment which shouldn't be there and trips up
    557                 // later passes.
    558                 assert( functionDecl->unique() );
    559                 ast::FunctionType * mutType = mutate( functionDecl->type.get() );
    560 
    561                 for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) {
    562                         if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) {
    563                                 if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) {
    564                                         if ( init->value->env == nullptr ) continue;
    565                                         // clone initializer minus the initializer environment
    566                                         auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() );
    567                                         auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() );
    568                                         auto mutValue = mutate( mutInit->value.get() );
    569 
    570                                         mutValue->env = nullptr;
    571                                         mutInit->value = mutValue;
    572                                         mutParam->init = mutInit;
    573                                         mutType->params[i] = mutParam;
    574 
    575                                         assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env);
    576                                 }
    577                         }
    578                 }
    579                 mutate_field(functionDecl, &ast::FunctionDecl::type, mutType);
    580                 return functionDecl;
    581         }
    582 
    583         const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) {
    584                 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()],
    585                 // class-variable `initContext` is changed multiple times because the LHS is analyzed
    586                 // twice. The second analysis changes `initContext` because a function type can contain
    587                 // object declarations in the return and parameter types. Therefore each value of
    588                 // `initContext` is retained so the type on the first analysis is preserved and used for
    589                 // selecting the RHS.
    590                 GuardValue( currentObject );
    591 
    592                 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) {
    593                         // enumerator initializers should not use the enum type to initialize, since the
    594                         // enum type is still incomplete at this point. Use `int` instead.
    595 
    596                         if ( auto enumBase = dynamic_cast< const ast::EnumInstType * >
    597                                 ( objectDecl->get_type() )->base->base ) {
    598                                 objectDecl = fixObjectType( objectDecl, context );
    599                                 currentObject = ast::CurrentObject{
    600                                         objectDecl->location,
    601                                         enumBase
    602                                 };
    603                         } else {
    604                                 objectDecl = fixObjectType( objectDecl, context );
    605                                 currentObject = ast::CurrentObject{
    606                                         objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } };
    607                         }
    608 
    609                 }
    610                 else {
    611                         if ( !objectDecl->isTypeFixed ) {
    612                                 auto newDecl = fixObjectType(objectDecl, context);
    613                                 auto mutDecl = mutate(newDecl);
    614 
    615                                 // generate CtorInit wrapper when necessary.
    616                                 // in certain cases, fixObjectType is called before reaching
    617                                 // this object in visitor pass, thus disabling CtorInit codegen.
    618                                 // this happens on aggregate members and function parameters.
    619                                 if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) {
    620                                         // constructed objects cannot be designated
    621                                         if ( InitTweak::isDesignated( mutDecl->init ) ) {
    622                                                 ast::Pass<ResolveDesignators> res( context );
    623                                                 maybe_accept( mutDecl->init.get(), res );
    624                                                 if ( !res.core.result ) {
    625                                                         SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n"
    626                                                                                    "If this is really what you want, initialize with @=." );
    627                                                 }
    628                                         }
    629                                         // constructed objects should not have initializers nested too deeply
    630                                         if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " );
    631 
    632                                         mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl );
    633                                 }
    634 
    635                                 objectDecl = mutDecl;
    636                         }
    637                         currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() };
    638                 }
    639 
    640                 return objectDecl;
    641         }
    642 
    643         void Resolver::previsit( const ast::AggregateDecl * _aggDecl ) {
    644                 auto aggDecl = mutate(_aggDecl);
    645                 assertf(aggDecl == _aggDecl, "type declarations must be unique");
    646 
    647                 for (auto & member: aggDecl->members) {
    648                         // nested type decls are hoisted already. no need to do anything
    649                         if (auto obj = member.as<ast::ObjectDecl>()) {
    650                                 member = fixObjectType(obj, context);
    651                         }
    652                 }
    653         }
    654 
    655         void Resolver::previsit( const ast::StructDecl * structDecl ) {
    656                 previsit(static_cast<const ast::AggregateDecl *>(structDecl));
    657                 managedTypes.handleStruct(structDecl);
    658         }
    659 
    660         void Resolver::previsit( const ast::EnumDecl * ) {
    661                 // in case we decide to allow nested enums
    662                 GuardValue( inEnumDecl );
    663                 inEnumDecl = true;
    664                 // don't need to fix types for enum fields
    665         }
    666 
    667         const ast::StaticAssertDecl * Resolver::previsit(
    668                 const ast::StaticAssertDecl * assertDecl
    669         ) {
    670                 return ast::mutate_field(
    671                         assertDecl, &ast::StaticAssertDecl::cond,
    672                         findIntegralExpression( assertDecl->cond, context ) );
    673         }
    674 
    675         template< typename PtrType >
    676         const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) {
    677                 if ( type->dimension ) {
    678                         const ast::Type * sizeType = context.global.sizeType.get();
    679                         ast::ptr< ast::Expr > dimension = findSingleExpression( type->dimension, sizeType, context );
    680                         assertf(dimension->env->empty(), "array dimension expr has nonempty env");
    681                         dimension.get_and_mutate()->env = nullptr;
    682                         ast::mutate_field( type, &PtrType::dimension, dimension );
    683                 }
    684                 return type;
    685         }
    686 
    687         const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) {
    688                 return handlePtrType( at, context );
    689         }
    690 
    691         const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) {
    692                 return handlePtrType( pt, context );
    693         }
    694 
    695         const ast::ExprStmt * Resolver::previsit( const ast::ExprStmt * exprStmt ) {
    696                 visit_children = false;
    697                 assertf( exprStmt->expr, "ExprStmt has null expression in resolver" );
    698 
    699                 return ast::mutate_field(
    700                         exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) );
    701         }
    702 
    703         const ast::AsmExpr * Resolver::previsit( const ast::AsmExpr * asmExpr ) {
    704                 visit_children = false;
    705 
    706                 asmExpr = ast::mutate_field(
    707                         asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) );
    708 
    709                 return asmExpr;
    710         }
    711 
    712         const ast::AsmStmt * Resolver::previsit( const ast::AsmStmt * asmStmt ) {
    713                 visitor->maybe_accept( asmStmt, &ast::AsmStmt::input );
    714                 visitor->maybe_accept( asmStmt, &ast::AsmStmt::output );
    715                 visit_children = false;
    716                 return asmStmt;
    717         }
    718 
    719         const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
    720                 return ast::mutate_field(
    721                         ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) );
    722         }
    723 
    724         const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
    725                 return ast::mutate_field(
    726                         whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) );
    727         }
    728 
    729         const ast::ForStmt * Resolver::previsit( const ast::ForStmt * forStmt ) {
    730                 if ( forStmt->cond ) {
    731                         forStmt = ast::mutate_field(
    732                                 forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) );
    733                 }
    734 
    735                 if ( forStmt->inc ) {
    736                         forStmt = ast::mutate_field(
    737                                 forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) );
    738                 }
    739 
    740                 return forStmt;
    741         }
    742 
    743         const ast::SwitchStmt * Resolver::previsit( const ast::SwitchStmt * switchStmt ) {
    744                 GuardValue( currentObject );
    745                 switchStmt = ast::mutate_field(
    746                         switchStmt, &ast::SwitchStmt::cond,
    747                         findIntegralExpression( switchStmt->cond, context ) );
    748                 currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result };
    749                 return switchStmt;
    750         }
    751 
    752         const ast::CaseClause * Resolver::previsit( const ast::CaseClause * caseStmt ) {
    753                 if ( caseStmt->cond ) {
    754                         std::deque< ast::InitAlternative > initAlts = currentObject.getOptions();
    755                         assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral "
    756                                 "expression." );
    757 
    758                         ast::ptr< ast::Expr > untyped =
    759                                 new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type };
    760                         ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context );
    761 
    762                         // case condition cannot have a cast in C, so it must be removed here, regardless of
    763                         // whether it would perform a conversion.
    764                         if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) {
    765                                 swap_and_save_env( newExpr, castExpr->arg );
    766                         }
    767 
    768                         caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr );
    769                 }
    770                 return caseStmt;
    771         }
    772 
    773         const ast::BranchStmt * Resolver::previsit( const ast::BranchStmt * branchStmt ) {
    774                 visit_children = false;
    775                 // must resolve the argument of a computed goto
    776                 if ( branchStmt->kind == ast::BranchStmt::Goto && branchStmt->computedTarget ) {
    777                         // computed goto argument is void*
    778                         ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} };
    779                         branchStmt = ast::mutate_field(
    780                                 branchStmt, &ast::BranchStmt::computedTarget,
    781                                 findSingleExpression( branchStmt->computedTarget, target, context ) );
    782                 }
    783                 return branchStmt;
    784         }
    785 
    786         const ast::ReturnStmt * Resolver::previsit( const ast::ReturnStmt * returnStmt ) {
    787                 visit_children = false;
    788                 if ( returnStmt->expr ) {
    789                         returnStmt = ast::mutate_field(
    790                                 returnStmt, &ast::ReturnStmt::expr,
    791                                 findSingleExpression( returnStmt->expr, functionReturn, context ) );
    792                 }
    793                 return returnStmt;
    794         }
    795 
    796         const ast::ThrowStmt * Resolver::previsit( const ast::ThrowStmt * throwStmt ) {
    797                 visit_children = false;
    798                 if ( throwStmt->expr ) {
    799                         const ast::StructDecl * exceptionDecl =
    800                                 symtab.lookupStruct( "__cfaehm_base_exception_t" );
    801                         assert( exceptionDecl );
    802                         ast::ptr< ast::Type > exceptType =
    803                                 new ast::PointerType{ new ast::StructInstType{ exceptionDecl } };
    804                         throwStmt = ast::mutate_field(
    805                                 throwStmt, &ast::ThrowStmt::expr,
    806                                 findSingleExpression( throwStmt->expr, exceptType, context ) );
    807                 }
    808                 return throwStmt;
    809         }
    810 
    811         const ast::CatchClause * Resolver::previsit( const ast::CatchClause * catchClause ) {
    812                 // Until we are very sure this invarent (ifs that move between passes have then)
    813                 // holds, check it. This allows a check for when to decode the mangling.
    814                 if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) {
    815                         assert( ifStmt->then );
    816                 }
    817                 // Encode the catchStmt so the condition can see the declaration.
    818                 if ( catchClause->cond ) {
    819                         ast::CatchClause * clause = mutate( catchClause );
    820                         clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body );
    821                         clause->cond = nullptr;
    822                         return clause;
    823                 }
    824                 return catchClause;
    825         }
    826 
    827         const ast::CatchClause * Resolver::postvisit( const ast::CatchClause * catchClause ) {
    828                 // Decode the catchStmt so everything is stored properly.
    829                 const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>();
    830                 if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
    831                         assert( ifStmt->cond );
    832                         assert( ifStmt->else_ );
    833                         ast::CatchClause * clause = ast::mutate( catchClause );
    834                         clause->cond = ifStmt->cond;
    835                         clause->body = ifStmt->else_;
    836                         // ifStmt should be implicately deleted here.
    837                         return clause;
    838                 }
    839                 return catchClause;
    840         }
    841 
    842         const ast::WaitForStmt * Resolver::previsit( const ast::WaitForStmt * stmt ) {
    843                 visit_children = false;
    844 
    845                 // Resolve all clauses first
    846                 for ( unsigned i = 0; i < stmt->clauses.size(); ++i ) {
    847                         const ast::WaitForClause & clause = *stmt->clauses[i];
    848 
    849                         ast::TypeEnvironment env;
    850                         CandidateFinder funcFinder( context, env );
    851 
    852                         // Find all candidates for a function in canonical form
    853                         funcFinder.find( clause.target, ResolveMode::withAdjustment() );
    854 
    855                         if ( funcFinder.candidates.empty() ) {
    856                                 stringstream ss;
    857                                 ss << "Use of undeclared indentifier '";
    858                                 ss << clause.target.strict_as< ast::NameExpr >()->name;
    859                                 ss << "' in call to waitfor";
    860                                 SemanticError( stmt->location, ss.str() );
    861                         }
    862 
    863                         if ( clause.target_args.empty() ) {
    864                                 SemanticError( stmt->location,
    865                                         "Waitfor clause must have at least one mutex parameter");
    866                         }
    867 
    868                         // Find all alternatives for all arguments in canonical form
    869                         std::vector< CandidateFinder > argFinders =
    870                                 funcFinder.findSubExprs( clause.target_args );
    871 
    872                         // List all combinations of arguments
    873                         std::vector< CandidateList > possibilities;
    874                         combos( argFinders.begin(), argFinders.end(), back_inserter( possibilities ) );
    875 
    876                         // For every possible function:
    877                         // * try matching the arguments to the parameters, not the other way around because
    878                         //   more arguments than parameters
    879                         CandidateList funcCandidates;
    880                         std::vector< CandidateList > argsCandidates;
    881                         SemanticErrorException errors;
    882                         for ( CandidateRef & func : funcFinder.candidates ) {
    883                                 try {
    884                                         auto pointerType = dynamic_cast< const ast::PointerType * >(
    885                                                 func->expr->result->stripReferences() );
    886                                         if ( ! pointerType ) {
    887                                                 SemanticError( stmt->location, func->expr->result.get(),
    888                                                         "candidate not viable: not a pointer type\n" );
    889                                         }
    890 
    891                                         auto funcType = pointerType->base.as< ast::FunctionType >();
    892                                         if ( ! funcType ) {
    893                                                 SemanticError( stmt->location, func->expr->result.get(),
    894                                                         "candidate not viable: not a function type\n" );
    895                                         }
    896 
    897                                         {
    898                                                 auto param    = funcType->params.begin();
    899                                                 auto paramEnd = funcType->params.end();
    900 
    901                                                 if( ! nextMutex( param, paramEnd ) ) {
    902                                                         SemanticError( stmt->location, funcType,
    903                                                                 "candidate function not viable: no mutex parameters\n");
    904                                                 }
    905                                         }
    906 
    907                                         CandidateRef func2{ new Candidate{ *func } };
    908                                         // strip reference from function
    909                                         func2->expr = referenceToRvalueConversion( func->expr, func2->cost );
    910 
    911                                         // Each argument must be matched with a parameter of the current candidate
    912                                         for ( auto & argsList : possibilities ) {
    913                                                 try {
    914                                                         // Declare data structures needed for resolution
    915                                                         ast::OpenVarSet open;
    916                                                         ast::AssertionSet need, have;
    917                                                         ast::TypeEnvironment resultEnv{ func->env };
    918                                                         // Add all type variables as open so that those not used in the
    919                                                         // parameter list are still considered open
    920                                                         resultEnv.add( funcType->forall );
    921 
    922                                                         // load type variables from arguments into one shared space
    923                                                         for ( auto & arg : argsList ) {
    924                                                                 resultEnv.simpleCombine( arg->env );
    925                                                         }
    926 
    927                                                         // Make sure we don't widen any existing bindings
    928                                                         resultEnv.forbidWidening();
    929 
    930                                                         // Find any unbound type variables
    931                                                         resultEnv.extractOpenVars( open );
    932 
    933                                                         auto param = funcType->params.begin();
    934                                                         auto paramEnd = funcType->params.end();
    935 
    936                                                         unsigned n_mutex_param = 0;
    937 
    938                                                         // For every argument of its set, check if it matches one of the
    939                                                         // parameters. The order is important
    940                                                         for ( auto & arg : argsList ) {
    941                                                                 // Ignore non-mutex arguments
    942                                                                 if ( ! nextMutex( param, paramEnd ) ) {
    943                                                                         // We ran out of parameters but still have arguments.
    944                                                                         // This function doesn't match
    945                                                                         SemanticError( stmt->location, funcType,
    946                                                                                 toString("candidate function not viable: too many mutex "
    947                                                                                 "arguments, expected ", n_mutex_param, "\n" ) );
    948                                                                 }
    949 
    950                                                                 ++n_mutex_param;
    951 
    952                                                                 // Check if the argument matches the parameter type in the current scope.
    953                                                                 // ast::ptr< ast::Type > paramType = (*param)->get_type();
    954 
    955                                                                 if (
    956                                                                         ! unify(
    957                                                                                 arg->expr->result, *param, resultEnv, need, have, open )
    958                                                                 ) {
    959                                                                         // Type doesn't match
    960                                                                         stringstream ss;
    961                                                                         ss << "candidate function not viable: no known conversion "
    962                                                                                 "from '";
    963                                                                         ast::print( ss, *param );
    964                                                                         ss << "' to '";
    965                                                                         ast::print( ss, arg->expr->result );
    966                                                                         ss << "' with env '";
    967                                                                         ast::print( ss, resultEnv );
    968                                                                         ss << "'\n";
    969                                                                         SemanticError( stmt->location, funcType, ss.str() );
    970                                                                 }
    971 
    972                                                                 ++param;
    973                                                         }
    974 
    975                                                         // All arguments match!
    976 
    977                                                         // Check if parameters are missing
    978                                                         if ( nextMutex( param, paramEnd ) ) {
    979                                                                 do {
    980                                                                         ++n_mutex_param;
    981                                                                         ++param;
    982                                                                 } while ( nextMutex( param, paramEnd ) );
    983 
    984                                                                 // We ran out of arguments but still have parameters left; this
    985                                                                 // function doesn't match
    986                                                                 SemanticError( stmt->location, funcType,
    987                                                                         toString( "candidate function not viable: too few mutex "
    988                                                                         "arguments, expected ", n_mutex_param, "\n" ) );
    989                                                         }
    990 
    991                                                         // All parameters match!
    992 
    993                                                         // Finish the expressions to tie in proper environments
    994                                                         finishExpr( func2->expr, resultEnv );
    995                                                         for ( CandidateRef & arg : argsList ) {
    996                                                                 finishExpr( arg->expr, resultEnv );
    997                                                         }
    998 
    999                                                         // This is a match, store it and save it for later
    1000                                                         funcCandidates.emplace_back( std::move( func2 ) );
    1001                                                         argsCandidates.emplace_back( std::move( argsList ) );
    1002 
    1003                                                 } catch ( SemanticErrorException & e ) {
    1004                                                         errors.append( e );
    1005                                                 }
    1006                                         }
    1007                                 } catch ( SemanticErrorException & e ) {
    1008                                         errors.append( e );
    1009                                 }
    1010                         }
    1011 
    1012                         // Make sure correct number of arguments
    1013                         if( funcCandidates.empty() ) {
    1014                                 SemanticErrorException top( stmt->location,
    1015                                         "No alternatives for function in call to waitfor" );
    1016                                 top.append( errors );
    1017                                 throw top;
    1018                         }
    1019 
    1020                         if( argsCandidates.empty() ) {
    1021                                 SemanticErrorException top( stmt->location,
    1022                                         "No alternatives for arguments in call to waitfor" );
    1023                                 top.append( errors );
    1024                                 throw top;
    1025                         }
    1026 
    1027                         if( funcCandidates.size() > 1 ) {
    1028                                 SemanticErrorException top( stmt->location,
    1029                                         "Ambiguous function in call to waitfor" );
    1030                                 top.append( errors );
    1031                                 throw top;
    1032                         }
    1033                         if( argsCandidates.size() > 1 ) {
    1034                                 SemanticErrorException top( stmt->location,
    1035                                         "Ambiguous arguments in call to waitfor" );
    1036                                 top.append( errors );
    1037                                 throw top;
    1038                         }
    1039                         // TODO: need to use findDeletedExpr to ensure no deleted identifiers are used.
    1040 
    1041                         // build new clause
    1042                         auto clause2 = new ast::WaitForClause( clause.location );
    1043 
    1044                         clause2->target = funcCandidates.front()->expr;
    1045 
    1046                         clause2->target_args.reserve( clause.target_args.size() );
    1047                         const ast::StructDecl * decl_monitor = symtab.lookupStruct( "monitor$" );
    1048                         for ( auto arg : argsCandidates.front() ) {
    1049                                 const auto & loc = stmt->location;
    1050 
    1051                                 ast::Expr * init = new ast::CastExpr( loc,
    1052                                         new ast::UntypedExpr( loc,
    1053                                                 new ast::NameExpr( loc, "get_monitor" ),
    1054                                                 { arg->expr }
    1055                                         ),
    1056                                         new ast::PointerType(
    1057                                                 new ast::StructInstType(
    1058                                                         decl_monitor
    1059                                                 )
    1060                                         )
    1061                                 );
    1062 
    1063                                 clause2->target_args.emplace_back( findSingleExpression( init, context ) );
    1064                         }
    1065 
    1066                         // Resolve the conditions as if it were an IfStmt, statements normally
    1067                         clause2->when_cond = findSingleExpression( clause.when_cond, context );
    1068                         clause2->stmt = clause.stmt->accept( *visitor );
    1069 
    1070                         // set results into stmt
    1071                         auto n = mutate( stmt );
    1072                         n->clauses[i] = clause2;
    1073                         stmt = n;
    1074                 }
    1075 
    1076                 if ( stmt->timeout_stmt ) {
    1077                         // resolve the timeout as a size_t, the conditions like IfStmt, and stmts normally
    1078                         ast::ptr< ast::Type > target =
    1079                                 new ast::BasicType{ ast::BasicType::LongLongUnsignedInt };
    1080                         auto timeout_time = findSingleExpression( stmt->timeout_time, target, context );
    1081                         auto timeout_cond = findSingleExpression( stmt->timeout_cond, context );
    1082                         auto timeout_stmt = stmt->timeout_stmt->accept( *visitor );
    1083 
    1084                         // set results into stmt
    1085                         auto n = mutate( stmt );
    1086                         n->timeout_time = std::move( timeout_time );
    1087                         n->timeout_cond = std::move( timeout_cond );
    1088                         n->timeout_stmt = std::move( timeout_stmt );
    1089                         stmt = n;
    1090                 }
    1091 
    1092                 if ( stmt->else_stmt ) {
    1093                         // resolve the condition like IfStmt, stmts normally
    1094                         auto else_cond = findSingleExpression( stmt->else_cond, context );
    1095                         auto else_stmt = stmt->else_stmt->accept( *visitor );
    1096 
    1097                         // set results into stmt
    1098                         auto n = mutate( stmt );
    1099                         n->else_cond = std::move( else_cond );
    1100                         n->else_stmt = std::move( else_stmt );
    1101                         stmt = n;
    1102                 }
    1103 
    1104                 return stmt;
    1105         }
    1106 
    1107         const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) {
    1108                 auto mutStmt = mutate(withStmt);
    1109                 resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
    1110                 return mutStmt;
    1111         }
    1112 
    1113         void Resolver::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) {
    1114                 for (auto & expr : exprs) {
    1115                         // only struct- and union-typed expressions are viable candidates
    1116                         expr = findKindExpression( expr, context, structOrUnion, "with expression" );
    1117 
    1118                         // if with expression might be impure, create a temporary so that it is evaluated once
    1119                         if ( Tuples::maybeImpure( expr ) ) {
    1120                                 static UniqueName tmpNamer( "_with_tmp_" );
    1121                                 const CodeLocation loc = expr->location;
    1122                                 auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) );
    1123                                 expr = new ast::VariableExpr( loc, tmp );
    1124                                 stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) );
    1125                                 if ( InitTweak::isConstructable( tmp->type ) ) {
    1126                                         // generate ctor/dtor and resolve them
    1127                                         tmp->init = InitTweak::genCtorInit( loc, tmp );
    1128                                 }
    1129                                 // since tmp is freshly created, this should modify tmp in-place
    1130                                 tmp->accept( *visitor );
    1131                         }
    1132                         else if (expr->env && expr->env->empty()) {
    1133                                 expr = ast::mutate_field(expr.get(), &ast::Expr::env, nullptr);
    1134                         }
    1135                 }
    1136         }
    1137 
    1138 
    1139         const ast::SingleInit * Resolver::previsit( const ast::SingleInit * singleInit ) {
    1140                 visit_children = false;
    1141                 // resolve initialization using the possibilities as determined by the `currentObject`
    1142                 // cursor.
    1143                 ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{
    1144                         singleInit->location, singleInit->value, currentObject.getOptions() };
    1145                 ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context );
    1146                 const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >();
    1147 
    1148                 // move cursor to the object that is actually initialized
    1149                 currentObject.setNext( initExpr->designation );
    1150 
    1151                 // discard InitExpr wrapper and retain relevant pieces.
    1152                 // `initExpr` may have inferred params in the case where the expression specialized a
    1153                 // function pointer, and newExpr may already have inferParams of its own, so a simple
    1154                 // swap is not sufficient
    1155                 ast::Expr::InferUnion inferred = initExpr->inferred;
    1156                 swap_and_save_env( newExpr, initExpr->expr );
    1157                 newExpr.get_and_mutate()->inferred.splice( std::move(inferred) );
    1158 
    1159                 // get the actual object's type (may not exactly match what comes back from the resolver
    1160                 // due to conversions)
    1161                 const ast::Type * initContext = currentObject.getCurrentType();
    1162 
    1163                 removeExtraneousCast( newExpr );
    1164 
    1165                 // check if actual object's type is char[]
    1166                 if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) {
    1167                         if ( isCharType( at->base ) ) {
    1168                                 // check if the resolved type is char*
    1169                                 if ( auto pt = newExpr->result.as< ast::PointerType >() ) {
    1170                                         if ( isCharType( pt->base ) ) {
    1171                                                 // strip cast if we're initializing a char[] with a char*
    1172                                                 // e.g. char x[] = "hello"
    1173                                                 if ( auto ce = newExpr.as< ast::CastExpr >() ) {
    1174                                                         swap_and_save_env( newExpr, ce->arg );
    1175                                                 }
    1176                                         }
    1177                                 }
    1178                         }
    1179                 }
    1180 
    1181                 // move cursor to next object in preparation for next initializer
    1182                 currentObject.increment();
    1183 
    1184                 // set initializer expression to resolved expression
    1185                 return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) );
    1186         }
    1187 
    1188         const ast::ListInit * Resolver::previsit( const ast::ListInit * listInit ) {
    1189                 // move cursor into brace-enclosed initializer-list
    1190                 currentObject.enterListInit( listInit->location );
    1191 
    1192                 assert( listInit->designations.size() == listInit->initializers.size() );
    1193                 for ( unsigned i = 0; i < listInit->designations.size(); ++i ) {
    1194                         // iterate designations and initializers in pairs, moving the cursor to the current
    1195                         // designated object and resolving the initializer against that object
    1196                         listInit = ast::mutate_field_index(
    1197                                 listInit, &ast::ListInit::designations, i,
    1198                                 currentObject.findNext( listInit->designations[i] ) );
    1199                         listInit = ast::mutate_field_index(
    1200                                 listInit, &ast::ListInit::initializers, i,
    1201                                 listInit->initializers[i]->accept( *visitor ) );
    1202                 }
    1203 
    1204                 // move cursor out of brace-enclosed initializer-list
    1205                 currentObject.exitListInit();
    1206 
    1207                 visit_children = false;
    1208                 return listInit;
    1209         }
    1210 
    1211         const ast::ConstructorInit * Resolver::previsit( const ast::ConstructorInit * ctorInit ) {
    1212                 visitor->maybe_accept( ctorInit, &ast::ConstructorInit::ctor );
    1213                 visitor->maybe_accept( ctorInit, &ast::ConstructorInit::dtor );
    1214 
    1215                 // found a constructor - can get rid of C-style initializer
    1216                 // xxx - Rob suggests this field is dead code
    1217                 ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr );
    1218 
    1219                 // intrinsic single-parameter constructors and destructors do nothing. Since this was
    1220                 // implicitly generated, there's no way for it to have side effects, so get rid of it to
    1221                 // clean up generated code
    1222                 if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) {
    1223                         ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::ctor, nullptr );
    1224                 }
    1225                 if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->dtor ) ) {
    1226                         ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::dtor, nullptr );
    1227                 }
    1228 
    1229                 return ctorInit;
    1230         }
    1231 
    1232         // suppress error on autogen functions and mark invalid autogen as deleted.
    1233         bool Resolver::on_error(ast::ptr<ast::Decl> & decl) {
    1234                 if (auto functionDecl = decl.as<ast::FunctionDecl>()) {
    1235                         // xxx - can intrinsic gen ever fail?
    1236                         if (functionDecl->linkage == ast::Linkage::AutoGen) {
    1237                                 auto mutDecl = mutate(functionDecl);
    1238                                 mutDecl->isDeleted = true;
    1239                                 mutDecl->stmts = nullptr;
    1240                                 decl = mutDecl;
    1241                                 return false;
    1242                         }
    1243                 }
    1244                 return true;
    1245         }
     1239        }
     1240        return true;
     1241}
    12461242
    12471243} // namespace ResolvExpr
  • src/SymTab/GenImplicitCall.cpp

    r0522ebe ra4da45e  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // GenImplicitCall.hpp --
     7// GenImplicitCall.cpp -- Generate code for implicit operator calls.
    88//
    99// Author           : Andrew Beach
     
    3131namespace {
    3232
    33 template< typename OutIter >
     33using Inserter = std::back_insert_iterator<std::list<ast::ptr<ast::Stmt>>>;
     34
    3435ast::ptr< ast::Stmt > genCall(
    3536        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    36         const CodeLocation & loc, const std::string & fname, OutIter && out,
     37        const CodeLocation & loc, const std::string & fname, Inserter && out,
    3738        const ast::Type * type, const ast::Type * addCast, LoopDirection forward = LoopForward );
    3839
     
    4142/// optionally returns a statement which must be inserted prior to the containing loop, if
    4243/// there is one
    43 template< typename OutIter >
    4444ast::ptr< ast::Stmt > genScalarCall(
    4545        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    46         const CodeLocation & loc, std::string fname, OutIter && out, const ast::Type * type,
     46        const CodeLocation & loc, std::string fname, Inserter && out, const ast::Type * type,
    4747        const ast::Type * addCast = nullptr
    4848) {
     
    9797/// Store in out a loop which calls fname on each element of the array with srcParam and
    9898/// dstParam as arguments. If forward is true, loop goes from 0 to N-1, else N-1 to 0
    99 template< typename OutIter >
    10099void genArrayCall(
    101100        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    102         const CodeLocation & loc, const std::string & fname, OutIter && out,
     101        const CodeLocation & loc, const std::string & fname, Inserter && out,
    103102        const ast::ArrayType * array, const ast::Type * addCast = nullptr,
    104103        LoopDirection forward = LoopForward
     
    166165}
    167166
    168 template< typename OutIter >
    169167ast::ptr< ast::Stmt > genCall(
    170168        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    171         const CodeLocation & loc, const std::string & fname, OutIter && out,
     169        const CodeLocation & loc, const std::string & fname, Inserter && out,
    172170        const ast::Type * type, const ast::Type * addCast, LoopDirection forward
    173171) {
    174172        if ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
    175173                genArrayCall(
    176                         srcParam, dstParam, loc, fname, std::forward< OutIter >(out), at, addCast,
     174                        srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), at, addCast,
    177175                        forward );
    178176                return {};
    179177        } else {
    180178                return genScalarCall(
    181                         srcParam, dstParam, loc, fname, std::forward< OutIter >( out ), type, addCast );
     179                        srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), type, addCast );
    182180        }
    183181}
     
    185183} // namespace
    186184
    187 ast::ptr< ast::Stmt > genImplicitCall(
     185const ast::Stmt * genImplicitCall(
    188186        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    189187        const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj,
     
    191189) {
    192190        // unnamed bit fields are not copied as they cannot be accessed
    193         if ( isUnnamedBitfield( obj ) ) return {};
     191        if ( isUnnamedBitfield( obj ) ) return nullptr;
    194192
    195193        ast::ptr< ast::Type > addCast;
     
    199197        }
    200198
    201         std::vector< ast::ptr< ast::Stmt > > stmts;
     199        std::list< ast::ptr< ast::Stmt > > stmts;
    202200        genCall(
    203201                srcParam, dstParam, loc, fname, back_inserter( stmts ), obj->type, addCast, forward );
    204202
    205         if ( stmts.empty() ) {
    206                 return {};
    207         } else if ( stmts.size() == 1 ) {
    208                 const ast::Stmt * callStmt = stmts.front();
    209                 if ( addCast ) {
    210                         // implicitly generated ctor/dtor calls should be wrapped so that later passes are
    211                         // aware they were generated.
    212                         callStmt = new ast::ImplicitCtorDtorStmt( callStmt->location, callStmt );
    213                 }
    214                 return callStmt;
    215         } else {
    216                 assert( false );
    217                 return {};
    218         }
     203        if ( stmts.empty() ) return nullptr;
     204        assert( stmts.size() == 1 );
     205
     206        const ast::Stmt * callStmt = stmts.front().release();
     207        // Implicitly generated ctor/dtor calls should be wrapped so that
     208        // later passes are aware they were generated.
     209        if ( addCast ) {
     210                callStmt = new ast::ImplicitCtorDtorStmt( callStmt->location, callStmt );
     211        }
     212        return callStmt;
    219213}
    220214
     
    226220// compile-command: "make install" //
    227221// End: //
    228 
    229 
  • src/SymTab/GenImplicitCall.hpp

    r0522ebe ra4da45e  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // GenImplicitCall.hpp --
     7// GenImplicitCall.hpp -- Generate code for implicit operator calls.
    88//
    99// Author           : Andrew Beach
     
    2525/// Returns a generated call expression to function fname with srcParam and
    2626/// dstParam. Intended to be used with generated ?=?, ?{}, and ^?{} calls.
    27 ast::ptr<ast::Stmt> genImplicitCall(
     27const ast::Stmt * genImplicitCall(
    2828        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    2929        const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj,
  • src/Validate/Autogen.cpp

    r0522ebe ra4da45e  
    133133        /// Generates a single struct member operation.
    134134        /// (constructor call, destructor call, assignment call)
    135         // This is managed because it uses another helper that returns a ast::ptr.
    136         ast::ptr<ast::Stmt> makeMemberOp(
     135        const ast::Stmt * makeMemberOp(
    137136                const CodeLocation& location,
    138137                const ast::ObjectDecl * dstParam, const ast::Expr * src,
     
    525524}
    526525
    527 ast::ptr<ast::Stmt> StructFuncGenerator::makeMemberOp(
     526const ast::Stmt * StructFuncGenerator::makeMemberOp(
    528527                const CodeLocation& location, const ast::ObjectDecl * dstParam,
    529528                const ast::Expr * src, const ast::ObjectDecl * field,
     
    540539                )
    541540        );
    542         auto stmt = genImplicitCall(
     541        const ast::Stmt * stmt = genImplicitCall(
    543542                srcParam, dstSelect, location, func->name,
    544543                field, direction
     
    598597                        location, field, new ast::VariableExpr( location, srcParam )
    599598                ) : nullptr;
    600                 ast::ptr<ast::Stmt> stmt =
     599                const ast::Stmt * stmt =
    601600                        makeMemberOp( location, dstParam, srcSelect, field, func, direction );
    602601
    603602                if ( nullptr != stmt ) {
    604                         stmts->kids.push_back( stmt );
     603                        stmts->kids.emplace_back( stmt );
    605604                }
    606605        }
     
    623622        for ( auto param = params.begin() + 1 ; current != end ; ++current ) {
    624623                const ast::ptr<ast::Decl> & member = *current;
    625                 ast::ptr<ast::Stmt> stmt = nullptr;
     624                const ast::Stmt * stmt = nullptr;
    626625                auto field = member.as<ast::ObjectDecl>();
    627626                // Not sure why it could be null.
     
    641640
    642641                if ( nullptr != stmt ) {
    643                         stmts->kids.push_back( stmt );
     642                        stmts->kids.emplace_back( stmt );
    644643                }
    645644        }
  • src/main.cc

    r0522ebe ra4da45e  
    181181
    182182static void _Signal(struct sigaction & act, int sig, int flags ) {
     183        sigemptyset( &act.sa_mask );
    183184        act.sa_flags = flags;
    184185
  • tests/.expect/attributes.arm64.txt

    r0522ebe ra4da45e  
    66
    77}
    8 struct __anonymous0 {
     8struct __attribute__ ((unused)) __anonymous0 {
    99};
    1010static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1);
  • tests/.expect/attributes.x64.txt

    r0522ebe ra4da45e  
    66
    77}
    8 struct __anonymous0 {
     8struct __attribute__ ((unused)) __anonymous0 {
    99};
    1010static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1);
  • tests/.expect/attributes.x86.txt

    r0522ebe ra4da45e  
    66
    77}
    8 struct __anonymous0 {
     8struct __attribute__ ((unused)) __anonymous0 {
    99};
    1010static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1);
  • tests/collections/.expect/string-istream-manip.txt

    r0522ebe ra4da45e  
    707014
    717115
     7216 get this line
     7317 @# this line 1)-{}
     7418 @# this line 1)-{}
     7519 abc
     7620 abc 
     7721  d d
     78
     79d
     8022              ZC44%
     8123              ZC44%
    72821 yyyyyyyyyyyyyyyyyyyy
    73832 abcxxx
     
    859514
    869615
     9716 get this line
     9817 @# this line 1)-{}
     9918 @# this line 1)-{}
     10019 abc
     10120 abc 
     10221  d d
     103
     104d
     10522              ZC44%
     10623              ZC44%
  • tests/collections/.in/string-istream-manip.txt

    r0522ebe ra4da45e  
    3838aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww
    3939uuuuu
     40get this line
     41@# this line 1)-{}%
     42@# this line 2)-{}%
     43    "abc"
     44'abc  '
     45{ d d
     46
     47d }
     48X               ZC44%Y
     49X               ZC55%Y
    4050abc
    4151cccccb
     
    4555aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww
    4656uuuuu
     57get this line
     58@# this line 1)-{}%
     59@# this line 2)-{}%
     60    "abc"
     61'abc  '
     62{ d d
     63
     64d }
     65X               ZC44%Y
     66X               ZC55%Y
  • tests/collections/string-istream-manip.cfa

    r0522ebe ra4da45e  
    1010// The test cases that use plainjane(-) are exercising the via-manipulator code path,
    1111// just with trivial manipulation.
    12 static _Istream_Sstr plainjane( string     & s )  { return (_Istream_Sstr)@{  s, {{0p}, -1, {.flags.rwd : false}} }; }
     12static _Istream_Sstr plainjane( string & s )  { return (_Istream_Sstr)@{  s, {{0p}, -1, {.flags.rwd : false}} }; }
    1313static _Istream_Rstr plainjane( string_res & s )  { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.rwd : false}} }; }
    1414
    1515static void forceStringHeapFreeSpaceTo(int desiredSize) {
    16     for (1_000_000) {
    17         string x = "a";
    18         (void)x;
    19       if (desiredSize == DEBUG_string_bytes_avail_until_gc(DEBUG_string_heap())) return;
    20     }
    21     sout | "Unable to force size" | desiredSize | "in 1,000,000 tries";
     16        for (1_000_000) {
     17                string x = "a";
     18                (void)x;
     19                if (desiredSize == DEBUG_string_bytes_avail_until_gc(DEBUG_string_heap())) return;
     20        }
     21        sout | "Unable to force size" | desiredSize | "in 1,000,000 tries";
    2222}
    2323
    2424int main() {
    25     // These "pre" cases deal with issues analogous to the "pre" cases of io/manipulatorsInput.
    26     // The acceptance criterion is simpler but driving the cases is harder.
    27     // The tests just read strings and echo what they read; acceptance of simple echoing assures
    28     // no spurious splitting merging.
    29     // The lengths of the strings are chosen to match white-box knowledge of when the string layer
    30     // has tor drive the cstring layer through a second iteration:
    31     //  - for no-manip, lengths are near the room at end of string heap
    32     //    (chosen target size of 9 showed the original bug on preS2, aligned with the other cases)
    33     //  - for manip, lengths are near the auxiliary buffer size of 128
    34     // Only first case repeats for string_res; rest run only from the passthru string layer.
    35     // Similarly, the manipulator breadth isn't checked at the cstring layer either.
    36     {
    37         // S: string, no manipulator
    38         void echoTillX(const char * casename) {
    39             string s;
    40             // loop assumes behaviour not tested until main-case #15:
    41             // on reading nothing, the prior string value is left alone
    42             do {
    43                 s = "";
    44                 forceStringHeapFreeSpaceTo(9);
    45                 sin | s;
    46                 sout | casename | s;
    47             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    48         }
    49         echoTillX("preS1");
    50         echoTillX("preS2");
    51         echoTillX("preS3");
    52         echoTillX("preS4");
    53     }
    54     {
    55         // SMN: string, manipulator for no-op
    56         void echoTillX(const char * casename) {
    57             string s;
    58             do {
    59                 s = "";
    60                 sin | plainjane( s );
    61                 sout | casename | s;
    62             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    63         }
    64         echoTillX("preSMN1");
    65         echoTillX("preSMN2");
    66         echoTillX("preSMN3");
    67         echoTillX("preSMN4");
    68     }
    69     {
    70         // RMN: string_res, manipulator for no-op
    71         void echoTillX(const char * casename) {
    72             string_res s;
    73             do {
    74                 s = "";
    75                 sin | plainjane( s );
    76                 sout | casename | s;
    77             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    78         }
    79         echoTillX("preRMN1");
    80         echoTillX("preRMN2");
    81         echoTillX("preRMN3");
    82         echoTillX("preRMN4");
    83     }
    84     {
    85         // SMI: string, manipulator `incl`
    86         void echoTillX(const char * casename) {
    87             string s;
    88             do {
    89                 s = "";
    90                 sin | skip("-\n");
    91                 sin | incl( ".:|# x", s );
    92                 sout | casename | " \"" | s | "\"";
    93             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    94         }
    95         echoTillX("preSMI1");
    96         echoTillX("preSMI2");
    97         echoTillX("preSMI3");
    98         echoTillX("preSMI4");
    99     }
    100     {
    101         // SME: string, manipulator `excl`
    102         void echoTillX(const char * casename) {
    103             string s;
    104             do {
    105                 s = "";
    106                 sin | skip("-\n");
    107                 sin | excl( "-\n", s );
    108                 sout | casename | " \"" | s | "\"";
    109             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    110         }
    111         echoTillX("preSME1");
    112         echoTillX("preSME2");
    113         echoTillX("preSME3");
    114         echoTillX("preSME4");
    115     }
    116     sin | skip("-\n");
    117     {
    118         // SMG: string, manipulator `getline`
    119         void echoTillX(const char * casename) {
    120             string s;
    121             do {
    122                 s = "";
    123                 sin | getline( s );
    124                 sout | casename | s;
    125             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    126         }
    127         echoTillX("preSMG1");
    128         echoTillX("preSMG2");
    129         echoTillX("preSMG3");
    130         echoTillX("preSMG4");
    131     }
    132     {
    133         // SMD: string, manipulator (`getline` with custom) delimiter
    134         void echoTillX(const char * casename) {
    135             string s;
    136             do {
    137                 s = "";
    138                 sin | getline( s, '@' );
    139                 sout | casename | s;
    140             } while ( size(s) > 0 && s[size(s)-1] != 'x' );
    141             sin | skip(" \n");
    142         }
    143         echoTillX("preSMD1");
    144         echoTillX("preSMD2");
    145         echoTillX("preSMD3");
    146         echoTillX("preSMD4");
    147     }
    148 
    149     /* Keep harmonized with io/manipulatorsInput */
    150     {
    151         string s = "yyyyyyyyyyyyyyyyyyyy";
    152         char sk[] = "abc";
    153         sin | "abc " | skip( sk ) | skip( 5 );          sout | "1" | s;
    154         sin | s;                                        sout | "2" | s;
    155         sin | ignore( s );                              sout | "3" | s;
    156         sin | wdi( 8, s );                              sout | "4" | s;
    157         sin | ignore( wdi( 8, s ) );                    sout | "5" | s;
    158 
    159         sin | incl( "abc", s );                         sout | "6" | s;
    160         sin | excl( "abc", s );                         sout | "7" | s;
    161         sin | ignore( incl( "abc", s ) );               sout | "8" | s;
    162         sin | ignore( excl( "abc", s ) );               sout | "9" | s;
    163         sin | incl( "abc", wdi( 8, s ) );               sout | "10" | s;
    164         sin | excl( "abc", wdi( 8, s ) );               sout | "11" | s;
    165         sin | ignore( incl( "abc", wdi( 8, s ) ) );     sout | "12" | s;
    166         sin | ignore( excl( "abc", wdi( 8, s ) ) );     sout | "13" | s;
     25        // These "pre" cases deal with issues analogous to the "pre" cases of io/manipulatorsInput.
     26        // The acceptance criterion is simpler but driving the cases is harder.
     27        // The tests just read strings and echo what they read; acceptance of simple echoing assures
     28        // no spurious splitting merging.
     29        // The lengths of the strings are chosen to match white-box knowledge of when the string layer
     30        // has tor drive the cstring layer through a second iteration:
     31        //  - for no-manip, lengths are near the room at end of string heap
     32        //      (chosen target size of 9 showed the original bug on preS2, aligned with the other cases)
     33        //  - for manip, lengths are near the auxiliary buffer size of 128
     34        // Only first case repeats for string_res; rest run only from the passthru string layer.
     35        // Similarly, the manipulator breadth isn't checked at the cstring layer either.
     36        {
     37                // S: string, no manipulator
     38                void echoTillX(const char * casename) {
     39                        string s;
     40                        // loop assumes behaviour not tested until main-case #15:
     41                        // on reading nothing, the prior string value is left alone
     42                        do {
     43                                s = "";
     44                                forceStringHeapFreeSpaceTo(9);
     45                                sin | s;
     46                                sout | casename | s;
     47                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     48                }
     49                echoTillX("preS1");
     50                echoTillX("preS2");
     51                echoTillX("preS3");
     52                echoTillX("preS4");
     53        }
     54        {
     55                // SMN: string, manipulator for no-op
     56                void echoTillX(const char * casename) {
     57                        string s;
     58                        do {
     59                                s = "";
     60                                sin | plainjane( s );
     61                                sout | casename | s;
     62                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     63                }
     64                echoTillX("preSMN1");
     65                echoTillX("preSMN2");
     66                echoTillX("preSMN3");
     67                echoTillX("preSMN4");
     68        }
     69        {
     70                // RMN: string_res, manipulator for no-op
     71                void echoTillX(const char * casename) {
     72                        string_res s;
     73                        do {
     74                                s = "";
     75                                sin | plainjane( s );
     76                                sout | casename | s;
     77                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     78                }
     79                echoTillX("preRMN1");
     80                echoTillX("preRMN2");
     81                echoTillX("preRMN3");
     82                echoTillX("preRMN4");
     83        }
     84        {
     85                // SMI: string, manipulator `incl`
     86                void echoTillX(const char * casename) {
     87                        string s;
     88                        do {
     89                                s = "";
     90                                sin | skip("-\n");
     91                                sin | incl( ".:|# x", s );
     92                                sout | casename | " \"" | s | "\"";
     93                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     94                }
     95                echoTillX("preSMI1");
     96                echoTillX("preSMI2");
     97                echoTillX("preSMI3");
     98                echoTillX("preSMI4");
     99        }
     100        {
     101                // SME: string, manipulator `excl`
     102                void echoTillX(const char * casename) {
     103                        string s;
     104                        do {
     105                                s = "";
     106                                sin | skip("-\n");
     107                                sin | excl( "-\n", s );
     108                                sout | casename | " \"" | s | "\"";
     109                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     110                }
     111                echoTillX("preSME1");
     112                echoTillX("preSME2");
     113                echoTillX("preSME3");
     114                echoTillX("preSME4");
     115        }
     116        sin | skip("-\n");
     117        {
     118                // SMG: string, manipulator `getline`
     119                void echoTillX(const char * casename) {
     120                        string s;
     121                        do {
     122                                s = "";
     123                                sin | getline( s );
     124                                sout | casename | s;
     125                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     126                }
     127                echoTillX("preSMG1");
     128                echoTillX("preSMG2");
     129                echoTillX("preSMG3");
     130                echoTillX("preSMG4");
     131        }
     132        {
     133                // SMD: string, manipulator (`getline` with custom) delimiter
     134                void echoTillX(const char * casename) {
     135                        string s;
     136                        do {
     137                                s = "";
     138                                sin | getline( s, '@' );
     139                                sout | casename | s;
     140                        } while ( size(s) > 0 && s[size(s)-1] != 'x' );
     141                        sin | skip(" \n");
     142                }
     143                echoTillX("preSMD1");
     144                echoTillX("preSMD2");
     145                echoTillX("preSMD3");
     146                echoTillX("preSMD4");
     147        }
     148        // Keep harmonized with io/manipulatorsInput.
     149        {
     150                string s = "yyyyyyyyyyyyyyyyyyyy";
     151                char sk[] = "abc";
     152                sin | "abc " | skip( sk ) | skip( 5 );                  sout | "1" | s;
     153                sin | s;                                                                                sout | "2" | s;
     154                sin | ignore( s );                                                              sout | "3" | s;
     155                sin | wdi( 8, s );                                                              sout | "4" | s;
     156                sin | ignore( wdi( 8, s ) );                                    sout | "5" | s;
     157
     158                sin | incl( "abc", s );                                                 sout | "6" | s;
     159                sin | excl( "abc", s );                                                 sout | "7" | s;
     160                sin | ignore( incl( "abc", s ) );                               sout | "8" | s;
     161                sin | ignore( excl( "abc", s ) );                               sout | "9" | s;
     162                sin | incl( "abc", wdi( 8, s ) );                               sout | "10" | s;
     163                sin | excl( "abc", wdi( 8, s ) );                               sout | "11" | s;
     164                sin | ignore( incl( "abc", wdi( 8, s ) ) );             sout | "12" | s;
     165                sin | ignore( excl( "abc", wdi( 8, s ) ) );             sout | "13" | s;
     166                sin | nl;
     167
     168                s = "q";
     169                sin | incl( "abc", s );                                                 sout | "14" | s;
     170                s = "q";
     171                sin | excl( "u", s );                                                   sout | "15" | s;
     172                sin | skip( "u" ) | nl;
     173
     174                sin | getline( s );                                                             sout | "16" | s;
     175                sin | getline( s, '%' ) | nl;                                   sout | "17" | s;
     176                sin | ignore( getline( s, '%' ) ) | nl;                 sout | "18" | s;
     177
     178                sin | quoted( s );                                                              sout | "19" | s;
     179                sin | quoted( s, '\'' );                                                sout | "20" | s;
     180                sin | quoted( s, '{', '}' );                                    sout | "21" | s;
     181                sin | quoted( s, 'X', 'Y' );                                    sout | "22" | s;
     182                sin | ignore( quoted( s, 'X', 'Y' ) );                  sout | "23" | s;
     183                sin | nl;
     184        }
     185        // Full repeat on string_res layer assures the full manipulator vocabulary is supported there.
     186        {
     187                string_res s = "yyyyyyyyyyyyyyyyyyyy";
     188                char sk[] = "abc";
     189                sin | "abc " | skip( sk ) | skip( 5 );                  sout | "1" | s;
     190                sin | s;                                                                                sout | "2" | s;
     191                sin | ignore( s );                                                              sout | "3" | s;
     192                sin | wdi( 8, s );                                                              sout | "4" | s;
     193                sin | ignore( wdi( 8, s ) );                                    sout | "5" | s;
     194
     195                sin | incl( "abc", s );                                                 sout | "6" | s;
     196                sin | excl( "abc", s );                                                 sout | "7" | s;
     197                sin | ignore( incl( "abc", s ) );                               sout | "8" | s;
     198                sin | ignore( excl( "abc", s ) );                               sout | "9" | s;
     199                sin | incl( "abc", wdi( 8, s ) );                               sout | "10" | s;
     200                sin | excl( "abc", wdi( 8, s ) );                               sout | "11" | s;
     201                sin | ignore( incl( "abc", wdi( 8, s ) ) );             sout | "12" | s;
     202                sin | ignore( excl( "abc", wdi( 8, s ) ) );             sout | "13" | s;
    167203                sin | "\n";
    168204
    169205                s = "q";
    170                 sin | incl( "abc", s );                         sout | "14" | s;
    171                 s = "q";
    172                 sin | excl( "u", s );                           sout | "15" | s;
     206                sin | incl( "abc", s );                                                 sout | "14" | s;
     207                s = "q";
     208                sin | excl( "u", s );                                                   sout | "15" | s;
    173209                sin | skip( "u" );
    174210                sin | "\n";
    175         }
    176     // Full repeat on string_res layer assures the full manipulator vocabulary is supported there.
    177     {
    178         string_res s = "yyyyyyyyyyyyyyyyyyyy";
    179         char sk[] = "abc";
    180         sin | "abc " | skip( sk ) | skip( 5 );          sout | "1" | s;
    181         sin | s;                                        sout | "2" | s;
    182         sin | ignore( s );                              sout | "3" | s;
    183         sin | wdi( 8, s );                              sout | "4" | s;
    184         sin | ignore( wdi( 8, s ) );                    sout | "5" | s;
    185 
    186         sin | incl( "abc", s );                         sout | "6" | s;
    187         sin | excl( "abc", s );                         sout | "7" | s;
    188         sin | ignore( incl( "abc", s ) );               sout | "8" | s;
    189         sin | ignore( excl( "abc", s ) );               sout | "9" | s;
    190         sin | incl( "abc", wdi( 8, s ) );               sout | "10" | s;
    191         sin | excl( "abc", wdi( 8, s ) );               sout | "11" | s;
    192         sin | ignore( incl( "abc", wdi( 8, s ) ) );     sout | "12" | s;
    193         sin | ignore( excl( "abc", wdi( 8, s ) ) );     sout | "13" | s;
    194                 sin | "\n";
    195 
    196                 s = "q";
    197                 sin | incl( "abc", s );                         sout | "14" | s;
    198                 s = "q";
    199                 sin | excl( "u", s );                           sout | "15" | s;
    200                 sin | skip( "u" );
    201                 sin | "\n";
    202     }
     211                sin | getline( s );                                                             sout | "16" | s;
     212                sin | getline( s, '%' ) | nl;                                   sout | "17" | s;
     213                sin | ignore( getline( s, '%' ) ) | nl;                 sout | "18" | s;
     214
     215                sin | quoted( s );                                                              sout | "19" | s;
     216                sin | quoted( s, '\'' );                                                sout | "20" | s;
     217                sin | quoted( s, '{', '}' );                                    sout | "21" | s;
     218                sin | quoted( s, 'X', 'Y' );                                    sout | "22" | s;
     219                sin | ignore( quoted( s, 'X', 'Y' ) );                  sout | "23" | s;
     220        }
    203221}
  • tests/configs/parsebools.cfa

    r0522ebe ra4da45e  
    1010// Author           : Thierry Delisle
    1111// Created On       : Wed Oct 12 15:28:01 2022
    12 // Last Modified By :
    13 // Last Modified On :
    14 // Update Count     :
     12// Last Modified By : Peter A. Buhr
     13// Last Modified On : Sun Feb 11 09:26:13 2024
     14// Update Count     : 2
    1515//
    1616
    1717#include <fstream.hfa>
    18 
    1918#include "../meta/fork+exec.hfa"
    2019
     
    2221#include <parseargs.hfa>
    2322
    24 int main(int argc, char * argv[]) {
     23int main( int argc, char * argv[] ) {
    2524        check_main(argv[0]);
    2625
     
    4140
    4241        char **left;
    43         parse_args( options, "[OPTIONS]...\ntesting bool parameters", left);
     42        parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );
    4443
    4544        sout | "yes/no     :" | YN;
     
    5150}
    5251
    53 int true_main(const char * path, char * env[]) {
    54         printf("no arg:\n");
    55         if(pid_t child = strict_fork(); child == 0) {
    56                 int ret = execle(path, "parsebools", (const char*)0p, env);
    57                 if(ret < 0) {
    58                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    59                         exit(1);
     52int true_main( const char * path, char * env[] ) {
     53        printf( "no arg:\n" );
     54        if ( pid_t child = strict_fork(); child == 0 ) {
     55                int ret = execle( path, "parsebools", (const char*)0p, env );
     56                if ( ret < 0 ) {
     57                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     58                        exit( 1 );
    6059                }
    61         }
    62         else {
    63                 int status = do_wait(child);
    64                 print_status(status);
    65         }
     60        } else {
     61                int status = do_wait( child );
     62                print_status( status );
     63        } // if
    6664
    67         printf("all true/set arg:\n");
    68         if(pid_t child = strict_fork(); child == 0) {
    69                 int ret = execle(path, "parsebools", "-e=yes", "-y=Y", "-n=y", "-t=true", "-s", "-u", (const char*)0p, env);
    70                 if(ret < 0) {
    71                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    72                         exit(1);
    73                 }
    74         }
    75         else {
    76                 int status = do_wait(child);
    77                 print_status(status);
    78         }
     65        printf( "all true/set arg:\n" );
     66        if ( pid_t child = strict_fork(); child == 0 ) {
     67                int ret = execle( path, "parsebools", "-e=yes", "-y=Y", "-n=y", "-t=true", "-s", "-u", (const char*)0p, env );
     68                if ( ret < 0 ) {
     69                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     70                        exit( 1 );
     71                } // if
     72        } else {
     73                int status = do_wait( child );
     74                print_status( status );
     75        } // if
    7976
    80         printf("all false/unset arg:\n");
    81         if(pid_t child = strict_fork(); child == 0) {
    82                 int ret = execle(path, "parsebools", "-e=no", "-y=N", "-n=n", "-t=false", (const char*)0p, env);
    83                 if(ret < 0) {
    84                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    85                         exit(1);
    86                 }
    87         }
    88         else {
    89                 int status = do_wait(child);
    90                 print_status(status);
    91         }
     77        printf( "all false/unset arg:\n" );
     78        if ( pid_t child = strict_fork(); child == 0 ) {
     79                int ret = execle( path, "parsebools", "-e=no", "-y=N", "-n=n", "-t=false", (const char*)0p, env );
     80                if ( ret < 0 ) {
     81                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     82                        exit( 1 );
     83                } // if
     84        } else {
     85                int status = do_wait( child );
     86                print_status( status );
     87        } // if
    9288
    93         printf("gibberish arg 1:\n");
    94         if(pid_t child = strict_fork(); child == 0) {
    95                 int ret = execle(path, "parsebools", "-y=true", (const char*)0p, env);
    96                 if(ret < 0) {
    97                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    98                         exit(1);
    99                 }
    100         }
    101         else {
    102                 int status = do_wait(child);
    103                 print_status(status);
    104         }
     89        printf( "gibberish arg 1:\n" );
     90        if ( pid_t child = strict_fork(); child == 0 ) {
     91                int ret = execle( path, "parsebools", "-y=true", (const char*)0p, env );
     92                if ( ret < 0 ) {
     93                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     94                        exit( 1 );
     95                } // if
     96        } else {
     97                int status = do_wait( child );
     98                print_status( status );
     99        } // if
    105100
    106         printf("gibberish arg 2:\n");
    107         if(pid_t child = strict_fork(); child == 0) {
    108                 int ret = execle(path, "parsebools", "-t=yes", (const char*)0p, env);
    109                 if(ret < 0) {
    110                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    111                         exit(1);
    112                 }
    113         }
    114         else {
    115                 int status = do_wait(child);
    116                 print_status(status);
    117         }
     101        printf( "gibberish arg 2:\n" );
     102        if ( pid_t child = strict_fork(); child == 0 ) {
     103                int ret = execle( path, "parsebools", "-t=yes", (const char*)0p, env );
     104                if ( ret < 0 ) {
     105                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     106                        exit( 1 );
     107                } // if
     108        } else {
     109                int status = do_wait( child );
     110                print_status( status );
     111        } // if
    118112
    119         printf("gibberish arg 3:\n");
    120         if(pid_t child = strict_fork(); child == 0) {
    121                 int ret = execle(path, "parsebools", "-s=yes", (const char*)0p, env);
    122                 if(ret < 0) {
    123                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    124                         exit(1);
    125                 }
    126         }
    127         else {
    128                 int status = do_wait(child);
    129                 print_status(status);
    130         }
     113        printf( "gibberish arg 3:\n" );
     114        if ( pid_t child = strict_fork(); child == 0 ) {
     115                int ret = execle( path, "parsebools", "-s=yes", (const char*)0p, env );
     116                if ( ret < 0 ) {
     117                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     118                        exit( 1 );
     119                } // if
     120        } else {
     121                int status = do_wait( child );
     122                print_status( status );
     123        } // if
    131124
    132         printf("gibberish arg 4:\n");
    133         if(pid_t child = strict_fork(); child == 0) {
    134                 int ret = execle(path, "parsebools", "-u=yes", (const char*)0p, env);
    135                 if(ret < 0) {
    136                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    137                         exit(1);
    138                 }
    139         }
    140         else {
    141                 int status = do_wait(child);
    142                 print_status(status);
    143         }
     125        printf( "gibberish arg 4:\n" );
     126        if ( pid_t child = strict_fork(); child == 0 ) {
     127                int ret = execle( path, "parsebools", "-u=yes", ( const char*)0p, env );
     128                if ( ret < 0 ) {
     129                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     130                        exit( 1 );
     131                } // if
     132        } else {
     133                int status = do_wait( child );
     134                print_status( status );
     135        } // if
    144136
    145         printf("All Done!\n");
     137        printf( "All Done!\n" );
    146138
    147139        return 0;
  • tests/configs/parsenums.cfa

    r0522ebe ra4da45e  
    3232#endif
    3333
    34 int true_main(const char * exec);
    35 
    36 int main(int argc, char * argv[]) {
    37         check_main(argv[0]);
     34int true_main( const char * exec );
     35
     36int main( int argc, char * argv[]) {
     37        check_main( argv[0]);
    3838
    3939        int i = -3;
     
    5252
    5353        char **left;
    54         parse_args( options, "[OPTIONS]...\ntesting bool parameters", left);
     54        parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );
    5555
    5656        sout | "int                :" | i;
     
    6161}
    6262
    63 int true_main(const char * path, char * env[]) {
    64         printf("no arg:\n");
    65         if(pid_t child = strict_fork(); child == 0) {
    66                 int ret = execle(path, "parsenums", (const char*)0p, env);
    67                 if(ret < 0) {
    68                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    69                         exit(1);
    70                 }
    71         }
    72         else {
    73                 int status = do_wait(child);
    74                 print_status(status);
    75         }
    76 
    77         printf("all 0 arg:\n");
    78         if(pid_t child = strict_fork(); child == 0) {
    79                 int ret = execle(path, "parsenums", "-i=0", "-u=0", "-l=0", "-L=0", "-d=0", (const char*)0p, env);
    80                 if(ret < 0) {
    81                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    82                         exit(1);
    83                 }
    84         }
    85         else {
    86                 int status = do_wait(child);
    87                 print_status(status);
    88         }
    89 
    90         printf("negative vals arg:\n");
    91         if(pid_t child = strict_fork(); child == 0) {
    92                 int ret = execle(path, "parsenums", "-i=-1", "-d=-1", (const char*)0p, env);
    93                 if(ret < 0) {
    94                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    95                         exit(1);
    96                 }
    97         }
    98         else {
    99                 int status = do_wait(child);
    100                 print_status(status);
    101         }
    102 
    103         printf("funky notation arg:\n");
    104         if(pid_t child = strict_fork(); child == 0) {
    105                 int ret = execle(path, "parsenums", "-i=0x10", "-u=0x20", "-l=0x300", "-L=0x4000", "-d=5e6", (const char*)0p, env);
    106                 if(ret < 0) {
    107                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    108                         exit(1);
    109                 }
    110         }
    111         else {
    112                 int status = do_wait(child);
    113                 print_status(status);
    114         }
    115 
    116         printf("big values arg:\n");
    117         if(pid_t child = strict_fork(); child == 0) {
    118                 int ret = execle(path, "parsenums", "-i=2147483647", "-u=4294967295", "-l=" BIG_UNSIGNED_LONG, "-L=18446744073709551615", "-d=5e6", (const char*)0p, env);
    119                 if(ret < 0) {
    120                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    121                         exit(1);
    122                 }
    123         }
    124         else {
    125                 int status = do_wait(child);
    126                 print_status(status);
    127         }
    128 
    129         printf("too big values arg:\n");
    130         if(pid_t child = strict_fork(); child == 0) {
    131                 int ret = execle(path, "parsenums", "-i=2147483648", (const char*)0p, env);
    132                 if(ret < 0) {
    133                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    134                         exit(1);
    135                 }
    136         }
    137         else {
    138                 int status = do_wait(child);
    139                 print_status(status);
    140         }
    141 
    142         if(pid_t child = strict_fork(); child == 0) {
    143                 int ret = execle(path, "parsenums", "-u=4294967296", (const char*)0p, env);
    144                 if(ret < 0) {
    145                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    146                         exit(1);
    147                 }
    148         }
    149         else {
    150                 int status = do_wait(child);
    151                 print_status(status);
    152         }
    153 
    154         if(pid_t child = strict_fork(); child == 0) {
    155                 int ret = execle(path, "parsenums", "-l=" TOO_BIG_UNSIGNED_LONG, (const char*)0p, env);
    156                 if(ret < 0) {
    157                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    158                         exit(1);
    159                 }
    160         }
    161         else {
    162                 int status = do_wait(child);
    163                 print_status(status);
    164         }
    165 
    166         if(pid_t child = strict_fork(); child == 0) {
    167                 int ret = execle(path, "parsenums", "-L=18446744073709551616", (const char*)0p, env);
    168                 if(ret < 0) {
    169                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    170                         exit(1);
    171                 }
    172         }
    173         else {
    174                 int status = do_wait(child);
    175                 print_status(status);
    176         }
    177 
    178         printf("negative errors arg:\n");
    179         if(pid_t child = strict_fork(); child == 0) {
    180                 int ret = execle(path, "parsenums", "-u=-1", (const char*)0p, env);
    181                 if(ret < 0) {
    182                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    183                         exit(1);
    184                 }
    185         }
    186         else {
    187                 int status = do_wait(child);
    188                 print_status(status);
    189         }
    190 
    191         if(pid_t child = strict_fork(); child == 0) {
    192                 int ret = execle(path, "parsenums", "-l=-1", (const char*)0p, env);
    193                 if(ret < 0) {
    194                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    195                         exit(1);
    196                 }
    197         }
    198         else {
    199                 int status = do_wait(child);
    200                 print_status(status);
    201         }
    202 
    203         if(pid_t child = strict_fork(); child == 0) {
    204                 int ret = execle(path, "parsenums", "-L=-1", (const char*)0p, env);
    205                 if(ret < 0) {
    206                         fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno));
    207                         exit(1);
    208                 }
    209         }
    210         else {
    211                 int status = do_wait(child);
    212                 print_status(status);
    213         }
    214 
    215         printf("All Done!\n");
     63int true_main( const char * path, char * env[] ) {
     64        printf( "no arg:\n" );
     65        if ( pid_t child = strict_fork(); child == 0 ) {
     66                int ret = execle( path, "parsenums", (const char*)0p, env );
     67                if ( ret < 0 ) {
     68                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     69                        exit( 1 );
     70                } // if
     71        } else {
     72                int status = do_wait( child );
     73                print_status( status );
     74        } // if
     75
     76        printf( "all 0 arg:\n" );
     77        if ( pid_t child = strict_fork(); child == 0 ) {
     78                int ret = execle( path, "parsenums", "-i=0", "-u=0", "-l=0", "-L=0", "-d=0", (const char*)0p, env );
     79                if ( ret < 0 ) {
     80                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     81                        exit( 1 );
     82                } // if
     83        } else {
     84                int status = do_wait( child );
     85                print_status( status );
     86        } // if
     87
     88        printf( "negative vals arg:\n" );
     89        if ( pid_t child = strict_fork(); child == 0 ) {
     90                int ret = execle( path, "parsenums", "-i=-1", "-d=-1", (const char*)0p, env );
     91                if ( ret < 0 ) {
     92                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     93                        exit( 1 );
     94                } // if
     95        } else {
     96                int status = do_wait( child );
     97                print_status( status );
     98        } // if
     99
     100        printf( "funky notation arg:\n" );
     101        if ( pid_t child = strict_fork(); child == 0 ) {
     102                int ret = execle( path, "parsenums", "-i=0x10", "-u=0x20", "-l=0x300", "-L=0x4000", "-d=5e6", (const char*)0p, env );
     103                if ( ret < 0 ) {
     104                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     105                        exit( 1 );
     106                } // if
     107        } else {
     108                int status = do_wait( child );
     109                print_status( status );
     110        } // if
     111
     112        printf( "big values arg:\n" );
     113        if ( pid_t child = strict_fork(); child == 0 ) {
     114                int ret = execle( path, "parsenums", "-i=2147483647", "-u=4294967295", "-l=" BIG_UNSIGNED_LONG, "-L=18446744073709551615", "-d=5e6", (const char*)0p, env );
     115                if ( ret < 0 ) {
     116                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     117                        exit( 1 );
     118                } // if
     119        } else {
     120                int status = do_wait( child );
     121                print_status( status );
     122        } // if
     123
     124        printf( "too big values arg:\n" );
     125        if ( pid_t child = strict_fork(); child == 0 ) {
     126                int ret = execle( path, "parsenums", "-i=2147483648", (const char*)0p, env );
     127                if ( ret < 0 ) {
     128                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     129                        exit( 1 );
     130                } // if
     131        } else {
     132                int status = do_wait( child );
     133                print_status( status );
     134        } // if
     135
     136        if ( pid_t child = strict_fork(); child == 0 ) {
     137                int ret = execle( path, "parsenums", "-u=4294967296", (const char*)0p, env );
     138                if ( ret < 0 ) {
     139                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     140                        exit( 1 );
     141                } // if
     142        } else {
     143                int status = do_wait( child );
     144                print_status( status );
     145        } // if
     146
     147        if ( pid_t child = strict_fork(); child == 0 ) {
     148                int ret = execle( path, "parsenums", "-l=" TOO_BIG_UNSIGNED_LONG, (const char*)0p, env );
     149                if ( ret < 0 ) {
     150                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     151                        exit( 1 );
     152                } // if
     153        } else {
     154                int status = do_wait( child );
     155                print_status( status );
     156        } // if
     157
     158        if ( pid_t child = strict_fork(); child == 0 ) {
     159                int ret = execle( path, "parsenums", "-L=18446744073709551616", (const char*)0p, env );
     160                if ( ret < 0 ) {
     161                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     162                        exit( 1 );
     163                } // if
     164        } else {
     165                int status = do_wait( child );
     166                print_status( status );
     167        } // if
     168
     169        printf( "negative errors arg:\n" );
     170        if ( pid_t child = strict_fork(); child == 0 ) {
     171                int ret = execle( path, "parsenums", "-u=-1", (const char*)0p, env );
     172                if ( ret < 0 ) {
     173                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     174                        exit( 1 );
     175                } // if
     176        } else {
     177                int status = do_wait( child );
     178                print_status( status );
     179        } // if
     180
     181        if ( pid_t child = strict_fork(); child == 0 ) {
     182                int ret = execle( path, "parsenums", "-l=-1", (const char*)0p, env );
     183                if ( ret < 0 ) {
     184                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     185                        exit( 1 );
     186                } // if
     187        } else {
     188                int status = do_wait( child );
     189                print_status( status );
     190        } // if
     191
     192        if ( pid_t child = strict_fork(); child == 0 ) {
     193                int ret = execle( path, "parsenums", "-L=-1", (const char*)0p, env );
     194                if ( ret < 0 ) {
     195                        fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) );
     196                        exit( 1 );
     197                } // if
     198        } else {
     199                int status = do_wait( child );
     200                print_status( status );
     201        } // if
     202
     203        printf( "All Done!\n" );
    216204
    217205        return 0;
  • tests/configs/usage.cfa

    r0522ebe ra4da45e  
    1010// Author           : Thierry Delisle
    1111// Created On       : Wed Oct 12 15:28:01 2022
    12 // Last Modified By :
    13 // Last Modified On :
    14 // Update Count     :
     12// Last Modified By : Peter A. Buhr
     13// Last Modified On : Sun Feb 11 09:29:51 2024
     14// Update Count     : 1
    1515//
    1616
     
    2424
    2525        sout | "No args, no errors";
    26         if(pid_t child = strict_fork(); child == 0) {
     26        if ( pid_t child = strict_fork(); child == 0 ) {
    2727                array( cfa_option, 0 ) opts;
    28                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    29         }
    30         else {
    31                 int status = do_wait(child);
    32                 print_status(status);
    33         }
     28                print_args_usage(1, fake_argv, opts, "Test usage", false );
     29        } else {
     30                int status = do_wait( child );
     31                print_status( status );
     32        } // if
    3433
    3534        sout | "No args, with errors";
    36         if(pid_t child = strict_fork(); child == 0) {
     35        if ( pid_t child = strict_fork(); child == 0 ) {
    3736                array( cfa_option, 0 ) opts;
    38                 print_args_usage(1, fake_argv, opts, "Test usage", true);
    39         }
    40         else {
    41                 int status = do_wait(child);
    42                 print_status(status);
    43         }
     37                print_args_usage(1, fake_argv, opts, "Test usage", true );
     38        } else {
     39                int status = do_wait( child );
     40                print_status( status );
     41        } // if
    4442
    4543        sout | "Args with short names only:";
    46         if(pid_t child = strict_fork(); child == 0) {
     44        if ( pid_t child = strict_fork(); child == 0 ) {
    4745                int a, b, c;
    4846                array( cfa_option, 3 ) opts;
     
    5048                opts[1] = (cfa_option){'b', "", "Second arg", b };
    5149                opts[2] = (cfa_option){'c', "", "Third arg", c };
    52                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    53         }
    54         else {
    55                 int status = do_wait(child);
    56                 print_status(status);
    57         }
     50                print_args_usage(1, fake_argv, opts, "Test usage", false );
     51        } else {
     52                int status = do_wait( child );
     53                print_status( status );
     54        } // if
    5855
    5956        sout | "Args with long names only:";
    60         if(pid_t child = strict_fork(); child == 0) {
     57        if ( pid_t child = strict_fork(); child == 0 ) {
    6158                int a, b, c;
    6259                array( cfa_option, 3 ) opts;
     
    6461                opts[1] = (cfa_option){'\0', "BB", "Second arg", b };
    6562                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    66                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    67         }
    68         else {
    69                 int status = do_wait(child);
    70                 print_status(status);
    71         }
     63                print_args_usage(1, fake_argv, opts, "Test usage", false );
     64        } else {
     65                int status = do_wait( child );
     66                print_status( status );
     67        } // if
    7268
    7369        sout | "Mix of short and long args:";
    74         if(pid_t child = strict_fork(); child == 0) {
     70        if ( pid_t child = strict_fork(); child == 0 ) {
    7571                int a, b, c;
    7672                array( cfa_option, 3 ) opts;
     
    7874                opts[1] = (cfa_option){'b', "BBBB", "Second arg", b };
    7975                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    80                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    81         }
    82         else {
    83                 int status = do_wait(child);
    84                 print_status(status);
    85         }
     76                print_args_usage(1, fake_argv, opts, "Test usage", false );
     77        } else {
     78                int status = do_wait( child );
     79                print_status( status );
     80        } // if
    8681
    8782        sout | "Mix of short and long and some missing description:";
    88         if(pid_t child = strict_fork(); child == 0) {
     83        if ( pid_t child = strict_fork(); child == 0 ) {
    8984                int a, b, c;
    9085                array( cfa_option, 3 ) opts;
     
    9287                opts[1] = (cfa_option){'b', "BBBB", "", b };
    9388                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    94                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    95         }
    96         else {
    97                 int status = do_wait(child);
    98                 print_status(status);
    99         }
     89                print_args_usage(1, fake_argv, opts, "Test usage", false );
     90        } else {
     91                int status = do_wait( child );
     92                print_status( status );
     93        } // if
    10094
    10195        sout | "Mix of short and long and some long description:";
    102         if(pid_t child = strict_fork(); child == 0) {
     96        if ( pid_t child = strict_fork(); child == 0 ) {
    10397                int a, b, c;
    10498                array( cfa_option, 3 ) opts;
     
    106100                opts[1] = (cfa_option){'b', "BBBB", "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", b };
    107101                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    108                 print_args_usage(1, fake_argv, opts, "Test usage", false);
    109         }
    110         else {
    111                 int status = do_wait(child);
    112                 print_status(status);
    113         }
     102                print_args_usage(1, fake_argv, opts, "Test usage", false );
     103        } else {
     104                int status = do_wait( child );
     105                print_status( status );
     106        } // if
    114107}
    115108
    116109// no used
    117 static int true_main(const char * path, char * env[]) { return 0; }
     110static int true_main( const char * path, char * env[]) { return 0; }
  • tests/errors/.expect/declaration.txt

    r0522ebe ra4da45e  
    88  with members
    99    i: int
    10    with body
     10  with body
    1111
    1212
     
    1414  with members
    1515    i: int
    16    with body
     16  with body
    1717
    1818
  • tests/exceptions/pingpong_nonlocal.cfa

    r0522ebe ra4da45e  
    22#include <thread.hfa>
    33#include <mutex_stmt.hfa>
     4#include <Exception.hfa>
    45
    5 exception num_ping_pongs { int num; };
    6 vtable(num_ping_pongs) num_ping_pongs_vt;
     6ExceptionDecl( num_ping_pongs, int num; );
    77
    88thread Ping_Pong {
     
    1616        this.name = name;
    1717        cnt = 0;
    18         ?{}( except, &num_ping_pongs_vt, 0 );
     18        ?{}( except, ExceptionArgs( num_ping_pongs, 0 ) );
    1919}
    2020
     
    2929                for () {
    3030                        while( ! poll( this ) ) { yield(); }
    31             inc_resume_at( cnt );
     31                        inc_resume_at( cnt );
    3232                }
    3333        } catchResume( num_ping_pongs * e; e->num < numtimes ) {
     
    3737                mutex( sout ) sout | name | "catch" | cnt | e->num;
    3838                if ( e->num == numtimes ) {
    39             inc_resume_at( e->num );
     39                        inc_resume_at( e->num );
    4040                }
    4141        }
     
    4949                &ping.partner = &pong;                                                  // create cycle
    5050                &pong.partner = &ping;
    51                 num_ping_pongs except{ &num_ping_pongs_vt, 0 };
    52                 resumeAt( pong, except );
     51                resumeAt( pong, ExceptionInst( num_ping_pongs, 0 ) );
    5352        }
    5453        sout | "main end";
  • tests/io/.in/manipulatorsInput.txt

    r0522ebe ra4da45e  
    1313@# this line 1)-{}%
    1414@# this line 2)-{}%
    15 "abc"
     15    "abc"
    1616'abc  '
    1717{ d d
     
    3535@# this line 1)-{}%
    3636@# this line 2)-{}%
    37 "abc"
     37    "abc"
    3838'abc  '
    3939{ d d
  • tests/io/manipulatorsInput.cfa

    r0522ebe ra4da45e  
    77// Created On       : Sat Jun  8 17:58:54 2019
    88// Last Modified By : Peter A. Buhr
    9 // Last Modified On : Sun Jan 28 11:59:55 2024
    10 // Update Count     : 133
     9// Last Modified On : Mon Feb  5 21:54:49 2024
     10// Update Count     : 134
    1111//
    1212
     
    114114                sin | wdi( sizeof(s), s );                                              sout | "2" | s;
    115115                sin | ignore( s );                                                              sout | "3" | s;
    116                 sin | wdi( sizeof(s), 8, s );                                   sout | "4" | s;
     116                sin | wdi( sizeof(s), 8, s );                                   sout | "4" | s;
    117117                sin | ignore( wdi( sizeof(s), 8, s ) );                 sout | "5" | s;
    118118
     
    131131                s[0] = 'q'; s[1] = '\0';
    132132                sin | excl( "u", wdi( sizeof(s), s ) );                 sout | "15" | s;
    133                 sin | skip( "u" ) | "\n";
     133                sin | skip( "u" ) | nl;
    134134                sin | getline( wdi( sizeof(s), s ) );                   sout | "16" | s;
    135                 sin | getline( wdi( sizeof(s), s ), '%' ) | "\n"; sout | "17" | s;
    136                 sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | "\n"; sout | "18" | s;
     135                sin | getline( wdi( sizeof(s), s ), '%' ) | nl; sout | "17" | s;
     136                sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | nl; sout | "18" | s;
    137137
    138138                sin | quoted( wdi( sizeof(s), s ) );                    sout | "19" | s;
  • tools/cfa.nanorc

    r0522ebe ra4da45e  
    1010color green "\<(forall|trait|(o|d|f|t)type|mutex|_Bool|volatile|virtual)\>"
    1111color green "\<(float|double|bool|char|int|short|long|enum|void|auto)\>"
    12 color green "\<(static|const|extern|(un)?signed|inline|sizeof|vtable)\>"
    1312color green "\<((s?size)|one|zero|((u_?)?int(8|16|32|64|ptr)))_t\>"
     13color green "\<(static|const|extern|(un)?signed|inline)\>"
     14color green "\<(typeof|vtable|sizeof|alignof)\>"
    1415
    1516# Declarations
Note: See TracChangeset for help on using the changeset viewer.