Changes in / [a4da45e:0522ebe]


Ignore:
Files:
2 added
3 deleted
51 edited

Legend:

Unmodified
Added
Removed
  • doc/LaTeXmacros/common.sty

    ra4da45e r0522ebe  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Feb 25 17:37:46 2024
    14 %% Update Count     : 640
     13%% Last Modified On : Sun Jan 21 13:17:48 2024
     14%% Update Count     : 633
    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{C1\!1}\xspace}                    % C11 symbolic name
    43 
     42\newcommand{\Celeven}{\textrm{C11}\xspace}                              % C11 symbolic name
    4443\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
    4546% numbers disallowed in latex variables names => use number names
    46 \newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace}    % C++11 symbolic name
     47\newcommand{\CCeleven}{\protect\CCIcon{11}\xspace}              % C++11 symbolic name
    4748\newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace}    % C++14 symbolic name
    4849\newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace}   % C++17 symbolic name
    4950\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 
    5351\newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name
    5452
     
    295293xleftmargin=\parindentlnth,                             % indent code to paragraph indentation
    296294extendedchars=true,                                             % allow ASCII characters in the range 128-255
     295escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    297296mathescape=false,                                               % disable LaTeX math escape in CFA code $...$
    298297keepspaces=true,                                                %
     
    305304literate=
    306305%  {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1
    307   {-}{\raisebox{-1pt}{\ttfamily-}}1
     306  {-}{\raisebox{-1pt}{\texttt{-}}}1
    308307  {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
    309308  {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1
    310   {'}{\ttfamily'\!}1
    311309  {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1
    312310  {<-}{$\leftarrow$}2
     
    320318\lstset{
    321319language=CFA,
    322 escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
     320%moredelim=**[is][\color{red}]{@}{@},   % red highlighting @...@
    323321moredelim=**[is][\color{red}]{®}{®},    % red highlighting ®...® (registered trademark symbol) emacs: C-q M-.
    324322%moredelim=**[is][\color{blue}]{ß}{ß},  % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
     
    330328% inline code ©...© (copyright symbol) emacs: C-q M-)
    331329\lstMakeShortInline©                                    % single-character for \lstinline
    332 
    333330\else% regular ASCI characters
    334 
    335331\lstnewenvironment{cfa}[1][]{% necessary
    336332\lstset{
    337333language=CFA,
    338334escapechar=\$,                                                  % LaTeX escape in CFA code
     335mathescape=false,                                               % LaTeX math escape in CFA code $...$
    339336moredelim=**[is][\color{red}]{@}{@},    % red highlighting @...@
    340337}% lstset
  • doc/LaTeXmacros/common.tex

    ra4da45e r0522ebe  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Feb 25 18:11:56 2024
    14 %% Update Count     : 614
     13%% Last Modified On : Wed Jan 24 08:43:57 2024
     14%% Update Count     : 593
    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{C1\!1}\xspace}                    % C11 symbolic name
    43 
     42\newcommand{\Celeven}{\textrm{C11}\xspace}                              % C11 symbolic name
    4443\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
    4546% numbers disallowed in latex variables names => use number names
    46 \newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace}    % C++11 symbolic name
     47\newcommand{\CCeleven}{\protect\CCIcon{11}\xspace}              % C++11 symbolic name
    4748\newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace}    % C++14 symbolic name
    4849\newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace}   % C++17 symbolic name
    4950\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 
    5351\newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name
    5452
     
    299297xleftmargin=\parindentlnth,                             % indent code to paragraph indentation
    300298extendedchars=true,                                             % allow ASCII characters in the range 128-255
     299escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
    301300mathescape=false,                                               % disable LaTeX math escape in CFA code $...$
    302301keepspaces=true,                                                %
     
    309308literate=
    310309%  {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1
    311   {-}{\raisebox{-1pt}{\ttfamily-}}1
     310  {-}{\raisebox{-1pt}{\texttt{-}}}1
    312311  {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1
    313312  {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1
    314   {'}{\ttfamily'\!}1
    315313  {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1
    316314  {<-}{$\leftarrow$}2
     
    324322\lstset{
    325323language=CFA,
    326 escapechar=§,                                                   % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'
     324%moredelim=**[is][\color{red}]{@}{@},   % red highlighting @...@
    327325moredelim=**[is][\color{red}]{®}{®},    % red highlighting ®...® (registered trademark symbol) emacs: C-q M-.
    328326%moredelim=**[is][\color{blue}]{ß}{ß},  % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_
     
    334332% inline code ©...© (copyright symbol) emacs: C-q M-)
    335333\lstMakeShortInline©                                    % single-character for \lstinline
    336 
    337334\else% regular ASCI characters
    338 
    339335\lstnewenvironment{cfa}[1][]{% necessary
    340336\lstset{
    341337language=CFA,
    342338escapechar=\$,                                                  % LaTeX escape in CFA code
     339mathescape=false,                                               % LaTeX math escape in CFA code $...$
    343340moredelim=**[is][\color{red}]{@}{@},    % red highlighting @...@
    344341}% lstset
  • doc/LaTeXmacros/lstlang.sty

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

    ra4da45e r0522ebe  
    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}},
    37543744}
    37553745
     
    82538243}
    82548244
    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 
    82688245@inproceedings{Burns81,
    82698246    keywords    = {N-thread software-solution mutual exclusion},
  • doc/proposals/enum.tex

    ra4da45e r0522ebe  
    117117\end{abstract}
    118118
    119 \section{Introduction}
     119\section{Background}
    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), specific times (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), time (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 While 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 
    130 Fundamentally, all enumeration systems have an \newterm{enumeration} type with an associated set of \newterm{enumerator} names.
    131 An 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.
     128Fundamentally, 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.
     129However, the values for basic types are not named, other than the programming-language supplied constants.
     130
     131
     132\section{C-Style Enum}
     133
     134The 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]
     136enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
     137                $\(\uparrow\)$                                                                        $\(\uparrow\)$
     138    ${\rm \newterm{enumeration name}}$                                          ${\rm \newterm{enumerator names}}
     139\end{lstlisting}
     140Here, the enumeration type @Weekday@ defines the ordered \newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
     141By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constants.
     142Because 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{
     143The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}.
     144Enumerators 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@.
     145For 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.
     146Hence, there are 3 universal enumeration attributes: \newterm{position}, \newterm{label}, and \newterm{value}:
    132147\begin{cquote}
    133148\small\sf\setlength{\tabcolsep}{3pt}
    134149\begin{tabular}{rccccccccccc}
    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
     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
    141154\end{tabular}
    142155\end{cquote}
    143 Here, the \newterm{enumeration} @Weekday@ defines the ordered \newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
    144 By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constant values.
    145 Because 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{
    146 The 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 
    151 The C-Style enumeration has the following syntax and semantics.
    152 \begin{lstlisting}
    153 enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
    154 \end{lstlisting}
    155 Enumerators 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@.
    156 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@.
    157 Initialization may occur in any order.
    158 \begin{lstlisting}
    159 enum Weekday { Thursday@ = 10@, Friday, Saturday, Sunday, Monday@ = 0@, Tuesday, Wednesday };
    160 \end{lstlisting}
    161 Note, 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}
    163 enum Weekday {
    164         Thursday = 10, Friday, Saturday, Sunday,
    165         Monday = 0, Tuesday, Wednesday@,@ // terminating comma
    166 };
    167 \end{lstlisting}
    168 This feature allow enumerator lines to be interchanged without moving a comma.\footnote{
    169 A terminating comma appears in other C syntax, e.g., the initializer list.}
    170 Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible (projected) into the enclosing scope of the @enum@ type.
     156Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible in the enclosing scope of the @enum@ type.
    171157
    172158In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values.
    173 In 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.
    174 Finally, there is an implicit bidirectional conversion between an enumeration and integral types.
     159In 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.
     160Furthermore, there is an implicit bidirectional conversion between an enumeration and integral types.
    175161\begin{lstlisting}[label=lst:enum_scope]
    176162{
    177         enum Weekday { /* as above */ };        $\C{// enumerators implicitly projected into local scope}$
     163        enum Weekday { ... };                           $\C{// enumerators implicitly projected into local scope}$
    178164        Weekday weekday = Monday;                       $\C{// weekday == 0}$
    179165        weekday = Friday;                                       $\C{// weekday == 11}$
    180         int i = Sunday;                                         $\C{// implicit conversion to int, i == 13}$
     166        int i = Sunday                                          $\C{// implicit conversion to int, i == 13}$
    181167        weekday = 10000;                                        $\C{// UNDEFINED! implicit conversion to Weekday}$
    182168}
     
    185171The implicit conversion from @int@ to an enumeration type is an unnecessary source of error.
    186172
    187 It 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
    190 static const int Monday = 0;
    191 enum { Monday };
    192 \end{lstlisting}
    193 For @#define@, the programmer has to play compiler and explicitly manage the enumeration values;
    194 furthermore, these are independent constants outside of any language type mechanism.
    195 The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{
    196 C 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.
    197 Only 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 
    200173\section{\CFA-Style Enum}
    201174
     
    203176\CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages.
    204177
    205 
    206 \subsection{Enumerator Name Resolution}
    207 \label{s:EnumeratorNameResolution}
    208 
    209 In C, unscoping of enumerators presents a \newterm{naming problem} when multiple enumeration types appear in the same scope with duplicate enumerator names.
    210 There is no mechanism in C to resolve these naming conflicts other than renaming of one of the duplicates, which may be impossible.
    211 
    212 The \CFA type-system allows extensive overloading, including enumerators.
    213 Furthermore, \CFA uses the left-hand of assignment in type resolution to pinpoint the best overloaded name.
    214 Finally, qualification is provided to disambiguate any ambiguous situations.
    215 \begin{lstlisting}
    216 enum C1 { First, Second, Third, Fourth };
    217 enum C2 { @Fourth@, @Third@, @Second@, @First@ };
    218 C1 p() { return Third; }                                $\C{// correctly resolved duplicate names}$
    219 C2 p() { return Fourth; }
    220 void 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 
    232 An enumeration can be scoped, so the enumerator constants are not projected into the enclosing scope, using @'!'@.
    233 \begin{lstlisting}
    234 enum Weekday @!@ { /* as above */ };
    235 enum( char * ) Names @!@ { /* as above */ };
    236 \end{lstlisting}
    237 Now the enumerators \emph{must} be qualified with the associated enumeration.
    238 \begin{lstlisting}
    239 Weekday weekday = @Weekday@.Monday;
    240 Names names = @Names.@Fred;
    241 names = @Names.@Jane;
    242 \end{lstlisting}
    243 It 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}
    245 Weekday weekday;
    246 with ( @Weekday@, @Names@ ) {                   $\C{// type names}$
    247          Names names = @Fred@;
    248          names = @Jane@;
    249          weekday = Saturday;
    250 }
    251 \end{lstlisting}
    252 As 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 
    254178\subsection{Enumerator Typing}
    255179
    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.
    257 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 enumerator constants.
    258 Note, the synonyms @Liz@ and @Beth@ in the last declaration.
    259 
    260 Because enumerators are constants, the enumeration type is implicitly @const@, so all the enumerator types in Figure~\ref{f:EumeratorTyping} are rewritten with @const@.
    261 A typed enumeration has an implicit (safe) conversion to its base type.
    262 \begin{lstlisting}
    263 char currency = Dollar;
    264 string fred = Fred;                                             $\C{// implicit conversion from char * to \CFA string type}$
    265 Person student = Beth;
     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.
     181Figure~\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
     183Typed enumerates deals with \emph{harmonizing} problem between an enumeration and its companion data.
     184The following example is from the \CFA compiler, written in \CC.
     185\begin{lstlisting}
     186enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES };
     187char * 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}
     194The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit.
     195It 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).
     196The typed enumeration largely solves this problem by combining and managing the two data types.
     197\begin{lstlisting}
     198enum( 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};
    266204\end{lstlisting}
    267205
     
    285223        enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i };
    286224// pointer
    287         enum( @char *@ ) Names { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
     225        enum( @char *@ ) Names { Fred = "Fred", Mary = "Mary", Jane = "Jane" };
    288226        int i, j, k;
    289227        enum( @int *@ ) ptr { I = &i,  J = &j,  K = &k };
    290228        enum( @int &@ ) ref { I = i,   J = j,   K = k };
    291229// tuple
    292         enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$
     230        enum( @[int, int]@ ) { T = [ 1, 2 ] };
    293231// function
    294232        void f() {...}   void g() {...}
     
    296234// aggregate
    297235        struct Person { char * name; int age, height; };
    298 @***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz, Jon = { "JONATHAN", 35, 190 } };
     236        enum( @Person@ ) friends { Liz = { "Elizabeth", 22, 170 }, Beth = Liz, Jon = { "Jonathan", 35, 190 } };
    299237\end{lstlisting}
    300238\caption{Enumerator Typing}
     
    302240\end{figure}
    303241
    304 Typed enumerations deals with the \emph{harmonizing} problem between an enumeration and any companion data.
    305 The following example is from the \CFA compiler, written in \CC.
    306 \begin{lstlisting}
    307 enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES };
    308 char * 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}
    315 The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit.
    316 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).
    317 The typed enumeration largely solves this problem by combining and managing the two data types.
    318 \begin{lstlisting}
    319 enum( 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}
    326 Note, 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 
    329242\subsection{Pure Enumerators}
    330243
    331 An empty enumerator type, @enum()@, implies the enumerators are pure symbols without values but set properties;
     244An empty type, @enum()@, implies the enumerators are pure symbols without values;
    332245hence, there is no default conversion to @int@.
    333246
    334247\begin{lstlisting}
    335248enum() Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND };
    336 @***@Mode iomode = O_RDONLY;
    337 bool b = iomode == O_RDONLY || iomode < O_APPEND;
     249Mode iomode = O_RDONLY;
    338250int i = iomode;                                                 $\C{\color{red}// disallowed}$
     251sout | O_TRUNC;                                                 $\C{\color{red}// disallowed}$
    339252\end{lstlisting}
    340253
    341254\subsection{Enumerator Subset}
    342255
    343 If follows from enumerator typing that the enumerator type can be another enumerator.
    344 \begin{lstlisting}
    345 enum( @char@ ) Currency { Dollar = '$\textdollar$', Euro = '$\texteuro$', Pound = '$\textsterling$'  };
    346 enum( @Currency@ ) Europe { Euro = Currency.Euro, Pound = Currency.Pound }; // intersection
     256If follows from enumerator typing that the type of the enumerators can be another enumerator.
     257\begin{lstlisting}
    347258enum( char ) Letter { A = 'A',  B = 'B', C = 'C', ..., Z = 'Z' };
    348 enum( @Letter@ ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // intersection
    349 \end{lstlisting}
    350 Subset enumerations may have more or less enumerators than their typed enumeration, but the enumerator values must be from the typed enumeration.
    351 For 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}
     259enum( Letter ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // alphabet intersection
    353260Letter letter = A;
    354 @***@Greak greek = Beta;
    355 letter = Beta;                                                  $\C{// allowed, letter == B}$
     261Greak greek = Alph;
     262letter = Alph;                                                  $\C{// allowed}$
    356263greek = A;                                                              $\C{\color{red}// disallowed}$
    357264\end{lstlisting}
    358 
     265Enumeration @Greek@ may have more or less enumerators than @Letter@, but the enumerator values must be from @Letter@.
     266Therefore, @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@.
    359267
    360268\subsection{Enumeration Inheritance}
    361269
    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}
    364 enum( char * ) Names { /* as above */ };
    365 enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" };
    366 @***@enum /* inferred */ Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" };
    367 \end{lstlisting}
    368 Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Names@ by containment, and a @Names@ enumeration is a subtype of enumeration @Name2@.
     270\CFA Plan-9 inheritance may be used with enumerations.
     271\begin{lstlisting}
     272enum( char * ) Name2 { @inline Name@, Jack = "Jack", Jill = "Jill" };
     273enum /* inferred */ Name3 { @inline Name2@, Sue = "Sue", Tom = "Tom" };
     274\end{lstlisting}
     275Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Name@ by containment, and a @Name@ enumeration is a subtype of enumeration @Name2@.
    369276Note, enumerators must be unique in inheritance but enumerator values may be repeated.
    370277
    371278The enumeration type for the inheriting type must be the same as the inherited type;
    372279hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@.
    373 % When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
    374 Specifically, the inheritance relationship for @Names@ is:
    375 \begin{lstlisting}
    376 Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\(\subset\)$ const char * $\C{// enum type of Names}$
     280When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
     281
     282Specifically, the inheritance relationship for Names is:
     283\begin{lstlisting}
     284Name $\(\subset\)$ Name2 $\(\subset\)$ Name3 $\(\subset\)$ const char *         // enum type of Name
    377285\end{lstlisting}
    378286For the given function prototypes, the following calls are valid.
     
    380288\begin{tabular}{ll}
    381289\begin{lstlisting}
    382 void f( Names );
    383 void g( Names2 );
    384 void h( Names3 );
     290void f( Name );
     291void g( Name2 );
     292void h( Name3 );
    385293void j( const char * );
    386294\end{lstlisting}
     
    390298g( Fred );   g( Jill );
    391299h( Fred );   h( Jill );   h( Sue );
    392 j( Fred );   j( Jill );   j( Sue );   j( "WILL" );
     300j( Fred );   j( Jill );   j( Sue );   j( "Will" );
    393301\end{lstlisting}
    394302\end{tabular}
    395303\end{cquote}
    396 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.
    397 
     304Note, 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
     308A \CFA-enum can be scoped, meaning the enumerator constants are not projected into the enclosing scope.
     309\begin{lstlisting}
     310enum Weekday @!@ { /* as above */ };
     311enum Colour( char * ) @!@ { /* as above */ };
     312\end{lstlisting}
     313where the @'!'@ implies the enumerators are \emph{not} projected.
     314The 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}
     318Weekday weekday = @Weekday.Monday@;             $\C{// qualification}$
     319Colour colour = @Colour.@Red;
     320colour = @Colour.@Blue;
     321\end{lstlisting}
    398322
    399323\subsection{Enumeration Pseudo-functions}
    400324
    401 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@, @offsetof@, @typeof@.
     325Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@.
    402326Often 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..
    403327
     328\subsubsection{Enumerator Attributes}
    404329The attributes of an enumerator are accessed by pseudo-functions @position@, @value@, and @label@.
    405330\begin{lstlisting}
    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"}$
    409 sout | @label@( Names.Jane ) | @value@( Names.Jane );
    410 \end{lstlisting}
    411 Note the ability to print both enumerator label and value.
    412 
    413 
    414 \subsection{Enumerator Position or Value}
    415 
    416 Enumerators can be used in multiple contexts.
    417 In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution).
    418 However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts.
    419 In these contexts, a programmer's initition assumes an implicit conversion to postion.
    420 
    421 For 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}
    424 enum Count { First, Second, Third, Fourth };
    425 Count e;
    426 \end{lstlisting}
    427 \begin{tabular}{ll}
    428 \begin{lstlisting}
    429 
    430 choose( e ) {
    431         case @First@: ...;
    432         case @Second@: ...;
    433         case @Third@: ...;
    434         case @Fourth@: ...;
    435 }
    436 \end{lstlisting}
    437 &
    438 \begin{lstlisting}
    439 // rewrite
    440 choose( @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}
    449 Here, 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.
    450 However, this implementation is fragile, e.g., if the enumeration is changed to:
    451 \begin{lstlisting}
    452 enum Count { First, Second, Third @= First@, Fourth };
    453 \end{lstlisting}
    454 which make @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicase @case@ clauses.
    455 To better match with programmer intuition, \CFA toggles between value and position semantics depneding on the language context.
    456 For conditional clauses and switch statments, \CFA uses the robust position implementation.
    457 \begin{lstlisting}
    458 choose( @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}
    467 Count variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth;
     331int green_pos = @position@( Colour.Green );     $\C{// 1}$
     332char * green_value = @value@( Colour.Green ); $\C{// "G"}$
     333char * green_label = @label@( Colour.Green ); $\C{// "Green"}$
     334\end{lstlisting}
     335
     336Enumeration Greek may have more or less enumerators than Letter, but the enumerator values must be from Letter.
     337Therefore, 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]
     346enum(int) C_ENUM { First, Second, Third = First, Fourth };
     347int 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}
     356In the @C_ENUM@ example, @Third@ is an alias of @First@ and @Fourth@ is an alias of @Second@.
     357Programmers cannot make case branches for @Third@ and @Fourth@ because the switch statement matches cases by the enumerator's value.
     358Case @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]
     362enum(double) C_ENUM { First, Second, Third = First, Fourth };
     363C_ENUM variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth;
     364int 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};
    468372p(variable_a); // 0
    469373p(variable_b); // 1
     
    11111015If the @aggregation_name@ is identified as a \CFA enumeration, the compiler checks if @field@ presents in the declared \CFA enumeration.
    11121016
     1017\subsection{\lstinline{with} Clause/Statement}
     1018
     1019Instead 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]
     1021enum Color( char * ) { Red="R", Green="G", Blue="B" };
     1022enum Animal( int ) { Cat=10, Dog=20 };
     1023with ( Color, Animal ) {
     1024        char * red_string = Red; // value( Color.Red )
     1025        int cat = Cat; // value( Animal.Cat )
     1026}
     1027\end{lstlisting}
     1028The \lstinline{with} might introduce ambiguity to a scope. Consider the example:
     1029\begin{lstlisting}[label=lst:declaration]
     1030enum Color( char * ) { Red="R", Green="G", Blue="B" };
     1031enum RGB( int ) { Red=0, Green=1, Blue=2 };
     1032with ( 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
    11131038\subsection{Instance Declaration}
    11141039
     
    12931218
    12941219\subsection{\CC}
    1295 \label{s:C++RelatedWork}
    1296 
    1297 \CC is backwards compatible with C, so it inherited C's enumerations.
    1298 However, the following non-backwards compatible changes have been made.
    1299 \begin{quote}
    1300 7.2 Change: \CC objects of enumeration type can only be assigned values of the same enumeration type.
    1301 In C, objects of enumeration type can be assigned values of any integral type. \\
    1302 Example:
    1303 \begin{lstlisting}
    1304 enum color { red, blue, green };
    1305 color 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}
    1313 7.2 Change: In \CC, the type of an enumerator is its enumeration.
    1314 In C, the type of an enumerator is @int@. \\
    1315 Example:
    1316 \begin{lstlisting}
    1317 enum e { A };
    1318 sizeof(A) == sizeof(int)                                $\C{// in C}$
    1319 sizeof(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.
    1326 Taking the size of an enumerator is not a common C coding practice.
    1327 \end{quote}
    1328 Hence, the values in a \CC enumeration can only be its enumerators (without a cast).
    1329 While the storage size of an enumerator is up to the compiler, there is still an implicit cast to @int@.
    1330 \begin{lstlisting}
    1331 enum E { A, B, C };
    1332 E e = A;
    1333 int 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.
     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;
     1222hence, the values in a \CC enumeration can only be its enumerators (without a cast).
     1223There 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@.
     1226\CC{20} supports unscoped access with a \lstinline[language=c++]{using enum} declaration.
     1227
     1228For 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.
     1229In \CC{11}, the underlying integral type can be explicitly specified:
    13361230\begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
    1337 enum class E { A, B, C };
    1338 E e = @E::@A;                                                   $\C{// qualified enumerator}$
    1339 e = B;                                                                  $\C{// B not in scope}$
    1340 \end{lstlisting}
    1341 \CC{20} supports unscoped access with a \lstinline[language=c++]{using enum} declaration.
    1342 \begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}]
    1343 enum class E { A, B, C };
    1344 @using enum E;@
    1345 E e = A;                                                                $\C{// direct access}$
    1346 e = 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}]{@}{@}}]
    1350 enum class RGB @: long@ { Red, Green, Blue };
    1351 enum class rgb @: char@ { Red = 'r', Green = 'g', Blue = 'b' };
    1352 enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 };
    1353 \end{lstlisting}
    1354 There 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}]{@}{@}}]
    1356 rgb crgb = rgb::Red;
    1357 char ch = rgb::Red;   ch = crgb;                $\C{// disallowed}$
    1358 \end{lstlisting}
    1359 Finally, there is no mechanism to iterate through an enumeration nor use the enumeration type to declare an array dimension.
    1360 
     1231enum class RGB : @long@ { Red, Green, Blue };
     1232enum class rgb : @char@ { Red = 'r', Green = 'g', Blue = 'b' };
     1233enum class srgb : @signed char@ { Red = -1, Green = 0, Blue = 1 };
     1234RGB colour1 = @RGB::@Red;
     1235rgb colour2 = @rgb::@Red;
     1236srgb colour3 = @srgb::@Red;
     1237\end{lstlisting}
    13611238
    13621239\subsection{Go}
  • doc/theses/jiada_liang_MMath/background.tex

    ra4da45e r0522ebe  
    11\chapter{Background}
    2 
    3 
    4 \section{C-Style Enum}
    5 
    6 The C-Style enumeration has the following syntax and semantics.
    7 \begin{cfa}
    8 enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday };
    9 \end{cfa}
    10 Enumerators 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@.
    11 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@.
    12 Initialization may occur in any order.
    13 \begin{cfa}
    14 enum Weekday { Thursday@ = 10@, Friday, Saturday, Sunday, Monday@ = 0@, Tuesday, Wednesday };
    15 \end{cfa}
    16 Note, 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}
    18 enum Weekday {
    19         Thursday = 10, Friday, Saturday, Sunday,
    20         Monday = 0, Tuesday, Wednesday@,@ // terminating comma
    21 };
    22 \end{cfa}
    23 This feature allow enumerator lines to be interchanged without moving a comma.\footnote{
    24 A terminating comma appears in other C syntax, \eg the initializer list.}
    25 Finally, C enumerators are \Newterm{unscoped}, \ie enumerators declared inside of an @enum@ are visible (projected) into the enclosing scope of the @enum@ type.
    26 
    27 In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values.
    28 In 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.
    29 Finally, 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 }
    38 int j = Wednesday;                                              $\C{// ERROR! Wednesday is not declared in this scope}$
    39 \end{cfa}
    40 The implicit conversion from @int@ to an enumeration type is an unnecessary source of error.
    41 
    42 It is common for C programmers to ``believe'' there are 3 equivalent forms of constant enumeration.
    43 \begin{cfa}
    44 #define Monday 0
    45 static const int Monday = 0;
    46 enum { Monday };
    47 \end{cfa}
    48 For @#define@, the programmer has to play compiler and explicitly manage the enumeration values;
    49 furthermore, these are independent constants outside of any language type mechanism.
    50 The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{
    51 C 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.
    52 Only 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

    ra4da45e r0522ebe  
    11\chapter{Introduction}
    22
    3 Naming values is a common practice in mathematics and engineering, \eg $\pi$, $\tau$ (2$\pi$), $\phi$ (golden ratio), MHz (1E6), etc.
    4 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), specific times (noon, New Years).
    5 Many programming languages capture this important software engineering capability through a mechanism called an \Newterm{enumeration}.
    6 An 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.
    7 Note, all enumeration names must be unique but different names can represent the same value (eight note, quaver), which are synonyms.
     3Testing glossy abbreviations \gls{foo} and \gls{bar}, and glossy definitions \gls{git} and \gls{gulp}.
    84
    9 Specifically, an enumerated type restricts its values to a fixed set of named constants.
    10 While 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 
    12 Fundamentally, all enumeration systems have an \Newterm{enumeration} type with an associated set of \Newterm{enumerator} names.
    13 An 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}
    25 Here, the \Newterm{enumeration} @Weekday@ defines the ordered \Newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@.
    26 By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constant values.
    27 Because 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{
    28 The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}.
    29 
    30 \section{Contributions}
     5And use the glossy abbreviations \gls{foo} and \gls{bar}, and definitions \gls{git} and \gls{gulp} again.
  • doc/theses/jiada_liang_MMath/uw-ethesis-frontpgs.tex

    ra4da45e r0522ebe  
    131131\begin{center}\textbf{Abstract}\end{center}
    132132
    133 An enumeration is a type defining an ordered set of named constant values, where a name abstracts a value, \eg @PI@ versus @3.145159@.
    134 C 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.
    136 Furthermore, \CFA adds other useful features for enumerations to support better software-engineering practices and simplify program development.
     133Enumerated type ...
    137134
    138135\cleardoublepage
  • doc/theses/jiada_liang_MMath/uw-ethesis.tex

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

    ra4da45e r0522ebe  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Mon Feb 12 11:50:26 2024
    14 %% Update Count     : 6199
     13%% Last Modified On : Tue Jan 30 09:02:41 2024
     14%% Update Count     : 6046
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    41774177The \CFA header file for the I/O library is \Indexc{fstream.hfa}.
    41784178
    4179 
    4180 \subsubsection{Stream Output}
    4181 
    41824179For implicit formatted output, the common case is printing a series of variables separated by whitespace.
    41834180\begin{cquote}
     
    42584255Note, \CFA stream variables ©stdin©, ©stdout©, ©stderr©, ©exit©, and ©abort© overload C variables ©stdin©, ©stdout©, ©stderr©, and functions ©exit© and ©abort©, respectively.
    42594256
    4260 
    4261 \subsubsection{Stream Input}
    4262 
    42634257For 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.
    42644258\begin{cquote}
    42654259\begin{lrbox}{\myboxA}
    42664260\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4267 char c;   int i;   double d
     4261int x;   double y   char z;
    42684262\end{cfa}
    42694263\end{lrbox}
     
    42724266\multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}}        & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}}       & \multicolumn{1}{c}{\textbf{Python}}   \\
    42734267\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4274 sin | c | i | d;
     4268sin | x | y | z;
    42754269\end{cfa}
    42764270&
    42774271\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4278 cin >> c >> i >> d;
     4272cin >> x >> y >> z;
    42794273\end{cfa}
    42804274&
    42814275\begin{cfa}[aboveskip=0pt,belowskip=0pt]
    4282 c = input();   i = int(input());   d = float(input());
     4276x = int(input());  y = float(input());  z = input();
    42834277\end{cfa}
    42844278\\
    42854279\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4286 ®A® ®1® ®2.5®
     4280®1® ®2.5® ®A®
    42874281
    42884282
     
    42904284&
    42914285\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4292 ®A® ®1® ®2.5®
     4286®1® ®2.5® ®A®
    42934287
    42944288
     
    42964290&
    42974291\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    4298 ®A®
    42994292®1®
    43004293®2.5®
     4294®A®
    43014295\end{cfa}
    43024296\end{tabular}
     
    43154309For 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.
    43164310Floating-point values can also be written in hexadecimal format preceded by ©0x© or ©0X© with hexadecimal digits and exponent denoted by ©p© or ©P©.
    4317 In all cases, whitespace characters are skipped until an appropriate value is found.
    4318 \begin{cfa}[belowskip=0pt]
    4319 char ch;  int i;  float f; double d;  _Complex double cxd;
    4320 sin | ch | i | f | d | cxd;
    4321 X   42   1234.5     0xfffp-2    3.5+7.1i
    4322 \end{cfa}
    4323 It is also possible to scan and ignore specific strings and whitespace using a string format.
    4324 \begin{cfa}[belowskip=0pt]
    4325 sin | "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}
    4330 A 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.
    4331 Note, a single whitespace in the format string matches \Textbf{any} quantity of whitespace characters from the stream (including none).
    4332 
    4333 For the C-string type, the default input format is any number of \Textbf{non-whitespace} characters.
    4334 There is no escape character supported in an input string, but any Latin-1 character can be typed directly in the input string.
    4335 For example, if the following non-whitespace output is redirected into a file by the shell:
    4336 \begin{cfa}[belowskip=0pt]
    4337 sout | "\n\t\f\0234\x23";
    4338 \end{cfa}
    4339 it can be read back from the file by redirecting the file as input using:
    4340 \begin{cfa}[belowskip=0pt]
    4341 char s[64];
    4342 sin | wdi( sizeof(s), s );                              §\C{// must specify string size}§
    4343 \end{cfa}
    4344 The input string is always null terminated ©'\0'© in the input variable.
    4345 Because of potential buffer overrun when reading C strings, strings are restricted to work with input manipulators \see{\VRef{s:InputManipulators}}.
    4346 As 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.}
    4349 For 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.
    4350 If 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©.
     4311In 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
     4314For 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..
     4315In both cases, the string is null terminated ©'\0'©.
     4316For 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©.
    43564319\begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt]
    43574320\item
     
    49694932
    49704933\subsection{Input Manipulators}
    4971 \label{s:InputManipulators}
    4972 
    4973 A string variable \emph{must} be large enough to contain the input sequence.
    4974 To 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}
    4976 char line[64];
    4977 sin | wdi( ®sizeof(line)®, line );              §\C{// must specify string size}§
    4978 \end{cfa}
    4979 
    4980 Certain 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.
     4934
     4935The following \Index{manipulator}s control scanning of input values (reading), and only affect the format of the argument.
     4936
     4937Certain 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.
    49814938For 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) ©'§'©.
    49824939The following string is matched by this scanset:
    49834940\begin{cfa}
    4984 !&%$  abAA () ZZZ  ??§\S§  xx§\S\S§
    4985 \end{cfa}
    4986 To match a minus, make it the first character in the set, \eg ©"©{\color{red}\raisebox{-1pt}{\texttt{-}}}©0-9"©.
    4987 Other complex forms of regular-expression matching are unsupported.
    4988 
    4989 The following \Index{manipulator}s control scanning of input values (reading) and only affect the format of the argument.
     4941!&%$  abAA () ZZZ  ??  xx§\S\S\S§
     4942\end{cfa}
     4943To match a minus, put it as the first character, ©"-0-9"©.
     4944Other complex forms of regular-expression matching are not supported.
     4945
     4946A string variable \emph{must} be large enough to contain the input sequence.
     4947To 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}
     4949char line[64];
     4950sin | wdi( ®sizeof(line)®, line ); // must specify size
     4951\end{cfa}
     4952
     4953Currently, 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.
     4954Hence, 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.
     4955All reads in \CFA check if values do not fit into the argument variable's type and raise the exception
     4956All types are
    49904957
    49914958\begin{enumerate}
    49924959\item
    4993 \Indexc{skip}( \textit{scanset} )\index{manipulator!skip@©skip©}, ©skip©( $N$ )
    4994 consumes either the \textit{scanset} or the next $N$ characters, including newlines.
    4995 If the match successes, the input characters are ignored, and input continues with the next character.
     4960\Indexc{skip}( scanset )\index{manipulator!skip@©skip©}, ©skip©( $N$ )
     4961The first form uses a scanset to skip matching characters.
     4962The second form skips the next $N$ characters, including newline.
     4963If the match successes, the input characters are discarded, and input continues with the next character.
    49964964If the match fails, the input characters are left unread.
    49974965\begin{cfa}[belowskip=0pt]
    4998 char scanset[§\,§] = "abc";
    4999 sin | "abc§\textvisiblespace§" | skip( scanset ) | skip( 5 ); §\C{// match and skip input sequence}§
     4966char sk[§\,§] = "abc";
     4967sin | "abc " | skip( sk ) | skip( 5 ); // match input sequence
    50004968\end{cfa}
    50014969\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5002 ®abc   abc  xxx®
    5003 \end{cfa}
    5004 Again, 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©}
    5008 For 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.
     4970®abc   ®
     4971®abc  ®
     4972®xx®
     4973\end{cfa}
     4974
     4975\item
     4976\Indexc{wdi}( maximum, variable )\index{manipulator!wdi@©wdi©}
     4977For all types except ©char *©, whitespace is skipped until an appropriate value is found for the specified variable type.
     4978maximum is the maximum number of characters read for the current operation.
    50104979\begin{cfa}[belowskip=0pt]
    50114980char ch;   char ca[3];   int i;   double d;   
     
    50164985\end{cfa}
    50174986Here, ©ca[0]© is type ©char©, so the width reads 3 characters \Textbf{without} a null terminator.
    5018 If an input value is not found for a variable, the exception ©missing_data© is raised, and the input variable is unchanged.
    50194987
    50204988Note, input ©wdi© cannot be overloaded with output ©wd© because both have the same parameters but return different types.
     
    50224990
    50234991\item
    5024 \Indexc{wdi}( $maximum\ size$, ©char s[]© )\index{manipulator!wdi@©wdi©}
    5025 For 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.
    5027 If the non-whitespace sequence of input characters is greater than $maximum\ size - 1$ (null termination), the exception ©cstring_length© is raised.
     4992\Indexc{wdi}( maximum size, ©char s[]© )\index{manipulator!wdi@©wdi©}
     4993For type ©char *©, maximum is the maximum number of characters read for the current operation.
     4994Any 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
    50284995\begin{cfa}[belowskip=0pt]
    5029 char cs[10];
    5030 sin | wdi( sizeof(cs), cs );
     4996char cstr[10];
     4997sin | wdi( sizeof(cstr), cstr );
    50314998\end{cfa}
    50324999\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5033 ®012345678®
    5034 \end{cfa}
    5035 Nine 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©}
    5039 This 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$.
     5000®abcd1233.456E+2®
     5001\end{cfa}
     5002
     5003\item
     5004\Indexc{wdi}( maximum size, maximum read, ©char s[]© )\index{manipulator!wdi@©wdi©}
     5005For type ©char *©, maximum is the maximum number of characters read for the current operation.
    50405006\begin{cfa}[belowskip=0pt]
    5041 char cs[10];
    5042 sin | wdi( sizeof(cs), 9, cs );
     5007char ch;   char ca[3];   int i;   double d;   
     5008sin | wdi( sizeof(ch), ch ) | wdi( sizeof(ca), ca[0] ) | wdi( 3, i ) | wdi( 8, d );  // c == 'a', ca == "bcd", i == 123, d == 345.6
    50435009\end{cfa}
    50445010\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5045 ®012345678®9
    5046 \end{cfa}
    5047 The 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©}
    5051 consumes 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.
    5052 If the delimiter character is omitted, it defaults to ©'\n'© (newline).
     5011®abcd1233.456E+2®
     5012\end{cfa}
     5013
     5014\item
     5015\Indexc{ignore}( reference-value )\index{manipulator!ignore@©ignore©}
     5016For all types, the data is read from the stream depending on the argument type but ignored, \ie it is not stored in the argument.
    50535017\begin{cfa}[belowskip=0pt]
    5054 char cs[10];
    5055 sin | getline( wdi( sizeof(cs), cs ) );
    5056 sin | getline( wdi( sizeof(cs), cs ), 'X' ); §\C{// X is the line delimiter}§
     5018double d;
     5019sin | ignore( d );  // d is unchanged
    50575020\end{cfa}
    50585021\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
    5059 ®abc ?? #@%®
    5060 ®abc ?? #@%X® w
    5061 \end{cfa}
    5062 The 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©}
    5066 consumes 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).
    5067 If the delimit character is omitted, it defaults to ©'\''© (single quote).
     5022®  -75.35e-4® 25
     5023\end{cfa}
     5024
     5025\item
     5026\Indexc{incl}( scanset, wdi-input-string )\index{manipulator!incl@©incl©}
     5027For C-string types only, the scanset matches any number of characters \emph{in} the set.
     5028Matching characters are read into the C input-string and null terminated.
    50685029\begin{cfa}[belowskip=0pt]
    5069 char ch;
    5070 sin | 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©}
    5079 consumes 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.
    5080 If the delimit character is omitted, it defaults to ©'\''© (single quote).
    5081 \end{sloppypar}
    5082 \begin{cfa}[belowskip=0pt]
    5083 char cs[10];
    5084 sin | quoted( wdi( sizeof(cs), cs ) ); §\C[3in]{// " is the start/end delimiter}§
    5085 sin | quoted( wdi( sizeof(cs), cs ), '\'' ); §\C{// ' is the start/end delimiter}§
    5086 sin | 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©}
    5094 consumes the scanset, which reads all the scanned characters into the string variable (null terminated).
    5095 \begin{cfa}[belowskip=0pt]
    5096 char cs[10];
    5097 sin | incl( "abc", cs );
     5030char s[10];
     5031sin | incl( "abc", s );
    50985032\end{cfa}
    50995033\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     
    51025036
    51035037\item
    5104 \Indexc{excl}( scanset, $wdi\ manipulator$ )\index{manipulator!excl@©excl©}
    5105 consumes the \emph{not} scanset, which reads all the scanned characters into the string variable (null terminated).
     5038\Indexc{excl}( scanset, wdi-input-string )\index{manipulator!excl@©excl©}
     5039For C-string types, the scanset matches any number of characters \emph{not in} the set.
     5040Non-matching characters are read into the C input-string and null terminated.
    51065041\begin{cfa}[belowskip=0pt]
    5107 char cs[10];
    5108 sin | excl( "abc", cs );
     5042char s[10];
     5043sin | excl( "abc", s );
    51095044\end{cfa}
    51105045\begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt]
     
    51125047\end{cfa}
    51135048
    5114 \item
    5115 \Indexc{ignore}( ©T & v© or ©const char cs[]© or $string\ manipulator$ )\index{manipulator!ignore@©ignore©}
    5116 consumes the appropriate characters for the type and ignores them, so the input variable is unchanged.
    5117 \begin{cfa}
    5118 double d;
    5119 char cs[10];
    5120 sin | ignore( d );                                              §\C{// d is unchanged}§
    5121 sin | ignore( cs );                                             §\C{// cs is unchanged, no wdi required}§
    5122 sin | 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}
     5049\Indexc{quoted}( char delimit, wdi-input-string )\index{manipulator!quoted@©quoted©}
     5050Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character.
     5051If 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©}
     5055Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character.
     5056If the delimit character is omitted, it defaults to ©'\n'© (newline).
    51275057\end{enumerate}
    51285058
  • libcfa/src/Exception.hfa

    ra4da45e r0522ebe  
    22
    33// TEMPORARY
    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 ) }
     4#define ExceptionDecl( name, fields... ) exception name{ fields }; __attribute__(( cfa_linkonce )) vtable( name ) name ## _vt
     5#define ExceptionInst( name, values... ) (name){ &name ## _vt, values }
  • libcfa/src/bits/signal.hfa

    ra4da45e r0522ebe  
    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

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

    ra4da45e r0522ebe  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  6 20:59:18 2024
    13 // Update Count     : 118
     12// Last Modified On : Sun Jan 14 12:03:46 2024
     13// Update Count     : 81
    1414//
    1515
     
    8080void ?|?(ofstream & out, const string & s);
    8181ifstream & ?|?(ifstream & in, string & s);
     82void ?|?( ifstream & in, string & s );
    8283
    8384static inline {
     
    9596void ?|?( ofstream & os, _Ostream_Manip(string) );
    9697
    97 struct _Istream_Swidth {
    98         string & s;
    99         inline _Istream_str_base;
    100 }; // _Istream_Swidth
    101 
    102 struct _Istream_Squoted {
    103         _Istream_Swidth sstr;
    104 }; // _Istream_Squoted
    105 
    10698struct _Istream_Sstr {
    10799        string & s;
    108100        inline _Istream_str_base;
    109 //      _Istream_Swidth sstr;
    110101}; // _Istream_Sstr
    111102
    112103static inline {
    113104        // read width does not include null terminator
    114         _Istream_Swidth wdi( unsigned int rwd, string & s ) { return (_Istream_Swidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
     105        _Istream_Sstr wdi( unsigned int rwd, string & s ) { return (_Istream_Sstr)@{ s, {{0p}, rwd, {.flags.rwd : true}} }; }
    115106        _Istream_Sstr getline( string & s, const char delimiter = '\n' ) {
    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} } };
     107                return (_Istream_Sstr)@{ s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    118108        }
    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;
     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;
    121111        }
    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; }
     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; }
    141118} // distribution
    142 ifstream & ?|?( ifstream & is, _Istream_Squoted f );
    143119ifstream & ?|?( ifstream & is, _Istream_Sstr f );
    144 static inline ifstream & ?|?( ifstream & is, _Istream_Swidth f ) { return is | *(_Istream_Sstr *)&f; }
     120void ?|?( ifstream & is, _Istream_Sstr t );
    145121
    146122// Concatenation
  • libcfa/src/collections/string_res.cfa

    ra4da45e r0522ebe  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb 10 17:47:22 2024
    13 // Update Count     : 83
     12// Last Modified On : Mon Jan 22 23:12:42 2024
     13// Update Count     : 43
    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 ifstream & ?|?( 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;
     250    return in;
     251}
     252
     253void ?|?( ifstream & in, string_res & s ) {
     254    (ifstream &)(in | s);
    276255}
    277256
     
    316295} // ?|?
    317296
     297void ?|?( ifstream & in, _Istream_Rstr f ) {
     298    (ifstream &)(in | f);
     299}
     300
     301
    318302// Empty constructor
    319303void ?{}(string_res & s) with(s) {
    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                 }
     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}
    333317
    334318static void eagerCopyCtorHelper(string_res & s, const char * rhs, size_t rhslnth) with(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;
     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;
    347331}
    348332
    349333// Constructor from a raw buffer and size
    350334void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    351         eagerCopyCtorHelper(s, rhs, rhslnth);
     335    eagerCopyCtorHelper(s, rhs, rhslnth);
    352336}
    353337
    354338void ?{}( string_res & s, ssize_t rhs ) {
    355         char buf[64];
    356         int len;
    357         snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
    358         ( s ){ buf, len };
     339    char buf[64];
     340    int len;
     341    snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
     342    ( s ){ buf, len };
    359343}
    360344void ?{}( string_res & s, size_t rhs ) {
    361         char buf[64];
    362         int len;
    363         snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
    364         ( s ){ buf, len };
     345    char buf[64];
     346    int len;
     347    snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
     348    ( s ){ buf, len };
    365349}
    366350void ?{}( string_res & s, double rhs ) {
    367         char buf[64];
    368         int len;
    369         snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
    370         ( s ){ buf, len };
     351    char buf[64];
     352    int len;
     353    snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
     354    ( s ){ buf, len };
    371355}
    372356void ?{}( string_res & s, long double rhs ) {
    373         char buf[64];
    374         int len;
    375         snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
    376         ( s ){ buf, len };
     357    char buf[64];
     358    int len;
     359    snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
     360    ( s ){ buf, len };
    377361}
    378362void ?{}( string_res & s, double _Complex rhs ) {
    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 };
     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 };
    383367}
    384368void ?{}( string_res & s, long double _Complex rhs ) {
    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 };
     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 };
    389373}
    390374
    391375// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
    392376void ?{}( string_res & s, VbyteHeap & heap, const char * rhs, size_t rhslnth ) with(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;
     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;
    400384}
    401385
     
    403387// General copy constructor
    404388void ?{}(string_res & s, const string_res & s2, StrResInitMode mode, size_t start, size_t len ) {
    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         }
     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    }
    447432}
    448433
    449434static void assignEditSet(string_res & s, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
    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         }
     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    }
    520505}
    521506
    522507// traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs
    523508static void locateInShareEditSet( string_res & s, string_res *& shareEditSetStartPeer, string_res *& shareEditSetEndPeer ) {
    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         }
     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    }
    534519}
    535520
    536521static string_res & assign_(string_res & s, const char * buffer, size_t bsize, const string_res & valSrc) {
    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;
     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;
    617603}
    618604
    619605string_res & assign(string_res & s, const string_res & src, size_t maxlen) {
    620         return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
     606    return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
    621607}
    622608
    623609string_res & assign(string_res & s, const char * buffer, size_t bsize) {
    624         return assign_(s, buffer, bsize, *0p);
     610    return assign_(s, buffer, bsize, *0p);
    625611}
    626612
    627613string_res & ?=?(string_res & s, char c) {
    628         return assign(s, &c, 1);
     614    return assign(s, &c, 1);
    629615}
    630616
    631617string_res & ?=?( string_res & s, ssize_t rhs ) {
    632         string_res rhs2 = rhs;
    633         s = rhs2;
    634         return s;
     618    string_res rhs2 = rhs;
     619    s = rhs2;
     620    return s;
    635621}
    636622string_res & ?=?( string_res & s, size_t rhs ) {
    637         string_res rhs2 = rhs;
    638         s = rhs2;
    639         return s;
     623    string_res rhs2 = rhs;
     624    s = rhs2;
     625    return s;
    640626}
    641627string_res & ?=?( string_res & s, double rhs ) {
    642         string_res rhs2 = rhs;
    643         s = rhs2;
    644         return s;
     628    string_res rhs2 = rhs;
     629    s = rhs2;
     630    return s;
    645631}
    646632string_res & ?=?( string_res & s, long double rhs ) {
    647         string_res rhs2 = rhs;
    648         s = rhs2;
    649         return s;
     633    string_res rhs2 = rhs;
     634    s = rhs2;
     635    return s;
    650636}
    651637string_res & ?=?( string_res & s, double _Complex rhs ) {
    652         string_res rhs2 = rhs;
    653         s = rhs2;
    654         return s;
     638    string_res rhs2 = rhs;
     639    s = rhs2;
     640    return s;
    655641}
    656642string_res & ?=?( string_res & s, long double _Complex rhs ) {
    657         string_res rhs2 = rhs;
    658         s = rhs2;
    659         return s;
     643    string_res rhs2 = rhs;
     644    s = rhs2;
     645    return s;
    660646}
    661647
    662648// Copy assignment operator
    663649string_res & ?=?(string_res & s, const string_res & rhs) with( s ) {
    664         return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
     650    return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
    665651}
    666652
    667653string_res & ?=?(string_res & s, string_res & rhs) with( s ) {
    668         const string_res & rhs2 = rhs;
    669         return s = rhs2;
     654    const string_res & rhs2 = rhs;
     655    return s = rhs2;
    670656}
    671657
     
    673659// Destructor
    674660void ^?{}(string_res & s) with(s) {
    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         }
     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    }
    686672}
    687673
     
    691677// offset from the start of the string.
    692678char ?[?](const string_res & s, size_t index) with(s) {
    693         //TODO: Check if index is valid (no exceptions yet)
    694         return Handle.s[index];
     679    //TODO: Check if index is valid (no exceptions yet)
     680    return Handle.s[index];
    695681}
    696682
    697683void assignAt(const string_res & s, size_t index, char val) {
    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);
     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);
    703689}
    704690
     
    708694
    709695void append(string_res & str1, const char * buffer, size_t bsize) {
    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;
     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;
    725711}
    726712
    727713void ?+=?(string_res & str1, const string_res & str2) {
    728         append( str1, str2.Handle.s, str2.Handle.lnth );
     714    append( str1, str2.Handle.s, str2.Handle.lnth );
    729715}
    730716
    731717void append(string_res & str1, const string_res & str2, size_t maxlen) {
    732         append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
     718    append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
    733719}
    734720
    735721void ?+=?(string_res & s, char c) {
    736         append( s, & c, 1 );
     722    append( s, & c, 1 );
    737723}
    738724void ?+=?(string_res & s, const char * c) {
    739         append( s, c, strlen(c) );
     725    append( s, c, strlen(c) );
    740726}
    741727
     
    744730
    745731void ?*=?(string_res & s, size_t factor) {
    746         string_res s2 = { s, COPY_VALUE };
    747         s = "";
    748         for (factor) s += s2;
     732    string_res s2 = { s, COPY_VALUE };
     733    s = "";
     734    for (factor) s += s2;
    749735}
    750736
     
    753739
    754740int strcmp(const string_res & s1, const string_res & s2) {
    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;
     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;
    759745}
    760746
     
    767753
    768754int strcmp (const string_res & s1, const char * s2) {
    769         string_res s2x = s2;
    770         return strcmp(s1, s2x);
     755    string_res s2x = s2;
     756    return strcmp(s1, s2x);
    771757}
    772758
     
    779765
    780766int strcmp (const char * s1, const string_res & s2) {
    781         string_res s1x = s1;
    782         return strcmp(s1x, s2);
     767    string_res s1x = s1;
     768    return strcmp(s1x, s2);
    783769}
    784770
     
    791777
    792778
     779
    793780//////////////////////////////////////////////////////////
    794781// Search
    795782
    796783bool contains(const string_res & s, char ch) {
    797         for ( i; size(s) ) {
    798                 if (s[i] == ch) return true;
    799         }
    800         return false;
     784    for ( i; size(s) ) {
     785        if (s[i] == ch) return true;
     786    }
     787    return false;
    801788}
    802789
    803790int find(const string_res & s, char search) {
    804         return findFrom(s, 0, search);
     791    return findFrom(s, 0, search);
    805792}
    806793
    807794int findFrom(const string_res & s, size_t fromPos, char search) {
    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;
     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;
    816803}
    817804
    818805int find(const string_res & s, const string_res & search) {
    819         return findFrom(s, 0, search);
     806    return findFrom(s, 0, search);
    820807}
    821808
    822809int findFrom(const string_res & s, size_t fromPos, const string_res & search) {
    823         return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     810    return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
    824811}
    825812
    826813int find(const string_res & s, const char * search) {
    827         return findFrom(s, 0, search);
     814    return findFrom(s, 0, search);
    828815}
    829816int findFrom(const string_res & s, size_t fromPos, const char * search) {
    830         return findFrom(s, fromPos, search, strlen(search));
     817    return findFrom(s, fromPos, search, strlen(search));
    831818}
    832819
    833820int find(const string_res & s, const char * search, size_t searchsize) {
    834         return findFrom(s, 0, search, searchsize);
     821    return findFrom(s, 0, search, searchsize);
    835822}
    836823
    837824int findFrom(const string_res & s, size_t fromPos, const char * search, size_t searchsize) {
    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;
     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;
    869859}
    870860
    871861bool includes(const string_res & s, const string_res & search) {
    872         return includes(s, search.Handle.s, search.Handle.lnth);
     862    return includes(s, search.Handle.s, search.Handle.lnth);
    873863}
    874864
    875865bool includes(const string_res & s, const char * search) {
    876         return includes(s, search, strlen(search));
     866    return includes(s, search, strlen(search));
    877867}
    878868
    879869bool includes(const string_res & s, const char * search, size_t searchsize) {
    880         return find(s, search, searchsize) < s.Handle.lnth;
     870    return find(s, search, searchsize) < s.Handle.lnth;
    881871}
    882872
    883873bool startsWith(const string_res & s, const string_res & prefix) {
    884         return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
     874    return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
    885875}
    886876
    887877bool startsWith(const string_res & s, const char * prefix) {
    888         return startsWith(s, prefix, strlen(prefix));
     878    return startsWith(s, prefix, strlen(prefix));
    889879}
    890880
    891881bool startsWith(const string_res & s, const char * prefix, size_t prefixsize) {
    892         if (s.Handle.lnth < prefixsize) {
    893                 return false;
    894         }
    895         return memcmp(s.Handle.s, prefix, prefixsize) == 0;
     882    if (s.Handle.lnth < prefixsize) {
     883        return false;
     884    }
     885    return memcmp(s.Handle.s, prefix, prefixsize) == 0;
    896886}
    897887
    898888bool endsWith(const string_res & s, const string_res & suffix) {
    899         return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
     889    return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
    900890}
    901891
    902892bool endsWith(const string_res & s, const char * suffix) {
    903         return endsWith(s, suffix, strlen(suffix));
     893    return endsWith(s, suffix, strlen(suffix));
    904894}
    905895
    906896bool endsWith(const string_res & s, const char * suffix, size_t suffixsize) {
    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 */
     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
    917908
    918909///////////////////////////////////////////////////////////////////////////
     
    920911
    921912void ?{}( charclass_res & s, const string_res & chars) {
    922         (s){ chars.Handle.s, chars.Handle.lnth };
     913    (s){ chars.Handle.s, chars.Handle.lnth };
    923914}
    924915
    925916void ?{}( charclass_res & s, const char * chars ) {
    926         (s){ chars, strlen(chars) };
     917    (s){ chars, strlen(chars) };
    927918}
    928919
    929920void ?{}( charclass_res & s, const char * chars, size_t charssize ) {
    930         (s.chars){ chars, charssize };
    931         // now sort it ?
     921    (s.chars){ chars, charssize };
     922    // now sort it ?
    932923}
    933924
    934925void ^?{}( charclass_res & s ) {
    935         ^(s.chars){};
     926    ^(s.chars){};
    936927}
    937928
    938929static bool test( const charclass_res & mask, char c ) {
    939         // instead, use sorted char list?
    940         return contains( mask.chars, c );
     930    // instead, use sorted char list?
     931    return contains( mask.chars, c );
    941932}
    942933
    943934int exclude(const string_res & s, const charclass_res & mask) {
    944         for ( i; size(s) ) {
    945                 if ( test(mask, s[i]) ) return i;
    946         }
    947         return size(s);
     935    for ( i; size(s) ) {
     936        if ( test(mask, s[i]) ) return i;
     937    }
     938    return size(s);
    948939}
    949940
    950941int include(const string_res & s, const charclass_res & mask) {
    951         for ( i; size(s) ) {
    952                 if ( ! test(mask, s[i]) ) return i;
    953         }
    954         return size(s);
     942    for ( i; size(s) ) {
     943        if ( ! test(mask, s[i]) ) return i;
     944    }
     945    return size(s);
    955946}
    956947
     
    962953static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) {
    963954#ifdef VbyteDebug
    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         {
     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    {
    975966                serr | "HandleList:";
    976967                serr | nlOff;
     
    983974                } // for
    984975                serr | nlOn;
    985         }
    986         serr | "exit:AddThisAfter";
     976    }
     977    serr | "exit:AddThisAfter";
    987978#endif // VbyteDebug
    988979} // AddThisAfter
     
    993984static void DeleteNode( HandleNode & s ) with(s) {
    994985#ifdef VbyteDebug
    995         serr | "enter:DeleteNode, s:" | &s;
    996 #endif // VbyteDebug
    997         flink->blink = blink;
    998         blink->flink = flink;
    999 #ifdef VbyteDebug
    1000         serr | "exit:DeleteNode";
     986    serr | "enter:DeleteNode, s:" | &s;
     987#endif // VbyteDebug
     988    flink->blink = blink;
     989    blink->flink = flink;
     990#ifdef VbyteDebug
     991    serr | "exit:DeleteNode";
    1001992#endif // VbyteDebug
    1002993} //  DeleteNode
     
    1008999static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) {
    10091000#ifdef VbyteDebug
    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 ?
     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 ?
    10171008                garbage( s, size );                                                             // firer up the garbage collector
    10181009                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    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;
     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;
    10261017} // VbyteAlloc
    10271018
     
    10361027
    10371028static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) {
    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;
     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;
    10521043}
    10531044
     
    10581049static void MoveThisAfter( HandleNode & s, const HandleNode  & h ) with(s) {
    10591050#ifdef VbyteDebug
    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
     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
    10651056                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    1066                 //        | ( h->s ) | " and keep handles in ascending order";
     1057                //      | ( h->s ) | " and keep handles in ascending order";
    10671058                // exit(-1 );
    10681059                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    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 ) {
     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 ) {
    10741065                DeleteNode( s );
    10751066                AddThisAfter( s, *i->blink );
    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";
     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";
    10911082#endif // VbyteDebug
    10921083} // MoveThisAfter
     1084
     1085
     1086
    10931087
    10941088
     
    11031097int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  {
    11041098#ifdef VbyteDebug
    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
     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 ) {
     1106                if ( i == Src1Lnth - 1 ) {
     1107                    cmp = 0;
     1108                    break CharZip;
    11221109                } // exit
    1123                 if ( i == Src1Lnth - 1 ) {
    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
     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;
    11341114                } // exit
    1135                 if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
    1136                         cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
    1137                         break CharZip;
     1115            } // for
     1116        } // exit
     1117        if ( i == Src1Lnth - 1 ) {
     1118            for ( ; ; i += 1 ) {
     1119                if ( i == Src2Lnth - 1 ) {
     1120                    cmp = 0;
     1121                    break CharZip;
    11381122                } // exit
    1139         } // for
    1140 #ifdef VbyteDebug
    1141         serr | "exit:ByteCmp, cmp:" | cmp;
    1142 #endif // VbyteDebug
    1143         return cmp;
     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;
    11441138} // ByteCmp
    11451139
     
    11511145
    11521146void compaction(VbyteHeap & s) with(s) {
    1153         HandleNode *h;
    1154         char *obase, *nbase, *limit;
    1155        
    1156         NoOfCompactions += 1;
    1157         EndVbyte = StartVbyte;
    1158         h = Header.flink;                                       // ignore header node
    1159         for () {
     1147    HandleNode *h;
     1148    char *obase, *nbase, *limit;
     1149   
     1150    NoOfCompactions += 1;
     1151    EndVbyte = StartVbyte;
     1152    h = Header.flink;                                   // ignore header node
     1153    for () {
    11601154                memmove( EndVbyte, h->s, h->lnth );
    11611155                obase = h->s;
     
    11751169                } // for
    11761170                if ( h == &Header ) break;                      // end of header list ?
    1177         } // for
     1171    } // for
    11781172} // compaction
    11791173
    11801174
    11811175static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
    1182                                                                                                                 // probably an unreasonable default, but need to assess early-round tests on changing it
     1176                                                        // probably an unreasonable default, but need to assess early-round tests on changing it
    11831177
    11841178void TUNING_set_string_heap_liveness_threshold( double val ) {
    1185         heap_expansion_freespace_threshold = 1.0 - val;
     1179    heap_expansion_freespace_threshold = 1.0 - val;
    11861180}
    11871181
     
    11921186void garbage(VbyteHeap & s, int minreq ) with(s) {
    11931187#ifdef VbyteDebug
    1194         serr | "enter:garbage";
    1195         {
     1188    serr | "enter:garbage";
     1189    {
    11961190                serr | "HandleList:";
    11971191                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12041198                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12051199                } // for
    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
     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
    12121206                AmountUsed += i->lnth;
    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
     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
    12171211
    12181212                extend( s, max( CurrSize, minreq ) );                           // extend the heap
    12191213
    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         {
     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    {
    12311225                serr | "HandleList:";
    12321226                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12391233                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12401234                } // for
    1241         }
    1242         serr | "exit:garbage";
     1235    }
     1236    serr | "exit:garbage";
    12431237#endif // VbyteDebug
    12441238} // garbage
     
    12531247void extend( VbyteHeap & s, int size ) with (s) {
    12541248#ifdef VbyteDebug
    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;
     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;
    12691263#endif // VbyteDebug
    12701264} // extend
     
    12781272void VbyteHeap::reduce( int size ) {
    12791273#ifdef VbyteDebug
    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;
     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;
    12941288#endif // VbyteDebug
    12951289} // reduce
  • libcfa/src/collections/string_res.hfa

    ra4da45e r0522ebe  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Feb  7 21:24:40 2024
    13 // Update Count     : 59
     12// Last Modified On : Thu Jan  4 11:28:06 2024
     13// Update Count     : 27
    1414//
    1515
     
    123123void ?|?(ofstream & out, const string_res & s);
    124124ifstream & ?|?(ifstream & in, string_res & s);
    125 
    126 struct _Istream_Rwidth {
    127         string_res * s;
    128         inline _Istream_str_base;
    129 }; // _Istream_Rwidth
    130 
    131 struct _Istream_Rquoted {
    132         // string_res * s;
    133         // inline _Istream_str_base;
    134         _Istream_Rwidth rstr;
    135 }; // _Istream_Rquoted
     125void ?|?( ifstream & in, string_res & s );
    136126
    137127struct _Istream_Rstr {
    138128        string_res * s;
    139129        inline _Istream_str_base;
    140 //      _Istream_Rwidth rstr;
    141130}; // _Istream_Rstr
    142131
    143132static inline {
    144133        // read width does not include null terminator
    145         _Istream_Rwidth wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rwidth)@{ .s : &s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
     134        _Istream_Rstr wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, rwd, {.flags.rwd : true}} }; }
    146135        _Istream_Rstr getline( string_res & s, const char delimiter = '\n' ) {
    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} } };
     136                return (_Istream_Rstr)@{ &s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    149137        }
    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;
     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;
    152140        }
    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; }
     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; }
    168147} // distribution
    169 ifstream & ?|?( ifstream & is, _Istream_Rquoted f );
    170148ifstream & ?|?( ifstream & is, _Istream_Rstr f );
    171 static inline ifstream & ?|?( ifstream & is, _Istream_Rwidth f ) { return is | *(_Istream_Rstr *)&f; }
     149void ?|?( ifstream & is, _Istream_Rstr t );
    172150
    173151// Concatenation
  • libcfa/src/fstream.cfa

    ra4da45e r0522ebe  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb 11 20:55:45 2024
    13 // Update Count     : 580
     12// Last Modified On : Sun Jan 28 09:56:08 2024
     13// Update Count     : 554
    1414//
    1515
     
    121121    } // for
    122122        if ( file == 0p ) {
    123                 throwResume (open_failure){ os };
     123                throw (open_failure){ os };
    124124                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    125125        } // if
     
    241241    } // for
    242242        if ( file == 0p ) {
    243                 throwResume (open_failure){ is };
     243                throw (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, tmperrno;
     297        int nargs;
    298298    for () {                                                                                    // no check for EINTR limit waiting for keyboard input
     299                errno = 0;
    299300                disable_interrupts();
    300                 errno = 0;
    301301                nargs = vfscanf( (FILE *)(is.file$), format, args );
    302                 tmperrno = errno;
    303302                enable_interrupts();
    304303          if ( nargs != EOF || errno != EINTR ) break;          // timer interrupt ?
     
    309308                } // if
    310309        } // if
    311         if ( tmperrno == ERANGE ) throwResume ExceptionInst( data_range );
    312310        va_end( args );
    313311        return nargs;
     
    320318// *********************************** exceptions ***********************************
    321319
     320
     321static vtable(open_failure) open_failure_vt;
    322322
    323323// exception I/O constructors
     
    335335
    336336
     337static vtable(close_failure) close_failure_vt;
     338
    337339// exception I/O constructors
    338340void ?{}( close_failure & ex, ofstream & ostream ) with( ex ) {
     
    349351
    350352
     353static vtable(write_failure) write_failure_vt;
     354
    351355// exception I/O constructors
    352356void ?{}( write_failure & ex, ofstream & ostream ) with( ex ) {
     
    363367
    364368
     369static vtable(read_failure) read_failure_vt;
     370
    365371// exception I/O constructors
    366372void ?{}( read_failure & ex, ofstream & ostream ) with( ex ) {
  • libcfa/src/fstream.hfa

    ra4da45e r0522ebe  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb 11 20:35:00 2024
    13 // Update Count     : 274
     12// Last Modified On : Wed Oct 18 20:30:12 2023
     13// Update Count     : 261
    1414//
    1515
     
    139139
    140140
    141 ExceptionDecl( open_failure,
     141exception 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 ExceptionDecl( close_failure,
     153exception 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 ExceptionDecl( write_failure,
     165exception 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 ExceptionDecl( read_failure,
     177exception 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

    ra4da45e r0522ebe  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Feb 12 09:26:05 2024
    13 // Update Count     : 1966
     12// Last Modified On : Sun Jan 28 11:58:54 2024
     13// Update Count     : 1917
    1414//
    1515
     
    774774forall( istype & | basic_istream( istype ) ) {
    775775        istype & ?|?( istype & is, bool & b ) {
    776                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    777776                char val[6];
    778777                int args = fmt( is, "%5s", val );
    779                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     778                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    780779                if ( strcmp( val, "true" ) == 0 ) b = true;
    781780                else if ( strcmp( val, "false" ) == 0 ) b = false;
     
    788787
    789788        istype & ?|?( istype & is, char & c ) {
    790                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    791789                char temp;
    792790                for () {
    793791                        int args = fmt( is, "%c", &temp );
    794                         if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     792                        if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    795793                        // do not overwrite parameter with newline unless appropriate
    796794                        if ( temp != '\n' || getANL$( is ) ) { c = temp; break; }
     
    801799
    802800        istype & ?|?( istype & is, signed char & sc ) {
    803                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    804801                int args = fmt( is, "%hhi", &sc );
    805                 if ( args != 1 ) throwResume ExceptionInst( missing_data );
     802                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    806803                return is;
    807804        } // ?|?
    808805
    809806        istype & ?|?( istype & is, unsigned char & usc ) {
    810                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    811807                int args = fmt( is, "%hhi", &usc );
    812                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     808                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    813809                return is;
    814810        } // ?|?
    815811
    816812        istype & ?|?( istype & is, short int & si ) {
    817                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    818813                int args = fmt( is, "%hi", &si );
    819                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     814                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    820815                return is;
    821816        } // ?|?
    822817
    823818        istype & ?|?( istype & is, unsigned short int & usi ) {
    824                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    825819                int args = fmt( is, "%hi", &usi );
    826                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     820                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    827821                return is;
    828822        } // ?|?
    829823
    830824        istype & ?|?( istype & is, int & i ) {
    831                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    832825                int args = fmt( is, "%i", &i );
    833                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     826                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    834827                return is;
    835828        } // ?|?
    836829
    837830        istype & ?|?( istype & is, unsigned int & ui ) {
    838                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    839831                int args = fmt( is, "%i", &ui );
    840                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     832                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    841833                return is;
    842834        } // ?|?
    843835
    844836        istype & ?|?( istype & is, long int & li ) {
    845                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    846837                int args = fmt( is, "%li", &li );
    847                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     838                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    848839                return is;
    849840        } // ?|?
    850841
    851842        istype & ?|?( istype & is, unsigned long int & ulli ) {
    852                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    853843                int args = fmt( is, "%li", &ulli );
    854                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     844                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    855845                return is;
    856846        } // ?|?
    857847
    858848        istype & ?|?( istype & is, long long int & lli ) {
    859                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    860849                int args = fmt( is, "%lli", &lli );
    861                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     850                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    862851                return is;
    863852        } // ?|?
    864853
    865854        istype & ?|?( istype & is, unsigned long long int & ulli ) {
    866                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    867855                int args = fmt( is, "%lli", &ulli );
    868                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     856                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    869857                return is;
    870858        } // ?|?
     
    893881
    894882        istype & ?|?( istype & is, float & f ) {
    895                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    896883                int args = fmt( is, "%f", &f );
    897                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     884                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    898885                return is;
    899886        } // ?|?
    900887
    901888        istype & ?|?( istype & is, double & d ) {
    902                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    903889                int args = fmt( is, "%lf", &d );
    904                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     890                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    905891                return is;
    906892        } // ?|?
    907893
    908894        istype & ?|?( istype & is, long double & ld ) {
    909                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    910895                int args = fmt( is, "%Lf", &ld );
    911                 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data );
     896                if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data );
    912897                return is;
    913898        } // ?|?
    914899
    915900        istype & ?|?( istype & is, float _Complex & fc ) {
    916                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    917901                float re, im;
    918902                int args = fmt( is, "%f%fi", &re, &im );
    919                 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
     903                if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
    920904                fc = re + im * _Complex_I;
    921905                return is;
     
    923907
    924908        istype & ?|?( istype & is, double _Complex & dc ) {
    925                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    926909                double re, im;
    927910                int args = fmt( is, "%lf%lfi", &re, &im );
    928                 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
     911                if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
    929912                dc = re + im * _Complex_I;
    930913                return is;
     
    932915
    933916        istype & ?|?( istype & is, long double _Complex & ldc ) {
    934                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    935917                long double re, im;
    936918                int args = fmt( is, "%Lf%Lfi", &re, &im );
    937                 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data );
     919                if ( args != -1 && args != 2 ) throwResume ExceptionInst( missing_data );
    938920                ldc = re + im * _Complex_I;
    939921                return is;
     
    941923
    942924        istype & ?|?( istype & is, const char fmt[] ) {
    943                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    944925                size_t len = strlen( fmt );
    945                 char fmtstr[len + 16];
    946                 strcpy( fmtstr, fmt );                                                  // copy format and add %n
    947                 strcpy( &fmtstr[len], "%n" );
     926                char fmt2[len + 16];
     927                strcpy( fmt2, fmt );                                                    // copy format and add %n
     928                strcpy( &fmt2[len], "%n" );
    948929                int len2 = -1;
    949                 fmt( is, fmtstr, &len2 );
    950                 if ( ! eof( is ) && len2 == -1 ) throwResume ExceptionInst( missing_data );
     930                int args = fmt( is, fmt2, &len2 );
     931                if ( args != -1 && len2 == -1 ) throwResume ExceptionInst( missing_data );
    951932                return is;
    952933        } // ?|?
     
    982963forall( istype & | basic_istream( istype ) ) {
    983964        istype & ?|?( istype & is, _Istream_Cskip f ) {
    984                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
     965                // printf( "skip %s %d\n", f.scanset, f.wd );
    985966                if ( f.scanset ) {
    986967                        int nscanset = strlen(f.scanset);
     
    990971                        strcpy( &fmtstr[pos], f.scanset );  pos += nscanset;
    991972                        strcpy( &fmtstr[pos], "]" );
    992                         fmt( is, fmtstr, "" );                                          // skip scanset, zero or more
     973                        fmt( is, fmtstr, "" );                                          // skip scanset
    993974                } else {
    994975                        char ch;
     976                        // fprintf( stderr, "skip " );
    995977                        for ( f.wd ) {                                                          // skip N characters
    996                                 int args = fmt( is, "%c", &ch );
    997                                 if ( args != 1 ) throwResume ExceptionInst( missing_data );
     978                          if ( eof( is ) ) break;
     979                                fmt( is, "%c", &ch );
     980                                // fprintf( stderr, "`%c' ", ch );
    998981                        } // for
    999982                } // if
     
    1002985
    1003986        istype & ?|?( istype & is, _Istream_Cquoted f ) with( f.cstr ) {
    1004                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    1005987                int args;
    1006988          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;
     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;
    1011995
    1012996                        // Change the remainder of the read into a getline by reseting the closing delimiter.
     
    10271011
    10281012        istype & ?|?( istype & is, _Istream_Cstr f ) with( f.cstr ) {
    1029                 if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    10301013                const char * scanset;
    10311014                size_t nscanset = 0;
     
    10581041                        if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
    10591042                        else args = fmt( is, fmtstr, s, &len );
    1060                         // fprintf( stderr, "cstr %s %d %d %d\n", fmtstr, args, len, f.cstr.wd );
     1043                        // fprintf( stderr, "cstr %s %d %d %d %s\n", fmtstr, args, len, wd, s );
    10611044                        if ( check && len >= rwd && ! eof( is ) ) {     // might not fit
    10621045                                char peek;
    10631046                                fmt( is, "%c", &peek );                                 // check for whitespace terminator
    10641047                                // fprintf( stderr, "peek %d '%c'\n", args, peek );
    1065                                 if ( ! eof( is ) ) {                                    // can only fail at eof
     1048                                if ( ! eof( is ) ) {
    10661049                                        ungetc( is, peek );
    10671050                                        if ( ! isspace( peek ) ) throwResume ExceptionInst( cstring_length );
     
    10691052                        } // if
    10701053                        // FIX ME: CFA strings need to be modified to NOT change the argument for this case, then this can be removed.
    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
     1054                        if ( ! flags.ignore && args == 0 ) s[0]= '\0'; // read failed => no pattern match => set string to null
    10731055                } else {
    10741056                        if ( flags.delimiter ) {                                        // getline
    10751057                                int len = 0;                                                    // may not be set in fmt
    1076                                 if ( delimiters[2] != '\0' ) {                  // (quoted) read single character ?
     1058                                if ( delimiters[2] != '\0' ) {                  // read single character ?
    10771059                                        sprintf( &fmtstr[pos], "c%%n" );
    10781060                                } else {
     
    10811063                                if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*'
    10821064                                else args = fmt( is, fmtstr, s, &len );
    1083 
    10841065                                if ( check && len == rwd && ! eof( is ) ) {     // might not fit
    10851066                                        char peek;
     
    11151096                } // if
    11161097                if ( args == 1 && eof( is ) ) {                                 // data but scan ended at EOF
     1098                        // fprintf( stderr, "clear\n" );
    11171099                        clear( is );                                                            // => reset EOF => detect again on next read
    11181100                } // if
  • libcfa/src/iostream.hfa

    ra4da45e r0522ebe  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Feb  6 18:35:54 2024
    13 // Update Count     : 743
     12// Last Modified On : Sun Jan 28 11:56:29 2024
     13// Update Count     : 733
    1414//
    1515
     
    370370// *********************************** exceptions ***********************************
    371371
    372 ExceptionDecl( missing_data );                                                  // read finds no appropriate data
    373 ExceptionDecl( cstring_length );                                                // character string size exceeded
    374 ExceptionDecl( data_range );                                                    // value too large for numerical type
     372ExceptionDecl( cstring_length );
     373ExceptionDecl( missing_data );
    375374
    376375// *********************************** manipulators ***********************************
     
    407406        char * s;
    408407        inline _Istream_str_base;
    409 }; // _Istream_Cwidth
     408}; // _Istream_Cstr
    410409
    411410// Restrict nesting of input manipulators to those combinations that make sense.
     411
     412struct _Istream_Cstr {
     413        _Istream_Cwidth cstr;
     414}; // _Istream_Cstr
    412415
    413416struct _Istream_Cquoted {
     
    415418}; // _Istream_Cquoted
    416419
    417 struct _Istream_Cstr {
    418         _Istream_Cwidth cstr;
    419 }; // _Istream_Cstr
    420 
    421420static inline {
    422421        // 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);
    423422        _Istream_Cwidth wdi( unsigned int wd, char s[] ) {
    424                 if ( wd <= 1 ) throwResume ExceptionInst( cstring_length ); // minimum 1 character and null terminator
     423                if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator
    425424                return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } };
    426425        }
    427426        _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) {
    428                 if ( wd <= 1 || wd <= rwd ) throwResume ExceptionInst( cstring_length ); // minimum 1 character, null terminator, plus subset
     427                if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset
    429428                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;
    433429        }
    434430        _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) {
     
    439435                return (_Istream_Cquoted &)f;
    440436        }
     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        }
    441440        _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; }
    442441        _Istream_Cstr & excl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Cstr &)f; }
    443         _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cstr)@{ { .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; }
     442        _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cwidth)@{ .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
    444443        _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; }
    445444        _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; }
     
    451450        istype & ?|?( istype & is, _Istream_Cquoted f );
    452451        istype & ?|?( istype & is, _Istream_Cstr f );
    453         static inline istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
     452        static inline {
     453                istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
     454        } // distribution
    454455} // distribution
    455456
  • src/AST/Attribute.hpp

    ra4da45e r0522ebe  
    99// Author           : Aaron B. Moss
    1010// Created On       : Fri May 10 10:30:00 2019
    11 // Last Modified By : Peter A. Buhr
     11// Last Modified By : Aaron B. Moss
    1212// Created On       : Fri May 10 10:30:00 2019
    13 // Update Count     : 2
     13// Update Count     : 1
    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

    ra4da45e r0522ebe  
    366366
    367367        /// Passes extra layout arguments for sized polymorphic type parameters.
    368         void passTypeVars(
     368        ast::vector<ast::Expr>::iterator passTypeVars(
    369369                ast::ApplicationExpr * expr,
    370                 ast::vector<ast::Expr> & extraArgs,
    371370                ast::FunctionType const * funcType );
    372371        /// Wraps a function application with a new temporary for the
     
    388387        /// parameter list. arg should point into expr's argument list.
    389388        void boxParams(
    390                 ast::ApplicationExpr * expr,
    391                 ast::Type const * polyRetType,
     389                ast::ApplicationExpr const * expr,
     390                ast::vector<ast::Expr>::iterator arg,
    392391                ast::FunctionType const * function,
    393392                const TypeVarMap & typeVars );
     
    396395        void addInferredParams(
    397396                ast::ApplicationExpr * expr,
    398                 ast::vector<ast::Expr> & extraArgs,
     397                ast::vector<ast::Expr>::iterator arg,
    399398                ast::FunctionType const * functionType,
    400399                const TypeVarMap & typeVars );
     
    637636        ast::Expr const * ret = expr;
    638637
     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
    639642        TypeVarMap exprTypeVars;
    640643        makeTypeVarMap( function, exprTypeVars );
     
    659662        }
    660663
    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 );
     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 );
    667673        passAdapters( mutExpr, function, exprTypeVars );
    668674
     
    760766}
    761767
    762 void CallAdapter::passTypeVars(
     768ast::vector<ast::Expr>::iterator CallAdapter::passTypeVars(
    763769                ast::ApplicationExpr * expr,
    764                 ast::vector<ast::Expr> & extraArgs,
    765770                ast::FunctionType const * function ) {
    766771        assert( typeSubs );
     772        ast::vector<ast::Expr>::iterator arg = expr->args.begin();
    767773        // Pass size/align for type variables.
    768774        for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) {
     
    774780                                                   toString( typeSubs ).c_str(), typeVar->typeString().c_str() );
    775781                }
    776                 extraArgs.emplace_back(
     782                arg = expr->args.insert( arg,
    777783                        new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) );
    778                 extraArgs.emplace_back(
     784                arg++;
     785                arg = expr->args.insert( arg,
    779786                        new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) );
    780         }
     787                arg++;
     788        }
     789        return arg;
    781790}
    782791
     
    904913
    905914void CallAdapter::boxParams(
    906                 ast::ApplicationExpr * expr,
    907                 ast::Type const * polyRetType,
     915                ast::ApplicationExpr const * expr,
     916                ast::vector<ast::Expr>::iterator arg,
    908917                ast::FunctionType const * function,
    909918                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 
    914919        for ( auto param : function->params ) {
    915920                assertf( arg != expr->args.end(),
     
    923928void CallAdapter::addInferredParams(
    924929                ast::ApplicationExpr * expr,
    925                 ast::vector<ast::Expr> & extraArgs,
     930                ast::vector<ast::Expr>::iterator arg,
    926931                ast::FunctionType const * functionType,
    927932                TypeVarMap const & typeVars ) {
     933        ast::vector<ast::Expr>::iterator cur = arg;
    928934        for ( auto assertion : functionType->assertions ) {
    929935                auto inferParam = expr->inferred.inferParams().find(
     
    934940                ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr );
    935941                boxParam( newExpr, assertion->result, typeVars );
    936                 extraArgs.emplace_back( newExpr.release() );
     942                cur = expr->args.insert( cur, newExpr.release() );
     943                ++cur;
    937944        }
    938945}
     
    11091116        );
    11101117
    1111         for ( auto const & [assertArg, assertParam, assertReal] : group_iterate(
    1112                         realType->assertions, adapterType->assertions, adaptee->assertions ) ) {
     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 );
    11131123                adapteeApp->args.push_back( makeAdapterArg(
    11141124                        assertParam->var, assertArg->var->get_type(),
     
    19671977        bool hasDynamicLayout = false;
    19681978
    1969         for ( auto const & [baseParam, typeParam] : group_iterate(
    1970                         baseParams, typeParams ) ) {
     1979        for ( auto pair : group_iterate( baseParams, typeParams ) ) {
     1980                auto baseParam = std::get<0>( pair );
     1981                auto typeParam = std::get<1>( pair );
    19711982                if ( !baseParam->isComplete() ) continue;
    19721983                ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>();
  • src/InitTweak/FixInit.cpp

    ra4da45e r0522ebe  
    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                         const ast::Stmt * callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
     1136                        ast::ptr<ast::Stmt> callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );
    11371137
    11381138                        if ( callStmt ) {
  • src/InitTweak/GenInit.cc

    ra4da45e r0522ebe  
    239239                                        if ( varExpr->var == retVal ) return stmt;
    240240                                }
    241                                 const ast::Stmt * ctorStmt = genCtorDtor(
     241                                ast::ptr<ast::Stmt> ctorStmt = genCtorDtor(
    242242                                        retVal->location, "?{}", retVal, stmt->expr );
    243243                                assertf( ctorStmt,
     
    327327void ManagedTypes::endScope() { managedTypes.endScope(); }
    328328
    329 const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg ) {
     329ast::ptr<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

    ra4da45e r0522ebe  
    3333
    3434/// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument
    35 const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr );
     35ast::ptr<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

    ra4da45e r0522ebe  
    1010// Created On       : Sat May 16 12:34:05 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb 23 18:25:57 2024
    13 // Update Count     : 1533
     12// Last Modified On : Thu Dec 14 19:05:17 2023
     13// Update Count     : 1407
    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, ' ' );
    164                         ast::print( os, attr, indent + 2 );
     163                        os << string( indent + 4, ' ' ) << attr->name.c_str() << endl;
    165164                } // for
    166165        } // if
     
    538537} // DeclarationNode::checkSpecifiers
    539538
    540 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q, bool copyattr ) {
     539DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q ) {
    541540        funcSpecs |= q->funcSpecs;
    542541        storageClasses |= q->storageClasses;
    543542
    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
     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 );
    552549
    553550        return this;
     
    684681}
    685682
    686 DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) {
     683DeclarationNode * DeclarationNode::addType( DeclarationNode * o ) {
    687684        if ( o ) {
    688685                checkSpecifiers( o );
    689                 copySpecifiers( o, copyattr );
     686                copySpecifiers( o );
    690687                if ( o->type ) {
    691688                        if ( ! type ) {
    692689                                if ( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) {
    693                                         // Hide type information aggregate instances.
    694690                                        type = new TypeData( TypeData::AggregateInst );
    695                                         type->aggInst.aggregate = o->type;      // change ownership
    696                                         type->aggInst.aggregate->aggregate.attributes.swap( o->attributes ); // change ownership                                       
     691                                        type->aggInst.aggregate = o->type;
    697692                                        if ( o->type->kind == TypeData::Aggregate ) {
    698693                                                type->aggInst.hoistType = o->type->aggregate.body;
     
    705700                                        type = o->type;
    706701                                } // if
    707                                 o->type = nullptr;                                              // change ownership
     702                                o->type = nullptr;
    708703                        } else {
    709704                                addTypeToType( o->type, type );
     
    958953}
    959954
    960 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) {
     955DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o ) {
    961956        if ( ! o ) return nullptr;
    962957
    963         o->copySpecifiers( this, copyattr );
     958        o->copySpecifiers( this );
    964959        if ( type ) {
    965960                TypeData * srcType = type;
     
    1004999                        DeclarationNode * newnode = new DeclarationNode;
    10051000                        newnode->type = ret;
    1006                         if ( ret->kind == TypeData::Aggregate ) {
    1007                                 newnode->attributes.swap( ret->aggregate.attributes );
    1008                         } // if
    10091001                        return newnode;
    10101002                } // if
     
    11181110                                        if ( extr->type->kind == TypeData::Aggregate ) {
    11191111                                                // typedef struct { int A } B is the only case?
    1120                                                 extracted_named = ! extr->type->aggregate.anon;
     1112                                                extracted_named = !extr->type->aggregate.anon;
    11211113                                        } else if ( extr->type->kind == TypeData::Enum ) {
    11221114                                                // typedef enum { A } B is the only case?
    1123                                                 extracted_named = ! extr->type->enumeration.anon;
     1115                                                extracted_named = !extr->type->enumeration.anon;
    11241116                                        } else {
    11251117                                                extracted_named = true;
  • src/Parser/DeclarationNode.h

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

    ra4da45e r0522ebe  
    1010// Created On       : Sat May 16 15:12:51 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb 23 08:58:30 2024
    13 // Update Count     : 734
     12// Last Modified On : Thu Dec 14 18:59:12 2023
     13// Update Count     : 684
    1414//
    1515
     
    2020
    2121#include "AST/Decl.hpp"            // for AggregateDecl, ObjectDecl, TypeDe...
    22 #include "AST/Attribute.hpp"       // for Attribute
    2322#include "AST/Init.hpp"            // for SingleInit, ListInit
    2423#include "AST/Print.hpp"           // for print
    2524#include "Common/SemanticError.h"  // for SemanticError
    2625#include "Common/utility.h"        // for splice, spliceBegin
    27 #include "Common/Iterate.hpp"      // for reverseIterate
    2826#include "Parser/ExpressionNode.h" // for ExpressionNode
    2927#include "Parser/StatementNode.h"  // for StatementNode
     
    201199                newtype->aggregate.kind = aggregate.kind;
    202200                newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr;
    203                 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;
    204201                newtype->aggregate.params = maybeCopy( aggregate.params );
    205202                newtype->aggregate.actuals = maybeCopy( aggregate.actuals );
    206203                newtype->aggregate.fields = maybeCopy( aggregate.fields );
    207                 newtype->aggregate.attributes = aggregate.attributes;
    208204                newtype->aggregate.body = aggregate.body;
    209205                newtype->aggregate.anon = aggregate.anon;
    210206                newtype->aggregate.tagged = aggregate.tagged;
     207                newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;
    211208                break;
    212209        case AggregateInst:
     
    339336                } // if
    340337                if ( aggregate.body ) {
    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
     338                        os << string( indent + 2, ' ' ) << " with body" << endl;
    349339                } // if
    350340                break;
     
    368358                } // if
    369359                if ( enumeration.body ) {
    370                         os << string( indent + 2, ' ' ) << "with body" << endl;
     360                        os << string( indent + 2, ' ' ) << " with body" << endl;
    371361                } // if
    372362                if ( base ) {
     
    10981088
    10991089ast::BaseInstType * buildComAggInst(
    1100                 const TypeData * td,
     1090                const TypeData * type,
    11011091                std::vector<ast::ptr<ast::Attribute>> && attributes,
    11021092                ast::Linkage::Spec linkage ) {
    1103         switch ( td->kind ) {
     1093        switch ( type->kind ) {
    11041094        case TypeData::Enum:
    1105                 if ( td->enumeration.body ) {
     1095                if ( type->enumeration.body ) {
    11061096                        ast::EnumDecl * typedecl =
    1107                                 buildEnum( td, std::move( attributes ), linkage );
     1097                                buildEnum( type, std::move( attributes ), linkage );
    11081098                        return new ast::EnumInstType(
    11091099                                typedecl,
    1110                                 buildQualifiers( td )
     1100                                buildQualifiers( type )
    11111101                        );
    11121102                } else {
    11131103                        return new ast::EnumInstType(
    1114                                 *td->enumeration.name,
    1115                                 buildQualifiers( td )
     1104                                *type->enumeration.name,
     1105                                buildQualifiers( type )
    11161106                        );
    11171107                } // if
    11181108                break;
    11191109        case TypeData::Aggregate:
    1120                 if ( td->aggregate.body ) {
     1110                if ( type->aggregate.body ) {
    11211111                        ast::AggregateDecl * typedecl =
    1122                                 buildAggregate( td, std::move( attributes ), linkage );
    1123                         switch ( td->aggregate.kind ) {
     1112                                buildAggregate( type, std::move( attributes ), linkage );
     1113                        switch ( type->aggregate.kind ) {
    11241114                        case ast::AggregateDecl::Struct:
    11251115                        case ast::AggregateDecl::Coroutine:
     
    11281118                                return new ast::StructInstType(
    11291119                                        strict_dynamic_cast<ast::StructDecl *>( typedecl ),
    1130                                         buildQualifiers( td )
     1120                                        buildQualifiers( type )
    11311121                                );
    11321122                        case ast::AggregateDecl::Union:
    11331123                                return new ast::UnionInstType(
    11341124                                        strict_dynamic_cast<ast::UnionDecl *>( typedecl ),
    1135                                         buildQualifiers( td )
     1125                                        buildQualifiers( type )
    11361126                                );
    11371127                        case ast::AggregateDecl::Trait:
     
    11421132                        } // switch
    11431133                } else {
    1144                         switch ( td->aggregate.kind ) {
     1134                        switch ( type->aggregate.kind ) {
    11451135                        case ast::AggregateDecl::Struct:
    11461136                        case ast::AggregateDecl::Coroutine:
     
    11481138                        case ast::AggregateDecl::Thread:
    11491139                                return new ast::StructInstType(
    1150                                         *td->aggregate.name,
    1151                                         buildQualifiers( td )
     1140                                        *type->aggregate.name,
     1141                                        buildQualifiers( type )
    11521142                                );
    11531143                        case ast::AggregateDecl::Union:
    11541144                                return new ast::UnionInstType(
    1155                                         *td->aggregate.name,
    1156                                         buildQualifiers( td )
     1145                                        *type->aggregate.name,
     1146                                        buildQualifiers( type )
    11571147                                );
    11581148                        case ast::AggregateDecl::Trait:
    11591149                                return new ast::TraitInstType(
    1160                                         *td->aggregate.name,
    1161                                         buildQualifiers( td )
     1150                                        *type->aggregate.name,
     1151                                        buildQualifiers( type )
    11621152                                );
    11631153                        default:
  • src/Parser/TypeData.h

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

    ra4da45e r0522ebe  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Feb 23 18:25:46 2024
    13 // Update Count     : 6484
     12// Last Modified On : Sun Nov 26 13:18:06 2023
     13// Update Count     : 6398
    1414//
    1515
     
    102102
    103103DeclarationNode * distAttr( DeclarationNode * typeSpec, DeclarationNode * declList ) {
    104         // Distribute type specifier across all declared variables, e.g., static, const, __attribute__.
     104        // distribute declaration_specifier across all declared variables, e.g., static, const, but not __attribute__.
    105105        assert( declList );
    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..
     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
    129111        for ( DeclarationNode * cur = dynamic_cast<DeclarationNode *>( declList->get_next() ); cur != nullptr; cur = dynamic_cast<DeclarationNode *>( cur->get_next() ) ) {
    130                 cl->cloneBaseType( cur, copyattr );                             // cur is modified
     112                cl->cloneBaseType( cur );
    131113        } // for
    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!!!
     114        declList->addType( cl );
     115        // printf( "distAttr3 declList %p\n", declList ); declList->print( std::cout, 0 );
    136116        return declList;
    137117} // distAttr
     
    212192                fieldList = DeclarationNode::newName( nullptr );
    213193        } // if
     194//      return distAttr( typeSpec, fieldList );                         // mark all fields in list
    214195
    215196        // printf( "fieldDecl3 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout, 0 );
    216         DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list
     197        DeclarationNode * temp = distAttr( typeSpec, fieldList );                               // mark all fields in list
    217198        // printf( "fieldDecl4 temp %p\n", temp ); temp->print( std::cout, 0 );
    218199        return temp;
     
    780761        | string_literal '`' identifier                                         // CFA, postfix call
    781762                { $$ = 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                 //   }
    800763        | postfix_expression '.' identifier
    801764                { $$ = 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 
    807765        | postfix_expression '.' INTEGERconstant                        // CFA, tuple index
    808766                { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_constantInteger( yylloc, *$3 ) ) ); }
     
    10811039        | logical_OR_expression '?' comma_expression ':' conditional_expression
    10821040                { $$ = new ExpressionNode( build_cond( yylloc, $1, $3, $5 ) ); }
     1041                // FIX ME: computes $1 twice
    10831042        | logical_OR_expression '?' /* empty */ ':' conditional_expression // GCC, omitted first operand
    1084                 { $$ = new ExpressionNode( build_cond( yylloc, $1, nullptr, $4 ) ); }
     1043                { $$ = new ExpressionNode( build_cond( yylloc, $1, $1->clone(), $4 ) ); }
    10851044        ;
    10861045
     
    18971856declaration_list:
    18981857        declaration
    1899         | declaration_list declaration          { $$ = $1->appendList( $2 ); }
     1858        | declaration_list declaration
     1859                { $$ = $1->appendList( $2 ); }
    19001860        ;
    19011861
     
    19301890declaration:                                                                                    // old & new style declarations
    19311891        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                }
    19321898        | cfa_declaration ';'                                                           // CFA
    19331899        | static_assert                                                                         // C11
     
    23822348sue_declaration_specifier:                                                              // struct, union, enum + storage class + type specifier
    23832349        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                }
    23842356        | declaration_qualifier_list sue_type_specifier
    23852357                { $$ = $2->addQualifiers( $1 ); }
     
    23922364sue_type_specifier:                                                                             // struct, union, enum + type specifier
    23932365        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                }
    23942372        | type_qualifier_list
    23952373                { if ( $1->type != nullptr && $1->type->forall ) forall = true; } // remember generic type
     
    24642442elaborated_type:                                                                                // struct, union, enum
    24652443        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                }
    24662450        | enum_type
    24672451        ;
     
    26952679                { $$ = DeclarationNode::newEnum( nullptr, $4, true, false )->addQualifiers( $2 ); }
    26962680        | ENUM attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule
    2697                 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2681                { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    26982682        | ENUM attribute_list_opt identifier
    26992683                { typedefTable.makeTypedef( *$3, "enum_type 1" ); }
     
    27102694                }
    27112695        | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // unqualified type name
    2712                 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2696                { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    27132697        | ENUM '(' ')' attribute_list_opt '{' enumerator_list comma_opt '}'
    27142698                {
     
    27162700                }
    27172701        | ENUM '(' ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule
    2718                 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
     2702                { SemanticError( yylloc, "syntax error, hiding '!' the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }
    27192703        | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt identifier attribute_list_opt
    27202704                {
     
    31933177                        // unit, which is a dubious task, especially because C uses name rather than structural typing; hence it is
    31943178                        // disallowed at the moment.
    3195                         if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static &&
    3196                                  $1->type && $1->type->kind == TypeData::AggregateInst ) {
     3179                        if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static && $1->type && $1->type->kind == TypeData::AggregateInst ) {
    31973180                                if ( $1->type->aggInst.aggregate->kind == TypeData::Enum && $1->type->aggInst.aggregate->enumeration.anon ) {
    31983181                                        SemanticError( yylloc, "extern anonymous enumeration is currently unimplemented." ); $$ = nullptr;
  • src/ResolvExpr/CandidateFinder.cpp

    ra4da45e r0522ebe  
    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                CandidateFinder finder1( context, tenv );
    16201621                ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );
    1621                 CandidateFinder finder1( context, tenv );
    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();
    16291626                CandidateFinder finder2( context, tenv );
    16301627                finder2.allowVoid = true;
    1631                 finder2.find( arg2, ResolveMode::withAdjustment() );
     1628                finder2.find( conditionalExpr->arg2, ResolveMode::withAdjustment() );
    16321629                if ( finder2.candidates.empty() ) return;
    16331630
     
    19001897                                        CandidateRef newCand =
    19011898                                                std::make_shared<Candidate>(
    1902                                                         newExpr, copy( tenv ), ast::OpenVarSet{},
     1899                                                        newExpr, copy( tenv ), ast::OpenVarSet{}, 
    19031900                                                        ast::AssertionSet{}, Cost::zero, cost
    19041901                                                );
    1905 
     1902                                       
    19061903                                        if (newCand->expr->env) {
    19071904                                                newCand->env.add(*newCand->expr->env);
  • src/ResolvExpr/ResolveTypeof.cc

    ra4da45e r0522ebe  
    3535
    3636struct ResolveTypeof : public ast::WithShortCircuiting {
    37         const ResolveContext & context;
     37    const ResolveContext & context;
    3838
    39         ResolveTypeof( const ResolveContext & context ) : context( context ) {}
     39                ResolveTypeof( const ResolveContext & context ) :
     40                        context( context ) {}
    4041
    41         void previsit( const ast::TypeofType * ) { visit_children = false; }
     42                void previsit( const ast::TypeofType * ) { visit_children = false; }
    4243
    43         const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
    44                 // pass on null expression
    45                 if ( ! typeofType->expr ) return typeofType;
     44        const ast::Type * postvisit( const ast::TypeofType * typeofType ) {
     45        // pass on null expression
     46            if ( ! typeofType->expr ) return typeofType;
    4647
    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                 }
     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        }
    5960
    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                 }
     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        }
    7374
    74                 return newType.release();
    75         }
     75        return newType.release();
     76    }
    7677};
    7778
     
    110111
    111112const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) {
    112         if ( decl->isTypeFixed ) {
    113                 return decl;
    114         }
     113    if (decl->isTypeFixed) {
     114        return decl;
     115    }
    115116
    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         }
     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    }
    123124
    124         // Do not mangle unnamed variables.
    125         if ( !mutDecl->name.empty() ) {
    126                 mutDecl->mangleName = Mangle::mangle(mutDecl);
    127         }
     125    // Do not mangle unnamed variables.
     126    if (!mutDecl->name.empty()) {
     127        mutDecl->mangleName = Mangle::mangle(mutDecl);
     128    }
    128129
    129         mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
    130         mutDecl->isTypeFixed = true;
    131         return mutDecl;
     130    mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);
     131    mutDecl->isTypeFixed = true;
     132    return mutDecl;
    132133}
    133134
    134 const ast::ObjectDecl *fixObjectInit(
    135                 const ast::ObjectDecl *decl, const ResolveContext &context) {
    136         if ( decl->isTypeFixed ) {
    137                 return decl;
    138         }
     135const ast::ObjectDecl *fixObjectInit(const ast::ObjectDecl *decl,
     136                                    const ResolveContext &context) {
     137    if (decl->isTypeFixed) {
     138        return decl;
     139    }
    139140
    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;
     141    auto mutDecl = mutate(decl);
    146142
    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;
     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;
    180189}
    181190
    182 } // namespace ResolvExpr
     191}  // namespace ResolvExpr
    183192
    184193// Local Variables: //
  • src/ResolvExpr/Resolver.cc

    ra4da45e r0522ebe  
    5050
    5151namespace ResolvExpr {
    52 
    53 namespace {
    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);
     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; }
     60                        }
     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);
     67                                        }
    6868                                }
    6969                        }
    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;
     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                                                                }
    9595                                                        }
    9696                                                }
    9797                                        }
     98                                        visit_children = false;
    9899                                }
    99                                 visit_children = false;
    100                         }
    101                 }
    102         };
    103 } // anonymous namespace
    104 
    105 /// Check if this expression is or includes a deleted expression
    106 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) {
    107         return ast::Pass<DeleteFinder>::read( expr );
    108 }
    109 
    110 namespace {
    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
     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 );
    162153                                        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
    235 void 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 
    248 ast::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.
    269 ast::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 
    278 namespace {
    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                 }
    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 
    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" ) );
     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;
    468162                                        }
    469163                                }
    470                                 return mutAttr;
    471                         } else if (attr->params.size() > 1) {
    472                                 SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) );
     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                        }
     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
     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                                };
    473603                        } 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         } 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 @=." );
     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                                                }
    6251176                                        }
    6261177                                }
    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 
    641 void 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 
    653 void Resolver::previsit( const ast::StructDecl * structDecl ) {
    654         previsit(static_cast<const ast::AggregateDecl *>(structDecl));
    655         managedTypes.handleStruct(structDecl);
    656 }
    657 
    658 void 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 
    665 const 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 
    673 template< typename PtrType >
    674 const 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 
    685 const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) {
    686         return handlePtrType( at, context );
    687 }
    688 
    689 const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) {
    690         return handlePtrType( pt, context );
    691 }
    692 
    693 const 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 
    701 const 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 
    710 const 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 
    717 const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
    718         return ast::mutate_field(
    719                 ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) );
    720 }
    721 
    722 const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
    723         return ast::mutate_field(
    724                 whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) );
    725 }
    726 
    727 const 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 
    741 const 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 
    750 const 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 
    771 const 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 
    784 const 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 
    794 const 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 
    809 const 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 
    825 const 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 
    840 const 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 
    1105 const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) {
    1106         auto mutStmt = mutate(withStmt);
    1107         resolveWithExprs(mutStmt->exprs, stmtsToAddBefore);
    1108         return mutStmt;
    1109 }
    1110 
    1111 void 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 
    1135 const 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 );
    1171                                         }
    1172                                 }
    1173                         }
    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 
    1184 const 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 
    1207 const 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.
    1229 bool 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;
    1237                         return false;
    1238                 }
    1239         }
    1240         return true;
    1241 }
     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        }
    12421246
    12431247} // namespace ResolvExpr
  • src/SymTab/GenImplicitCall.cpp

    ra4da45e r0522ebe  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // GenImplicitCall.cpp -- Generate code for implicit operator calls.
     7// GenImplicitCall.hpp --
    88//
    99// Author           : Andrew Beach
     
    3131namespace {
    3232
    33 using Inserter = std::back_insert_iterator<std::list<ast::ptr<ast::Stmt>>>;
    34 
     33template< typename OutIter >
    3534ast::ptr< ast::Stmt > genCall(
    3635        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    37         const CodeLocation & loc, const std::string & fname, Inserter && out,
     36        const CodeLocation & loc, const std::string & fname, OutIter && out,
    3837        const ast::Type * type, const ast::Type * addCast, LoopDirection forward = LoopForward );
    3938
     
    4241/// optionally returns a statement which must be inserted prior to the containing loop, if
    4342/// there is one
     43template< typename OutIter >
    4444ast::ptr< ast::Stmt > genScalarCall(
    4545        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    46         const CodeLocation & loc, std::string fname, Inserter && out, const ast::Type * type,
     46        const CodeLocation & loc, std::string fname, OutIter && 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
     99template< typename OutIter >
    99100void genArrayCall(
    100101        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    101         const CodeLocation & loc, const std::string & fname, Inserter && out,
     102        const CodeLocation & loc, const std::string & fname, OutIter && out,
    102103        const ast::ArrayType * array, const ast::Type * addCast = nullptr,
    103104        LoopDirection forward = LoopForward
     
    165166}
    166167
     168template< typename OutIter >
    167169ast::ptr< ast::Stmt > genCall(
    168170        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    169         const CodeLocation & loc, const std::string & fname, Inserter && out,
     171        const CodeLocation & loc, const std::string & fname, OutIter && out,
    170172        const ast::Type * type, const ast::Type * addCast, LoopDirection forward
    171173) {
    172174        if ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) {
    173175                genArrayCall(
    174                         srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), at, addCast,
     176                        srcParam, dstParam, loc, fname, std::forward< OutIter >(out), at, addCast,
    175177                        forward );
    176178                return {};
    177179        } else {
    178180                return genScalarCall(
    179                         srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), type, addCast );
     181                        srcParam, dstParam, loc, fname, std::forward< OutIter >( out ), type, addCast );
    180182        }
    181183}
     
    183185} // namespace
    184186
    185 const ast::Stmt * genImplicitCall(
     187ast::ptr< ast::Stmt > genImplicitCall(
    186188        InitTweak::InitExpander & srcParam, const ast::Expr * dstParam,
    187189        const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj,
     
    189191) {
    190192        // unnamed bit fields are not copied as they cannot be accessed
    191         if ( isUnnamedBitfield( obj ) ) return nullptr;
     193        if ( isUnnamedBitfield( obj ) ) return {};
    192194
    193195        ast::ptr< ast::Type > addCast;
     
    197199        }
    198200
    199         std::list< ast::ptr< ast::Stmt > > stmts;
     201        std::vector< ast::ptr< ast::Stmt > > stmts;
    200202        genCall(
    201203                srcParam, dstParam, loc, fname, back_inserter( stmts ), obj->type, addCast, forward );
    202204
    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;
     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        }
    213219}
    214220
     
    220226// compile-command: "make install" //
    221227// End: //
     228
     229
  • src/SymTab/GenImplicitCall.hpp

    ra4da45e r0522ebe  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // GenImplicitCall.hpp -- Generate code for implicit operator calls.
     7// GenImplicitCall.hpp --
    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 const ast::Stmt * genImplicitCall(
     27ast::ptr<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

    ra4da45e r0522ebe  
    133133        /// Generates a single struct member operation.
    134134        /// (constructor call, destructor call, assignment call)
    135         const ast::Stmt * makeMemberOp(
     135        // This is managed because it uses another helper that returns a ast::ptr.
     136        ast::ptr<ast::Stmt> makeMemberOp(
    136137                const CodeLocation& location,
    137138                const ast::ObjectDecl * dstParam, const ast::Expr * src,
     
    524525}
    525526
    526 const ast::Stmt * StructFuncGenerator::makeMemberOp(
     527ast::ptr<ast::Stmt> StructFuncGenerator::makeMemberOp(
    527528                const CodeLocation& location, const ast::ObjectDecl * dstParam,
    528529                const ast::Expr * src, const ast::ObjectDecl * field,
     
    539540                )
    540541        );
    541         const ast::Stmt * stmt = genImplicitCall(
     542        auto stmt = genImplicitCall(
    542543                srcParam, dstSelect, location, func->name,
    543544                field, direction
     
    597598                        location, field, new ast::VariableExpr( location, srcParam )
    598599                ) : nullptr;
    599                 const ast::Stmt * stmt =
     600                ast::ptr<ast::Stmt> stmt =
    600601                        makeMemberOp( location, dstParam, srcSelect, field, func, direction );
    601602
    602603                if ( nullptr != stmt ) {
    603                         stmts->kids.emplace_back( stmt );
     604                        stmts->kids.push_back( stmt );
    604605                }
    605606        }
     
    622623        for ( auto param = params.begin() + 1 ; current != end ; ++current ) {
    623624                const ast::ptr<ast::Decl> & member = *current;
    624                 const ast::Stmt * stmt = nullptr;
     625                ast::ptr<ast::Stmt> stmt = nullptr;
    625626                auto field = member.as<ast::ObjectDecl>();
    626627                // Not sure why it could be null.
     
    640641
    641642                if ( nullptr != stmt ) {
    642                         stmts->kids.emplace_back( stmt );
     643                        stmts->kids.push_back( stmt );
    643644                }
    644645        }
  • src/main.cc

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

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

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

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

    ra4da45e r0522ebe  
    707014
    717115
    72 16 get this line
    73 17 @# this line 1)-{}
    74 18 @# this line 1)-{}
    75 19 abc
    76 20 abc 
    77 21  d d
    78 
    79 d
    80 22              ZC44%
    81 23              ZC44%
    82721 yyyyyyyyyyyyyyyyyyyy
    83732 abcxxx
     
    958514
    968615
    97 16 get this line
    98 17 @# this line 1)-{}
    99 18 @# this line 1)-{}
    100 19 abc
    101 20 abc 
    102 21  d d
    103 
    104 d
    105 22              ZC44%
    106 23              ZC44%
  • tests/collections/.in/string-istream-manip.txt

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

    ra4da45e r0522ebe  
    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;
     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";
     22}
     23
     24int 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;
     167                sin | "\n";
     168
     169                s = "q";
     170                sin | incl( "abc", s );                         sout | "14" | s;
     171                s = "q";
     172                sin | excl( "u", s );                           sout | "15" | s;
     173                sin | skip( "u" );
     174                sin | "\n";
    20175        }
    21         sout | "Unable to force size" | desiredSize | "in 1,000,000 tries";
     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    }
    22203}
    23 
    24 int 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         // 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;
    203                 sin | "\n";
    204 
    205                 s = "q";
    206                 sin | incl( "abc", s );                                                 sout | "14" | s;
    207                 s = "q";
    208                 sin | excl( "u", s );                                                   sout | "15" | s;
    209                 sin | skip( "u" );
    210                 sin | "\n";
    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         }
    221 }
  • tests/configs/parsebools.cfa

    ra4da45e r0522ebe  
    1010// Author           : Thierry Delisle
    1111// Created On       : Wed Oct 12 15:28:01 2022
    12 // Last Modified By : Peter A. Buhr
    13 // Last Modified On : Sun Feb 11 09:26:13 2024
    14 // Update Count     : 2
     12// Last Modified By :
     13// Last Modified On :
     14// Update Count     :
    1515//
    1616
    1717#include <fstream.hfa>
     18
    1819#include "../meta/fork+exec.hfa"
    1920
     
    2122#include <parseargs.hfa>
    2223
    23 int main( int argc, char * argv[] ) {
     24int main(int argc, char * argv[]) {
    2425        check_main(argv[0]);
    2526
     
    4041
    4142        char **left;
    42         parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );
     43        parse_args( options, "[OPTIONS]...\ntesting bool parameters", left);
    4344
    4445        sout | "yes/no     :" | YN;
     
    5051}
    5152
    52 int 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 );
     53int 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);
    5960                }
    60         } else {
    61                 int status = do_wait( child );
    62                 print_status( status );
    63         } // if
     61        }
     62        else {
     63                int status = do_wait(child);
     64                print_status(status);
     65        }
    6466
    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
     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        }
    7679
    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
     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        }
    8892
    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
     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        }
    100105
    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
     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        }
    112118
    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
     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        }
    124131
    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
     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        }
    136144
    137         printf( "All Done!\n" );
     145        printf("All Done!\n");
    138146
    139147        return 0;
  • tests/configs/parsenums.cfa

    ra4da45e r0522ebe  
    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                 } // 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" );
     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                }
     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");
    204216
    205217        return 0;
  • tests/configs/usage.cfa

    ra4da45e r0522ebe  
    1010// Author           : Thierry Delisle
    1111// Created On       : Wed Oct 12 15:28:01 2022
    12 // Last Modified By : Peter A. Buhr
    13 // Last Modified On : Sun Feb 11 09:29:51 2024
    14 // Update Count     : 1
     12// Last Modified By :
     13// Last Modified On :
     14// Update Count     :
    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         } else {
    30                 int status = do_wait( child );
    31                 print_status( status );
    32         } // if
     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        }
    3334
    3435        sout | "No args, with errors";
    35         if ( pid_t child = strict_fork(); child == 0 ) {
     36        if(pid_t child = strict_fork(); child == 0) {
    3637                array( cfa_option, 0 ) opts;
    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
     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        }
    4244
    4345        sout | "Args with short names only:";
    44         if ( pid_t child = strict_fork(); child == 0 ) {
     46        if(pid_t child = strict_fork(); child == 0) {
    4547                int a, b, c;
    4648                array( cfa_option, 3 ) opts;
     
    4850                opts[1] = (cfa_option){'b', "", "Second arg", b };
    4951                opts[2] = (cfa_option){'c', "", "Third arg", c };
    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
     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        }
    5558
    5659        sout | "Args with long names only:";
    57         if ( pid_t child = strict_fork(); child == 0 ) {
     60        if(pid_t child = strict_fork(); child == 0) {
    5861                int a, b, c;
    5962                array( cfa_option, 3 ) opts;
     
    6164                opts[1] = (cfa_option){'\0', "BB", "Second arg", b };
    6265                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    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
     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        }
    6872
    6973        sout | "Mix of short and long args:";
    70         if ( pid_t child = strict_fork(); child == 0 ) {
     74        if(pid_t child = strict_fork(); child == 0) {
    7175                int a, b, c;
    7276                array( cfa_option, 3 ) opts;
     
    7478                opts[1] = (cfa_option){'b', "BBBB", "Second arg", b };
    7579                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    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
     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        }
    8186
    8287        sout | "Mix of short and long and some missing description:";
    83         if ( pid_t child = strict_fork(); child == 0 ) {
     88        if(pid_t child = strict_fork(); child == 0) {
    8489                int a, b, c;
    8590                array( cfa_option, 3 ) opts;
     
    8792                opts[1] = (cfa_option){'b', "BBBB", "", b };
    8893                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    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
     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        }
    94100
    95101        sout | "Mix of short and long and some long description:";
    96         if ( pid_t child = strict_fork(); child == 0 ) {
     102        if(pid_t child = strict_fork(); child == 0) {
    97103                int a, b, c;
    98104                array( cfa_option, 3 ) opts;
     
    100106                opts[1] = (cfa_option){'b', "BBBB", "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", b };
    101107                opts[2] = (cfa_option){'\0', "CC", "Third arg", c };
    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
     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        }
    107114}
    108115
    109116// no used
    110 static int true_main( const char * path, char * env[]) { return 0; }
     117static int true_main(const char * path, char * env[]) { return 0; }
  • tests/errors/.expect/declaration.txt

    ra4da45e r0522ebe  
    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

    ra4da45e r0522ebe  
    22#include <thread.hfa>
    33#include <mutex_stmt.hfa>
    4 #include <Exception.hfa>
    54
    6 ExceptionDecl( num_ping_pongs, int num; );
     5exception num_ping_pongs { int num; };
     6vtable(num_ping_pongs) num_ping_pongs_vt;
    77
    88thread Ping_Pong {
     
    1616        this.name = name;
    1717        cnt = 0;
    18         ?{}( except, ExceptionArgs( num_ping_pongs, 0 ) );
     18        ?{}( except, &num_ping_pongs_vt, 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                 resumeAt( pong, ExceptionInst( num_ping_pongs, 0 ) );
     51                num_ping_pongs except{ &num_ping_pongs_vt, 0 };
     52                resumeAt( pong, except );
    5253        }
    5354        sout | "main end";
  • tests/io/.in/manipulatorsInput.txt

    ra4da45e r0522ebe  
    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

    ra4da45e r0522ebe  
    77// Created On       : Sat Jun  8 17:58:54 2019
    88// Last Modified By : Peter A. Buhr
    9 // Last Modified On : Mon Feb  5 21:54:49 2024
    10 // Update Count     : 134
     9// Last Modified On : Sun Jan 28 11:59:55 2024
     10// Update Count     : 133
    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" ) | nl;
     133                sin | skip( "u" ) | "\n";
    134134                sin | getline( wdi( sizeof(s), s ) );                   sout | "16" | s;
    135                 sin | getline( wdi( sizeof(s), s ), '%' ) | nl; sout | "17" | s;
    136                 sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | nl; sout | "18" | s;
     135                sin | getline( wdi( sizeof(s), s ), '%' ) | "\n"; sout | "17" | s;
     136                sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | "\n"; sout | "18" | s;
    137137
    138138                sin | quoted( wdi( sizeof(s), s ) );                    sout | "19" | s;
  • tools/cfa.nanorc

    ra4da45e r0522ebe  
    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)\>"
     12color green "\<(static|const|extern|(un)?signed|inline|sizeof|vtable)\>"
    1213color green "\<((s?size)|one|zero|((u_?)?int(8|16|32|64|ptr)))_t\>"
    13 color green "\<(static|const|extern|(un)?signed|inline)\>"
    14 color green "\<(typeof|vtable|sizeof|alignof)\>"
    1514
    1615# Declarations
Note: See TracChangeset for help on using the changeset viewer.