Changes in / [a4da45e:0522ebe]
- Files:
-
- 2 added
- 3 deleted
- 51 edited
-
doc/LaTeXmacros/common.sty (modified) (6 diffs)
-
doc/LaTeXmacros/common.tex (modified) (6 diffs)
-
doc/LaTeXmacros/lstlang.sty (modified) (2 diffs)
-
doc/bibliography/pl.bib (modified) (2 diffs)
-
doc/proposals/enum.tex (modified) (11 diffs)
-
doc/theses/jiada_liang_MMath/CFAenum.tex (deleted)
-
doc/theses/jiada_liang_MMath/background.tex (modified) (1 diff)
-
doc/theses/jiada_liang_MMath/content1.tex (added)
-
doc/theses/jiada_liang_MMath/content2.tex (added)
-
doc/theses/jiada_liang_MMath/implementation.tex (deleted)
-
doc/theses/jiada_liang_MMath/intro.tex (modified) (1 diff)
-
doc/theses/jiada_liang_MMath/relatedwork.tex (deleted)
-
doc/theses/jiada_liang_MMath/uw-ethesis-frontpgs.tex (modified) (1 diff)
-
doc/theses/jiada_liang_MMath/uw-ethesis.tex (modified) (2 diffs)
-
doc/user/user.tex (modified) (12 diffs)
-
libcfa/src/Exception.hfa (modified) (1 diff)
-
libcfa/src/bits/signal.hfa (modified) (2 diffs)
-
libcfa/src/collections/string.cfa (modified) (2 diffs)
-
libcfa/src/collections/string.hfa (modified) (3 diffs)
-
libcfa/src/collections/string_res.cfa (modified) (38 diffs)
-
libcfa/src/collections/string_res.hfa (modified) (2 diffs)
-
libcfa/src/fstream.cfa (modified) (9 diffs)
-
libcfa/src/fstream.hfa (modified) (6 diffs)
-
libcfa/src/iostream.cfa (modified) (16 diffs)
-
libcfa/src/iostream.hfa (modified) (6 diffs)
-
src/AST/Attribute.hpp (modified) (2 diffs)
-
src/GenPoly/Box.cpp (modified) (12 diffs)
-
src/InitTweak/FixInit.cpp (modified) (1 diff)
-
src/InitTweak/GenInit.cc (modified) (2 diffs)
-
src/InitTweak/GenInit.h (modified) (1 diff)
-
src/Parser/DeclarationNode.cc (modified) (8 diffs)
-
src/Parser/DeclarationNode.h (modified) (3 diffs)
-
src/Parser/TypeData.cc (modified) (9 diffs)
-
src/Parser/TypeData.h (modified) (2 diffs)
-
src/Parser/parser.yy (modified) (14 diffs)
-
src/ResolvExpr/CandidateFinder.cpp (modified) (4 diffs)
-
src/ResolvExpr/ResolveTypeof.cc (modified) (2 diffs)
-
src/ResolvExpr/Resolver.cc (modified) (1 diff)
-
src/SymTab/GenImplicitCall.cpp (modified) (9 diffs)
-
src/SymTab/GenImplicitCall.hpp (modified) (2 diffs)
-
src/Validate/Autogen.cpp (modified) (6 diffs)
-
src/main.cc (modified) (1 diff)
-
tests/.expect/attributes.arm64.txt (modified) (1 diff)
-
tests/.expect/attributes.x64.txt (modified) (1 diff)
-
tests/.expect/attributes.x86.txt (modified) (1 diff)
-
tests/collections/.expect/string-istream-manip.txt (modified) (2 diffs)
-
tests/collections/.in/string-istream-manip.txt (modified) (2 diffs)
-
tests/collections/string-istream-manip.cfa (modified) (1 diff)
-
tests/configs/parsebools.cfa (modified) (4 diffs)
-
tests/configs/parsenums.cfa (modified) (3 diffs)
-
tests/configs/usage.cfa (modified) (7 diffs)
-
tests/errors/.expect/declaration.txt (modified) (2 diffs)
-
tests/exceptions/pingpong_nonlocal.cfa (modified) (5 diffs)
-
tests/io/.in/manipulatorsInput.txt (modified) (2 diffs)
-
tests/io/manipulatorsInput.cfa (modified) (3 diffs)
-
tools/cfa.nanorc (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
doc/LaTeXmacros/common.sty
ra4da45e r0522ebe 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sun Feb 25 17:37:46202414 %% Update Count : 6 4013 %% Last Modified On : Sun Jan 21 13:17:48 2024 14 %% Update Count : 633 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 40 40 \newcommand{\CFA}{\protect\CFAIcon\xspace} % CFA symbolic name 41 41 \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 44 43 \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 45 46 % numbers disallowed in latex variables names => use number names 46 \newcommand{\CCeleven}{\protect\CCIcon{1 \!1}\xspace}% C++11 symbolic name47 \newcommand{\CCeleven}{\protect\CCIcon{11}\xspace} % C++11 symbolic name 47 48 \newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace} % C++14 symbolic name 48 49 \newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace} % C++17 symbolic name 49 50 \newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace} % C++20 symbolic name 50 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name51 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym52 53 51 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name 54 52 … … 295 293 xleftmargin=\parindentlnth, % indent code to paragraph indentation 296 294 extendedchars=true, % allow ASCII characters in the range 128-255 295 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 297 296 mathescape=false, % disable LaTeX math escape in CFA code $...$ 298 297 keepspaces=true, % … … 305 304 literate= 306 305 % {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 307 {-}{\raisebox{-1pt}{\t tfamily-}}1306 {-}{\raisebox{-1pt}{\texttt{-}}}1 308 307 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 309 308 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 310 {'}{\ttfamily'\!}1311 309 {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1 312 310 {<-}{$\leftarrow$}2 … … 320 318 \lstset{ 321 319 language=CFA, 322 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 320 %moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 323 321 moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 324 322 %moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ … … 330 328 % inline code ©...© (copyright symbol) emacs: C-q M-) 331 329 \lstMakeShortInline© % single-character for \lstinline 332 333 330 \else% regular ASCI characters 334 335 331 \lstnewenvironment{cfa}[1][]{% necessary 336 332 \lstset{ 337 333 language=CFA, 338 334 escapechar=\$, % LaTeX escape in CFA code 335 mathescape=false, % LaTeX math escape in CFA code $...$ 339 336 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 340 337 }% lstset -
doc/LaTeXmacros/common.tex
ra4da45e r0522ebe 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sun Feb 25 18:11:56202414 %% Update Count : 61413 %% Last Modified On : Wed Jan 24 08:43:57 2024 14 %% Update Count : 593 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 40 40 \newcommand{\CFA}{\protect\CFAIcon\xspace} % CFA symbolic name 41 41 \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 44 43 \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 45 46 % numbers disallowed in latex variables names => use number names 46 \newcommand{\CCeleven}{\protect\CCIcon{1 \!1}\xspace}% C++11 symbolic name47 \newcommand{\CCeleven}{\protect\CCIcon{11}\xspace} % C++11 symbolic name 47 48 \newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace} % C++14 symbolic name 48 49 \newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace} % C++17 symbolic name 49 50 \newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace} % C++20 symbolic name 50 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name51 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym52 53 51 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name 54 52 … … 299 297 xleftmargin=\parindentlnth, % indent code to paragraph indentation 300 298 extendedchars=true, % allow ASCII characters in the range 128-255 299 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 301 300 mathescape=false, % disable LaTeX math escape in CFA code $...$ 302 301 keepspaces=true, % … … 309 308 literate= 310 309 % {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 311 {-}{\raisebox{-1pt}{\t tfamily-}}1310 {-}{\raisebox{-1pt}{\texttt{-}}}1 312 311 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 313 312 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 314 {'}{\ttfamily'\!}1315 313 {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1 316 314 {<-}{$\leftarrow$}2 … … 324 322 \lstset{ 325 323 language=CFA, 326 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 324 %moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 327 325 moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 328 326 %moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ … … 334 332 % inline code ©...© (copyright symbol) emacs: C-q M-) 335 333 \lstMakeShortInline© % single-character for \lstinline 336 337 334 \else% regular ASCI characters 338 339 335 \lstnewenvironment{cfa}[1][]{% necessary 340 336 \lstset{ 341 337 language=CFA, 342 338 escapechar=\$, % LaTeX escape in CFA code 339 mathescape=false, % LaTeX math escape in CFA code $...$ 343 340 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 344 341 }% lstset -
doc/LaTeXmacros/lstlang.sty
ra4da45e r0522ebe 8 8 %% Created On : Sat May 13 16:34:42 2017 9 9 %% Last Modified By : Peter A. Buhr 10 %% Last Modified On : Fri Feb 16 07:59:29 202411 %% Update Count : 3 710 %% Last Modified On : Thu Sep 21 08:40:05 2023 11 %% Update Count : 31 12 12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 13 … … 125 125 } 126 126 127 % C++ programming language 128 \lstdefinelanguage{C++}[ANSI]{C++}{ 129 morekeywords={nullptr,} 130 } 131 127 132 % uC++ programming language, based on ANSI C++ 128 \lstdefinelanguage{uC++}[ GNU]{C++}{133 \lstdefinelanguage{uC++}[ANSI]{C++}{ 129 134 morekeywords={ 130 135 _Accept, _AcceptReturn, _AcceptWait, _Actor, _At, _Catch, _CatchResume, _CorActor, _Cormonitor, _Coroutine, -
doc/bibliography/pl.bib
ra4da45e r0522ebe 3742 3742 month = jul, year = 1987, 3743 3743 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}},3754 3744 } 3755 3745 … … 8253 8243 } 8254 8244 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 8268 8245 @inproceedings{Burns81, 8269 8246 keywords = {N-thread software-solution mutual exclusion}, -
doc/proposals/enum.tex
ra4da45e r0522ebe 117 117 \end{abstract} 118 118 119 \section{ Introduction}119 \section{Background} 120 120 121 121 Naming 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).122 Naming is also commonly used to represent many other numerical phenomenon, such as days of the week, months of a year, floors of a building (basement), time (noon, New Years). 123 123 Many programming languages capture this important software-engineering capability through a mechanism called an \newterm{enumeration}. 124 124 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. … … 126 126 127 127 Specifically, 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. 128 Fundamentally, all types are restricted to a fixed set of values because of the underlying von Neumann architecture, and hence, to a corresponding set of constants, e.g., @3@, @3.5@, @3.5+2.1i@, @'c'@, @"abc"@, etc. 129 However, the values for basic types are not named, other than the programming-language supplied constants. 130 131 132 \section{C-Style Enum} 133 134 The C-Style enumeration has the following syntax and semantics, and is representative of enumerations in many other programming languages (see Section~\ref{s:RelatedWork}). 135 \begin{lstlisting}[label=lst:weekday] 136 enum Weekday { Monday, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday }; 137 $\(\uparrow\)$ $\(\uparrow\)$ 138 ${\rm \newterm{enumeration name}}$ ${\rm \newterm{enumerator names}} 139 \end{lstlisting} 140 Here, the enumeration type @Weekday@ defines the ordered \newterm{enumerator}s @Monday@, @Tuesday@, @Wednesday@, @Thursday@, @Friday@, @Saturday@ and @Sunday@. 141 By convention, the successor of @Tuesday@ is @Monday@ and the predecessor of @Tuesday@ is @Wednesday@, independent of the associated enumerator constants. 142 Because an enumerator is a constant, it cannot appear in a mutable context, e.g. @Mon = Sun@ is meaningless, and has no address, it is an rvalue\footnote{ 143 The term rvalue defines an expression that can only appear on the right-hand side of an assignment.}. 144 Enumerators without explicitly designated constants are auto-initialized by the compiler: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@. 145 For example, @Monday@ to @Wednesday@ are implicitly assigned with constants 0--2, @Thursday@ is explicitly set to constant @10@, and @Friday@ to @Sunday@ are implicitly assigned with constants 11--13. 146 Hence, there are 3 universal enumeration attributes: \newterm{position}, \newterm{label}, and \newterm{value}: 132 147 \begin{cquote} 133 148 \small\sf\setlength{\tabcolsep}{3pt} 134 149 \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 141 154 \end{tabular} 142 155 \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. 156 Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible in the enclosing scope of the @enum@ type. 171 157 172 158 In 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, whichhave type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables.174 F inally, there is an implicit bidirectional conversion between an enumeration and integral types.159 In practice, since integral constants in C have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables. 160 Furthermore, there is an implicit bidirectional conversion between an enumeration and integral types. 175 161 \begin{lstlisting}[label=lst:enum_scope] 176 162 { 177 enum Weekday { /* as above */ };$\C{// enumerators implicitly projected into local scope}$163 enum Weekday { ... }; $\C{// enumerators implicitly projected into local scope}$ 178 164 Weekday weekday = Monday; $\C{// weekday == 0}$ 179 165 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}$ 181 167 weekday = 10000; $\C{// UNDEFINED! implicit conversion to Weekday}$ 182 168 } … … 185 171 The implicit conversion from @int@ to an enumeration type is an unnecessary source of error. 186 172 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 0190 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 200 173 \section{\CFA-Style Enum} 201 174 … … 203 176 \CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages. 204 177 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 254 178 \subsection{Enumerator Typing} 255 179 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. 181 Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's constants used to set the enumerators. 182 183 Typed enumerates deals with \emph{harmonizing} problem between an enumeration and its companion data. 184 The following example is from the \CFA compiler, written in \CC. 185 \begin{lstlisting} 186 enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES }; 187 char * integral_names[NO_OF_ITYPES] = { 188 "char", "signed char", "unsigned char", 189 "signed short int", "unsigned short int", 190 "signed int", "unsigned int", 191 ... 192 }; 193 \end{lstlisting} 194 The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit. 195 It is up to the programmer to ensure changes made in one location are harmonized with the other location (by identifying this requirement within a comment). 196 The typed enumeration largely solves this problem by combining and managing the two data types. 197 \begin{lstlisting} 198 enum( char * ) integral_types { 199 chr = "char", schar = "signed char", uschar = "unsigned char", 200 sshort = "signed short int", ushort = "unsigned short int", 201 sint = "signed int", usint = "unsigned int", 202 ... 203 }; 266 204 \end{lstlisting} 267 205 … … 285 223 enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i }; 286 224 // pointer 287 enum( @char *@ ) Names { Fred = "F RED", Mary = "MARY", Jane = "JANE" };225 enum( @char *@ ) Names { Fred = "Fred", Mary = "Mary", Jane = "Jane" }; 288 226 int i, j, k; 289 227 enum( @int *@ ) ptr { I = &i, J = &j, K = &k }; 290 228 enum( @int &@ ) ref { I = i, J = j, K = k }; 291 229 // tuple 292 enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$230 enum( @[int, int]@ ) { T = [ 1, 2 ] }; 293 231 // function 294 232 void f() {...} void g() {...} … … 296 234 // aggregate 297 235 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 } }; 299 237 \end{lstlisting} 300 238 \caption{Enumerator Typing} … … 302 240 \end{figure} 303 241 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 329 242 \subsection{Pure Enumerators} 330 243 331 An empty enumerator type, @enum()@, implies the enumerators are pure symbols without values but set properties;244 An empty type, @enum()@, implies the enumerators are pure symbols without values; 332 245 hence, there is no default conversion to @int@. 333 246 334 247 \begin{lstlisting} 335 248 enum() 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; 249 Mode iomode = O_RDONLY; 338 250 int i = iomode; $\C{\color{red}// disallowed}$ 251 sout | O_TRUNC; $\C{\color{red}// disallowed}$ 339 252 \end{lstlisting} 340 253 341 254 \subsection{Enumerator Subset} 342 255 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 256 If follows from enumerator typing that the type of the enumerators can be another enumerator. 257 \begin{lstlisting} 347 258 enum( 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} 259 enum( Letter ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // alphabet intersection 353 260 Letter letter = A; 354 @***@Greak greek = Beta;355 letter = Beta; $\C{// allowed, letter == B}$261 Greak greek = Alph; 262 letter = Alph; $\C{// allowed}$ 356 263 greek = A; $\C{\color{red}// disallowed}$ 357 264 \end{lstlisting} 358 265 Enumeration @Greek@ may have more or less enumerators than @Letter@, but the enumerator values must be from @Letter@. 266 Therefore, @Greek@ enumerators are a subset of type @Letter@ and are type compatible with enumeration @Letter@, but @Letter@ enumerators are not type compatible with enumeration @Greek@. 359 267 360 268 \subsection{Enumeration Inheritance} 361 269 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} 272 enum( char * ) Name2 { @inline Name@, Jack = "Jack", Jill = "Jill" }; 273 enum /* inferred */ Name3 { @inline Name2@, Sue = "Sue", Tom = "Tom" }; 274 \end{lstlisting} 275 Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Name@ by containment, and a @Name@ enumeration is a subtype of enumeration @Name2@. 369 276 Note, enumerators must be unique in inheritance but enumerator values may be repeated. 370 277 371 278 The enumeration type for the inheriting type must be the same as the inherited type; 372 279 hence 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}$ 280 When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important. 281 282 Specifically, the inheritance relationship for Names is: 283 \begin{lstlisting} 284 Name $\(\subset\)$ Name2 $\(\subset\)$ Name3 $\(\subset\)$ const char * // enum type of Name 377 285 \end{lstlisting} 378 286 For the given function prototypes, the following calls are valid. … … 380 288 \begin{tabular}{ll} 381 289 \begin{lstlisting} 382 void f( Name s);383 void g( Name s2 );384 void h( Name s3 );290 void f( Name ); 291 void g( Name2 ); 292 void h( Name3 ); 385 293 void j( const char * ); 386 294 \end{lstlisting} … … 390 298 g( Fred ); g( Jill ); 391 299 h( Fred ); h( Jill ); h( Sue ); 392 j( Fred ); j( Jill ); j( Sue ); j( "W ILL" );300 j( Fred ); j( Jill ); j( Sue ); j( "Will" ); 393 301 \end{lstlisting} 394 302 \end{tabular} 395 303 \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 304 Note, the validity of calls is the same for call-by-reference as for call-by-value, and const restrictions are the same as for other types. 305 306 \subsection{Enumerator Scoping} 307 308 A \CFA-enum can be scoped, meaning the enumerator constants are not projected into the enclosing scope. 309 \begin{lstlisting} 310 enum Weekday @!@ { /* as above */ }; 311 enum Colour( char * ) @!@ { /* as above */ }; 312 \end{lstlisting} 313 where the @'!'@ implies the enumerators are \emph{not} projected. 314 The enumerators of a scoped enumeration are accessed using qualifications, like the fields of an aggregate. 315 % The syntax of $qualified\_expression$ for \CFA-enum is the following: 316 % $$<qualified\_expression> := <enum\_type>.<enumerator>$$ 317 \begin{lstlisting} 318 Weekday weekday = @Weekday.Monday@; $\C{// qualification}$ 319 Colour colour = @Colour.@Red; 320 colour = @Colour.@Blue; 321 \end{lstlisting} 398 322 399 323 \subsection{Enumeration Pseudo-functions} 400 324 401 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@ , @offsetof@, @typeof@.325 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@. 402 326 Often 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.. 403 327 328 \subsubsection{Enumerator Attributes} 404 329 The attributes of an enumerator are accessed by pseudo-functions @position@, @value@, and @label@. 405 330 \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; 331 int green_pos = @position@( Colour.Green ); $\C{// 1}$ 332 char * green_value = @value@( Colour.Green ); $\C{// "G"}$ 333 char * green_label = @label@( Colour.Green ); $\C{// "Green"}$ 334 \end{lstlisting} 335 336 Enumeration Greek may have more or less enumerators than Letter, but the enumerator values must be from Letter. 337 Therefore, Greek enumerators are a subset of type Letter and are type compatible with enumeration Letter, but Letter enumerators are not type compatible with enumeration Greek. 338 339 % An instance of \CFA-enum (denoted as @<enum_instance>@) is a label for the defined enum name. 340 % The label can be retrieved by calling the function @label( <enum_instance> )@. 341 % Similarly, the @value()@ function returns the value used to initialize the \CFA-enum. 342 343 \subsubsection{\lstinline{enumerate()}} 344 345 \begin{lstlisting}[label=lst:c_switch] 346 enum(int) C_ENUM { First, Second, Third = First, Fourth }; 347 int v( C_ENUM e ) { 348 switch( e ) { 349 case First: return 0; break; 350 case Second: return 1; break; 351 // case Third: return 2; break; 352 // case Fourth: return 3; break; 353 }; 354 }; 355 \end{lstlisting} 356 In the @C_ENUM@ example, @Third@ is an alias of @First@ and @Fourth@ is an alias of @Second@. 357 Programmers cannot make case branches for @Third@ and @Fourth@ because the switch statement matches cases by the enumerator's value. 358 Case @First@ and @Third@, or @Second@ and @Fourth@, has duplicate case values. 359 360 @enumerate()@ is a pseudo-function that makes the switch statement match by an enumerator instead. 361 \begin{lstlisting}[label=lst:c_switch_enumerate] 362 enum(double) C_ENUM { First, Second, Third = First, Fourth }; 363 C_ENUM variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth; 364 int v(C_ENUM e) { 365 switch( enumeratate( e ) ) { 366 case First: return e; break; 367 case Second: return value( e ); break; 368 case Third: return label( e ); break; 369 case Fourth: return position( e ); break; 370 }; 371 }; 468 372 p(variable_a); // 0 469 373 p(variable_b); // 1 … … 1111 1015 If the @aggregation_name@ is identified as a \CFA enumeration, the compiler checks if @field@ presents in the declared \CFA enumeration. 1112 1016 1017 \subsection{\lstinline{with} Clause/Statement} 1018 1019 Instead of qualifying an enumeration expression every time, the @with@ can be used to expose enumerators to the current scope, making them directly accessible. 1020 \begin{lstlisting}[label=lst:declaration] 1021 enum Color( char * ) { Red="R", Green="G", Blue="B" }; 1022 enum Animal( int ) { Cat=10, Dog=20 }; 1023 with ( Color, Animal ) { 1024 char * red_string = Red; // value( Color.Red ) 1025 int cat = Cat; // value( Animal.Cat ) 1026 } 1027 \end{lstlisting} 1028 The \lstinline{with} might introduce ambiguity to a scope. Consider the example: 1029 \begin{lstlisting}[label=lst:declaration] 1030 enum Color( char * ) { Red="R", Green="G", Blue="B" }; 1031 enum RGB( int ) { Red=0, Green=1, Blue=2 }; 1032 with ( Color, RGB ) { 1033 // int red = Red; 1034 } 1035 \end{lstlisting} 1036 \CFA will not try to resolve the expression with ambiguity. It would report an error. In this case, it is necessary to qualify @Red@ even inside of the \lstinline{with} clause. 1037 1113 1038 \subsection{Instance Declaration} 1114 1039 … … 1293 1218 1294 1219 \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; 1222 hence, the values in a \CC enumeration can only be its enumerators (without a cast). 1223 There is no mechanism to iterate through an enumeration. 1224 1225 \CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), so the enumerators are local to the enumeration and must be accessed using type qualification, e.g., @Weekday::Monday@. 1226 \CC{20} supports unscoped access with a \lstinline[language=c++]{using enum} declaration. 1227 1228 For both unscoped and scoped enumerations, the underlying type is an implementation-defined integral type large enough to hold all enumerated values; it does not have to be the smallest possible type. 1229 In \CC{11}, the underlying integral type can be explicitly specified: 1336 1230 \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 1231 enum class RGB : @long@ { Red, Green, Blue }; 1232 enum class rgb : @char@ { Red = 'r', Green = 'g', Blue = 'b' }; 1233 enum class srgb : @signed char@ { Red = -1, Green = 0, Blue = 1 }; 1234 RGB colour1 = @RGB::@Red; 1235 rgb colour2 = @rgb::@Red; 1236 srgb colour3 = @srgb::@Red; 1237 \end{lstlisting} 1361 1238 1362 1239 \subsection{Go} -
doc/theses/jiada_liang_MMath/background.tex
ra4da45e r0522ebe 1 1 \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 comma21 };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 045 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 1 1 \chapter{Introduction} 2 2 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. 3 Testing glossy abbreviations \gls{foo} and \gls{bar}, and glossy definitions \gls{git} and \gls{gulp}. 8 4 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} 5 And 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 131 131 \begin{center}\textbf{Abstract}\end{center} 132 132 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. 133 Enumerated type ... 137 134 138 135 \cleardoublepage -
doc/theses/jiada_liang_MMath/uw-ethesis.tex
ra4da45e r0522ebe 91 91 % cfa macros used in the document 92 92 \input{common} 93 %\usepackage {common}93 %\usepackageinput{common} 94 94 \CFAStyle % CFA code-style 95 \lstset{language= cfa,belowskip=-1pt} % set default language to CFA96 95 \lstset{language=CFA} % default language 96 \lstset{basicstyle=\linespread{0.9}\sf} % CFA typewriter font 97 97 \newcommand{\newtermFont}{\emph} 98 98 \newcommand{\Newterm}[1]{\newtermFont{#1}} … … 211 211 \input{intro} 212 212 \input{background} 213 \input{CFAenum} 214 \input{implementation} 215 \input{relatedwork} 213 \input{content1} 214 \input{content2} 216 215 \input{performance} 217 216 \input{conclusion} -
doc/user/user.tex
ra4da45e r0522ebe 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Feb 12 11:50:26202414 %% Update Count : 6 19913 %% Last Modified On : Tue Jan 30 09:02:41 2024 14 %% Update Count : 6046 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 4177 4177 The \CFA header file for the I/O library is \Indexc{fstream.hfa}. 4178 4178 4179 4180 \subsubsection{Stream Output}4181 4182 4179 For implicit formatted output, the common case is printing a series of variables separated by whitespace. 4183 4180 \begin{cquote} … … 4258 4255 Note, \CFA stream variables ©stdin©, ©stdout©, ©stderr©, ©exit©, and ©abort© overload C variables ©stdin©, ©stdout©, ©stderr©, and functions ©exit© and ©abort©, respectively. 4259 4256 4260 4261 \subsubsection{Stream Input}4262 4263 4257 For 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. 4264 4258 \begin{cquote} 4265 4259 \begin{lrbox}{\myboxA} 4266 4260 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4267 char c; int i; double d 4261 int x; double y char z; 4268 4262 \end{cfa} 4269 4263 \end{lrbox} … … 4272 4266 \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{Python}} \\ 4273 4267 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4274 sin | c | i | d;4268 sin | x | y | z; 4275 4269 \end{cfa} 4276 4270 & 4277 4271 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4278 cin >> c >> i >> d;4272 cin >> x >> y >> z; 4279 4273 \end{cfa} 4280 4274 & 4281 4275 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4282 c = input(); i = int(input()); d = float(input());4276 x = int(input()); y = float(input()); z = input(); 4283 4277 \end{cfa} 4284 4278 \\ 4285 4279 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4286 ® A® ®1® ®2.5®4280 ®1® ®2.5® ®A® 4287 4281 4288 4282 … … 4290 4284 & 4291 4285 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4292 ® A® ®1® ®2.5®4286 ®1® ®2.5® ®A® 4293 4287 4294 4288 … … 4296 4290 & 4297 4291 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4298 ®A®4299 4292 ®1® 4300 4293 ®2.5® 4294 ®A® 4301 4295 \end{cfa} 4302 4296 \end{tabular} … … 4315 4309 For 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. 4316 4310 Floating-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©. 4311 In all cases, all whitespace characters are skipped until an appropriate value is found. 4312 \Textbf{If an appropriate value is not found, the exception ©missing_data© is raised.} 4313 4314 For the C-string type, there are two input forms: any number of \Textbf{non-whitespace} characters or a quoted sequence containing any characters except the closing quote, \ie there is no escape character supported in the string.. 4315 In both cases, the string is null terminated ©'\0'©. 4316 For the quoted string, the start and end quote characters can be any character and do not have to match \see{\ref{XXX}}. 4317 4318 \VRef[Figure]{f:IOStreamFunctions} shows the I/O stream operations for interacting with files other than ©cin©, ©cout©, and ©cerr©. 4356 4319 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt] 4357 4320 \item … … 4969 4932 4970 4933 \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 4935 The following \Index{manipulator}s control scanning of input values (reading), and only affect the format of the argument. 4936 4937 Certain manipulators support a \newterm{scanset}, which is a simple regular expression, where the matching set contains any Latin-1 character (8-bits) or character ranges using minus. 4981 4938 For 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) ©'§'©. 4982 4939 The following string is matched by this scanset: 4983 4940 \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} 4943 To match a minus, put it as the first character, ©"-0-9"©. 4944 Other complex forms of regular-expression matching are not supported. 4945 4946 A string variable \emph{must} be large enough to contain the input sequence. 4947 To force programmers to consider buffer overruns for C-string input, C-strings can only be read with a width field, which should specify a size less than or equal to the C-string size, \eg: 4948 \begin{cfa} 4949 char line[64]; 4950 sin | wdi( ®sizeof(line)®, line ); // must specify size 4951 \end{cfa} 4952 4953 Currently, there is no mechanism to detect if a value read exceeds the capwhen Most types are finite sized, \eg integral types only store value that fit into their corresponding storage, 8, 16, 32, 64, 128 bits. 4954 Hence, an input value may be too large, and the result of the read is often considered undefined, which leads to difficlt to locate runtime errors. 4955 All reads in \CFA check if values do not fit into the argument variable's type and raise the exception 4956 All types are 4990 4957 4991 4958 \begin{enumerate} 4992 4959 \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$ ) 4961 The first form uses a scanset to skip matching characters. 4962 The second form skips the next $N$ characters, including newline. 4963 If the match successes, the input characters are discarded, and input continues with the next character. 4996 4964 If the match fails, the input characters are left unread. 4997 4965 \begin{cfa}[belowskip=0pt] 4998 char s canset[§\,§] = "abc";4999 sin | "abc §\textvisiblespace§" | skip( scanset ) | skip( 5 ); §\C{// match and skip input sequence}§4966 char sk[§\,§] = "abc"; 4967 sin | "abc " | skip( sk ) | skip( 5 ); // match input sequence 5000 4968 \end{cfa} 5001 4969 \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©} 4977 For all types except ©char *©, whitespace is skipped until an appropriate value is found for the specified variable type. 4978 maximum is the maximum number of characters read for the current operation. 5010 4979 \begin{cfa}[belowskip=0pt] 5011 4980 char ch; char ca[3]; int i; double d; … … 5016 4985 \end{cfa} 5017 4986 Here, ©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.5019 4987 5020 4988 Note, input ©wdi© cannot be overloaded with output ©wd© because both have the same parameters but return different types. … … 5022 4990 5023 4991 \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©} 4993 For type ©char *©, maximum is the maximum number of characters read for the current operation. 4994 Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence 5028 4995 \begin{cfa}[belowskip=0pt] 5029 char cs [10];5030 sin | wdi( sizeof(cs ), cs);4996 char cstr[10]; 4997 sin | wdi( sizeof(cstr), cstr ); 5031 4998 \end{cfa} 5032 4999 \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©} 5005 For type ©char *©, maximum is the maximum number of characters read for the current operation. 5040 5006 \begin{cfa}[belowskip=0pt] 5041 char c s[10];5042 sin | wdi( sizeof(c s), 9, cs );5007 char ch; char ca[3]; int i; double d; 5008 sin | wdi( sizeof(ch), ch ) | wdi( sizeof(ca), ca[0] ) | wdi( 3, i ) | wdi( 8, d ); // c == 'a', ca == "bcd", i == 123, d == 345.6 5043 5009 \end{cfa} 5044 5010 \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©} 5016 For all types, the data is read from the stream depending on the argument type but ignored, \ie it is not stored in the argument. 5053 5017 \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}§ 5018 double d; 5019 sin | ignore( d ); // d is unchanged 5057 5020 \end{cfa} 5058 5021 \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©} 5027 For C-string types only, the scanset matches any number of characters \emph{in} the set. 5028 Matching characters are read into the C input-string and null terminated. 5068 5029 \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 ); 5030 char s[10]; 5031 sin | incl( "abc", s ); 5098 5032 \end{cfa} 5099 5033 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 5102 5036 5103 5037 \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©} 5039 For C-string types, the scanset matches any number of characters \emph{not in} the set. 5040 Non-matching characters are read into the C input-string and null terminated. 5106 5041 \begin{cfa}[belowskip=0pt] 5107 char cs[10];5108 sin | excl( "abc", cs );5042 char s[10]; 5043 sin | excl( "abc", s ); 5109 5044 \end{cfa} 5110 5045 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 5112 5047 \end{cfa} 5113 5048 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©} 5050 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character. 5051 If the delimit character is omitted, it defaults to ©'\n'© (newline). 5052 5053 \item 5054 \Indexc{getline}( char delimit, wdi-input-string )\index{manipulator!getline@©getline©} 5055 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character. 5056 If the delimit character is omitted, it defaults to ©'\n'© (newline). 5127 5057 \end{enumerate} 5128 5058 -
libcfa/src/Exception.hfa
ra4da45e r0522ebe 2 2 3 3 // 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 34 34 act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler; 35 35 sigemptyset( &act.sa_mask ); 36 sigaddset( &act.sa_mask, SIGALRM ); // disabled during signal handler36 sigaddset( &act.sa_mask, SIGALRM ); // disabled during signal handler 37 37 sigaddset( &act.sa_mask, SIGUSR1 ); 38 38 sigaddset( &act.sa_mask, SIGSEGV ); … … 40 40 sigaddset( &act.sa_mask, SIGILL ); 41 41 sigaddset( &act.sa_mask, SIGFPE ); 42 sigaddset( &act.sa_mask, SIGHUP ); // revert to default on second delivery42 sigaddset( &act.sa_mask, SIGHUP ); // revert to default on second delivery 43 43 sigaddset( &act.sa_mask, SIGTERM ); 44 44 sigaddset( &act.sa_mask, SIGINT ); -
libcfa/src/collections/string.cfa
ra4da45e r0522ebe 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 7 21:17:06202413 // Update Count : 2 5912 // Last Modified On : Sun Jan 14 12:03:47 2024 13 // Update Count : 240 14 14 // 15 15 … … 210 210 } 211 211 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 } // ?|? 212 void ?|?( ifstream & in, string & s ) { 213 in | (*s.inner); 214 } 216 215 217 216 ifstream & ?|?( ifstream & is, _Istream_Sstr f ) { 218 // _Istream_Rstr f2 = {f.sstr.s.inner, (_Istream_str_base)f.sstr};219 217 _Istream_Rstr f2 = {f.s.inner, (_Istream_str_base)f}; 220 218 return is | f2; 221 219 } // ?|? 220 221 void ?|?( ifstream & in, _Istream_Sstr f ) { 222 (ifstream &)(in | f); 223 } 222 224 223 225 //////////////////////////////////////////////////////// -
libcfa/src/collections/string.hfa
ra4da45e r0522ebe 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 6 20:59:18202413 // Update Count : 11812 // Last Modified On : Sun Jan 14 12:03:46 2024 13 // Update Count : 81 14 14 // 15 15 … … 80 80 void ?|?(ofstream & out, const string & s); 81 81 ifstream & ?|?(ifstream & in, string & s); 82 void ?|?( ifstream & in, string & s ); 82 83 83 84 static inline { … … 95 96 void ?|?( ofstream & os, _Ostream_Manip(string) ); 96 97 97 struct _Istream_Swidth {98 string & s;99 inline _Istream_str_base;100 }; // _Istream_Swidth101 102 struct _Istream_Squoted {103 _Istream_Swidth sstr;104 }; // _Istream_Squoted105 106 98 struct _Istream_Sstr { 107 99 string & s; 108 100 inline _Istream_str_base; 109 // _Istream_Swidth sstr;110 101 }; // _Istream_Sstr 111 102 112 103 static inline { 113 104 // read width does not include null terminator 114 _Istream_S width 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}} }; } 115 106 _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}} }; 118 108 } 119 _Istream_Sstr & getline( _Istream_S width & 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; 121 111 } 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; } 141 118 } // distribution 142 ifstream & ?|?( ifstream & is, _Istream_Squoted f );143 119 ifstream & ?|?( ifstream & is, _Istream_Sstr f ); 144 static inline ifstream & ?|?( ifstream & is, _Istream_Swidth f ) { return is | *(_Istream_Sstr *)&f; } 120 void ?|?( ifstream & is, _Istream_Sstr t ); 145 121 146 122 // Concatenation -
libcfa/src/collections/string_res.cfa
ra4da45e r0522ebe 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 10 17:47:22 202413 // Update Count : 8312 // Last Modified On : Mon Jan 22 23:12:42 2024 13 // Update Count : 43 14 14 // 15 15 … … 25 25 26 26 #include <assert.h> 27 #include <complex.h> // creal, cimag27 #include <complex.h> // creal, cimag 28 28 29 29 //######################### VbyteHeap "header" ######################### … … 34 34 35 35 struct VbyteHeap { 36 int NoOfCompactions;// number of compactions of the byte area37 int NoOfExtensions;// number of extensions in the size of the byte area38 int NoOfReductions;// number of reductions in the size of the byte area39 40 int InitSize;// initial number of bytes in the byte-string area41 int CurrSize;// current number of bytes in the byte-string area42 char *StartVbyte;// pointer to the `st byte of the start of the byte-string area43 char *EndVbyte;// pointer to the next byte after the end of the currently used portion of byte-string area44 void *ExtVbyte;// pointer to the next byte after the end of the byte-string area45 46 HandleNode Header;// header node for handle list36 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 47 47 }; // VbyteHeap 48 48 49 50 static void compaction( VbyteHeap & ); // compaction of the byte area51 static void garbage( VbyteHeap &, int ); // garbage collect the byte area52 static void extend( VbyteHeap &, int ); // extend the size of the byte area53 static void reduce( VbyteHeap &, int ); // reduce the size of the byte area49 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 54 54 55 55 static void ?{}( VbyteHeap &, size_t = 1000 ); 56 56 static void ^?{}( VbyteHeap & ); 57 57 58 static int ByteCmp( char *, int, int, char *, int, int ); // compare 2 blocks of bytes58 static int ByteCmp( char *, int, int, char *, int, int ); // compare 2 blocks of bytes 59 59 static char *VbyteAlloc( VbyteHeap &, int ); // allocate a block bytes in the heap 60 60 static char *VbyteTryAdjustLast( VbyteHeap &, int ); … … 62 62 static void AddThisAfter( HandleNode &, HandleNode & ); 63 63 static void DeleteNode( HandleNode & ); 64 static void MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle64 static void MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle 65 65 66 66 … … 69 69 static void ?{}( VbyteHeap & s, size_t Size ) with(s) { 70 70 #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; 82 82 #endif // VbyteDebug 83 83 } // VbyteHeap … … 87 87 88 88 static void ^?{}( VbyteHeap & s ) with(s) { 89 free( StartVbyte );89 free( StartVbyte ); 90 90 } // ~VbyteHeap 91 91 … … 99 99 static void ?{}( HandleNode & s ) with(s) { 100 100 #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; 107 107 #endif // VbyteDebug 108 108 } // HandleNode … … 114 114 static void ?{}( HandleNode & s, VbyteHeap & vh ) with(s) { 115 115 #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; 124 124 #endif // VbyteDebug 125 125 } // HandleNode … … 131 131 static void ^?{}( HandleNode & s ) with(s) { 132 132 #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 } // for140 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 ); 145 145 } // ~HandleNode 146 146 … … 148 148 //######################### String Sharing Context ######################### 149 149 150 static string_sharectx * ambient_string_sharectx; // fickle top of stack150 static string_sharectx * ambient_string_sharectx; // fickle top of stack 151 151 static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack 152 152 153 153 void ?{}( 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; 162 162 } 163 163 164 164 void ^?{}( string_sharectx & s ) with( s ) { 165 if ( activeHeap ) delete( activeHeap );166 167 // unlink s from older-list starting from ambient_string_sharectx168 // usually, s==ambient_string_sharectx and the loop runs zero times169 string_sharectx *& c = ambient_string_sharectx;170 while ( c != &s ) &c = &c->older;// find s171 c = s.older;// unlink165 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 172 172 } 173 173 … … 176 176 177 177 VbyteHeap * 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; 180 180 } 181 181 182 182 size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) { 183 return ((char *)heap->ExtVbyte) - heap->EndVbyte;183 return ((char *)heap->ExtVbyte) - heap->EndVbyte; 184 184 } 185 185 186 186 size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) { 187 return heap->CurrSize;187 return heap->CurrSize; 188 188 } 189 189 190 190 const char * DEBUG_string_heap_start( VbyteHeap * heap ) { 191 return heap->StartVbyte;191 return heap->StartVbyte; 192 192 } 193 193 194 194 // Returns the size of the string in bytes 195 195 size_t size(const string_res & s) with(s) { 196 return Handle.lnth;196 return Handle.lnth; 197 197 } 198 198 … … 201 201 // CFA string is NOT null terminated, so print exactly lnth characters in a minimum width of 0. 202 202 out | wd( 0, s.Handle.lnth, s.Handle.s ) | nonl; 203 return out;203 return out; 204 204 } 205 205 … … 210 210 // Input operator 211 211 ifstream & ?|?(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 terminator224 temp += "--";225 assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );// last in heap226 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 into232 int lenReadable = (char *)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte;233 assert (lenReadable >= 2);234 235 // get bytes236 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 { 237 237 *(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 metadata245 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 } 248 248 249 249 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 253 void ?|?( ifstream & in, string_res & s ) { 254 (ifstream &)(in | s); 276 255 } 277 256 … … 316 295 } // ?|? 317 296 297 void ?|?( ifstream & in, _Istream_Rstr f ) { 298 (ifstream &)(in | f); 299 } 300 301 318 302 // Empty constructor 319 303 void ?{}(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 string326 (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 } 333 317 334 318 static 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; 347 331 } 348 332 349 333 // Constructor from a raw buffer and size 350 334 void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) { 351 eagerCopyCtorHelper(s, rhs, rhslnth);335 eagerCopyCtorHelper(s, rhs, rhslnth); 352 336 } 353 337 354 338 void ?{}( 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 }; 359 343 } 360 344 void ?{}( 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 }; 365 349 } 366 350 void ?{}( 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 }; 371 355 } 372 356 void ?{}( 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 }; 377 361 } 378 362 void ?{}( 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 }; 383 367 } 384 368 void ?{}( 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 }; 389 373 } 390 374 391 375 // private ctor (not in header): use specified heap (ignore ambient) and copy chars in 392 376 void ?{}( 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; 400 384 } 401 385 … … 403 387 // General copy constructor 404 388 void ?{}(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 } 447 432 } 448 433 449 434 static 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 string468 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 edit475 // take start and end as end-anchored476 size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;477 p->Handle.s = limit - startOffsetFromEnd;478 // p->Handle.lnth unaffected479 } else if ( p->Handle.s <= beforeBegin + beforeLen ) {480 // p starts before, or at the start of, the edit481 if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {482 // p ends before the edit483 // take end as start-anchored too484 // p->Handle.lnth unaffected485 } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {486 // p ends during the edit; p does not include the last character replaced487 // clip end of p to end at start of edit488 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );489 } else {490 // p ends after the edit491 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );492 // take end as end-anchored493 // stretch-shrink p according to the edit494 p->Handle.lnth += s.Handle.lnth;495 p->Handle.lnth -= oldLnth;496 }497 // take start as start-anchored498 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 edit503 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 replaced506 // set p to empty string at start of edit507 p->Handle.s = s.Handle.s;508 p->Handle.lnth = 0;509 } else {510 // p includes the end of the edit511 // clip start of p to start at end of edit512 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 position519 }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 } 520 505 } 521 506 522 507 // traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs 523 508 static 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 } 534 519 } 535 520 536 521 static 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; 617 603 } 618 604 619 605 string_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); 621 607 } 622 608 623 609 string_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); 625 611 } 626 612 627 613 string_res & ?=?(string_res & s, char c) { 628 return assign(s, &c, 1);614 return assign(s, &c, 1); 629 615 } 630 616 631 617 string_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; 635 621 } 636 622 string_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; 640 626 } 641 627 string_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; 645 631 } 646 632 string_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; 650 636 } 651 637 string_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; 655 641 } 656 642 string_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; 660 646 } 661 647 662 648 // Copy assignment operator 663 649 string_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); 665 651 } 666 652 667 653 string_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; 670 656 } 671 657 … … 673 659 // Destructor 674 660 void ^?{}(string_res & s) with(s) { 675 // much delegated to implied ^VbyteSM676 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 out684 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 } 686 672 } 687 673 … … 691 677 // offset from the start of the string. 692 678 char ?[?](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]; 695 681 } 696 682 697 683 void 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 string701 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); 703 689 } 704 690 … … 708 694 709 695 void 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-op713 } else {// must copy some text714 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 area716 } else {// copy the two parts717 char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );718 char * str1oldBuf = str1.Handle.s;// must read after VbyteAlloc call in case it gs's719 str1.Handle.s = str1newBuf;720 memcpy( str1.Handle.s, str1oldBuf, str1.Handle.lnth );721 } // if722 memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );723 } // if724 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; 725 711 } 726 712 727 713 void ?+=?(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 ); 729 715 } 730 716 731 717 void 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) ); 733 719 } 734 720 735 721 void ?+=?(string_res & s, char c) { 736 append( s, & c, 1 );722 append( s, & c, 1 ); 737 723 } 738 724 void ?+=?(string_res & s, const char * c) { 739 append( s, c, strlen(c) );725 append( s, c, strlen(c) ); 740 726 } 741 727 … … 744 730 745 731 void ?*=?(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; 749 735 } 750 736 … … 753 739 754 740 int 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; 759 745 } 760 746 … … 767 753 768 754 int 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); 771 757 } 772 758 … … 779 765 780 766 int 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); 783 769 } 784 770 … … 791 777 792 778 779 793 780 ////////////////////////////////////////////////////////// 794 781 // Search 795 782 796 783 bool 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; 801 788 } 802 789 803 790 int find(const string_res & s, char search) { 804 return findFrom(s, 0, search);791 return findFrom(s, 0, search); 805 792 } 806 793 807 794 int 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; 816 803 } 817 804 818 805 int find(const string_res & s, const string_res & search) { 819 return findFrom(s, 0, search);806 return findFrom(s, 0, search); 820 807 } 821 808 822 809 int 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); 824 811 } 825 812 826 813 int find(const string_res & s, const char * search) { 827 return findFrom(s, 0, search);814 return findFrom(s, 0, search); 828 815 } 829 816 int 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)); 831 818 } 832 819 833 820 int 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); 835 822 } 836 823 837 824 int 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; 869 859 } 870 860 871 861 bool 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); 873 863 } 874 864 875 865 bool includes(const string_res & s, const char * search) { 876 return includes(s, search, strlen(search));866 return includes(s, search, strlen(search)); 877 867 } 878 868 879 869 bool 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; 881 871 } 882 872 883 873 bool 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); 885 875 } 886 876 887 877 bool startsWith(const string_res & s, const char * prefix) { 888 return startsWith(s, prefix, strlen(prefix));878 return startsWith(s, prefix, strlen(prefix)); 889 879 } 890 880 891 881 bool 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; 896 886 } 897 887 898 888 bool 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); 900 890 } 901 891 902 892 bool endsWith(const string_res & s, const char * suffix) { 903 return endsWith(s, suffix, strlen(suffix));893 return endsWith(s, suffix, strlen(suffix)); 904 894 } 905 895 906 896 bool 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 917 908 918 909 /////////////////////////////////////////////////////////////////////////// … … 920 911 921 912 void ?{}( charclass_res & s, const string_res & chars) { 922 (s){ chars.Handle.s, chars.Handle.lnth };913 (s){ chars.Handle.s, chars.Handle.lnth }; 923 914 } 924 915 925 916 void ?{}( charclass_res & s, const char * chars ) { 926 (s){ chars, strlen(chars) };917 (s){ chars, strlen(chars) }; 927 918 } 928 919 929 920 void ?{}( 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 ? 932 923 } 933 924 934 925 void ^?{}( charclass_res & s ) { 935 ^(s.chars){};926 ^(s.chars){}; 936 927 } 937 928 938 929 static 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 ); 941 932 } 942 933 943 934 int 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); 948 939 } 949 940 950 941 int 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); 955 946 } 956 947 … … 962 953 static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) { 963 954 #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 { 975 966 serr | "HandleList:"; 976 967 serr | nlOff; … … 983 974 } // for 984 975 serr | nlOn; 985 }986 serr | "exit:AddThisAfter";976 } 977 serr | "exit:AddThisAfter"; 987 978 #endif // VbyteDebug 988 979 } // AddThisAfter … … 993 984 static void DeleteNode( HandleNode & s ) with(s) { 994 985 #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"; 1001 992 #endif // VbyteDebug 1002 993 } // DeleteNode … … 1008 999 static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) { 1009 1000 #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 ? 1017 1008 garbage( s, size ); // firer up the garbage collector 1018 1009 verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte && "garbage run did not free up required space" ); 1019 } // if1020 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; 1026 1017 } // VbyteAlloc 1027 1018 … … 1036 1027 1037 1028 static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) { 1038 if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {1039 // room available1040 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; 1052 1043 } 1053 1044 … … 1058 1049 static void MoveThisAfter( HandleNode & s, const HandleNode & h ) with(s) { 1059 1050 #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 values1051 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 1065 1056 // 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"; 1067 1058 // exit(-1 ); 1068 1059 verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order"); 1069 } // if1070 1071 HandleNode *i;1072 for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h1073 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 ) { 1074 1065 DeleteNode( s ); 1075 1066 AddThisAfter( s, *i->blink ); 1076 } // if1077 #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 } // for1086 serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;1087 } // for1088 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"; 1091 1082 #endif // VbyteDebug 1092 1083 } // MoveThisAfter 1084 1085 1086 1093 1087 1094 1088 … … 1103 1097 int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) { 1104 1098 #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; 1122 1109 } // 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; 1134 1114 } // 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; 1138 1122 } // 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; 1144 1138 } // ByteCmp 1145 1139 … … 1151 1145 1152 1146 void 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 node1159 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 () { 1160 1154 memmove( EndVbyte, h->s, h->lnth ); 1161 1155 obase = h->s; … … 1175 1169 } // for 1176 1170 if ( h == &Header ) break; // end of header list ? 1177 } // for1171 } // for 1178 1172 } // compaction 1179 1173 1180 1174 1181 1175 static 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 it1176 // probably an unreasonable default, but need to assess early-round tests on changing it 1183 1177 1184 1178 void TUNING_set_string_heap_liveness_threshold( double val ) { 1185 heap_expansion_freespace_threshold = 1.0 - val;1179 heap_expansion_freespace_threshold = 1.0 - val; 1186 1180 } 1187 1181 … … 1192 1186 void garbage(VbyteHeap & s, int minreq ) with(s) { 1193 1187 #ifdef VbyteDebug 1194 serr | "enter:garbage";1195 {1188 serr | "enter:garbage"; 1189 { 1196 1190 serr | "HandleList:"; 1197 1191 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) { … … 1204 1198 serr | "\" flink:" | n->flink | " blink:" | n->blink; 1205 1199 } // 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 used1200 } 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 1212 1206 AmountUsed += i->lnth; 1213 } // for1214 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 request1207 } // 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 1217 1211 1218 1212 extend( s, max( CurrSize, minreq ) ); // extend the heap 1219 1213 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 memory1223 1224 // `extend` implies a `compaction` during the copy1225 1226 } else {1227 compaction(s); // in-place1228 }// if1229 #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 { 1231 1225 serr | "HandleList:"; 1232 1226 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) { … … 1239 1233 serr | "\" flink:" | n->flink | " blink:" | n->blink; 1240 1234 } // for 1241 }1242 serr | "exit:garbage";1235 } 1236 serr | "exit:garbage"; 1243 1237 #endif // VbyteDebug 1244 1238 } // garbage … … 1253 1247 void extend( VbyteHeap & s, int size ) with (s) { 1254 1248 #ifdef VbyteDebug 1255 serr | "enter:extend, size:" | size;1256 #endif // VbyteDebug 1257 char *OldStartVbyte;1258 1259 NoOfExtensions += 1;1260 OldStartVbyte = StartVbyte; // save previous byte area1261 1262 CurrSize += size > InitSize ? size : InitSize; // minimum extension, initial size1263 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);1264 ExtVbyte = (void *)( StartVbyte + CurrSize );1265 compaction(s); // copy from old heap to new & adjust pointers to new heap1266 free( OldStartVbyte ); // release old heap1267 #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; 1269 1263 #endif // VbyteDebug 1270 1264 } // extend … … 1278 1272 void VbyteHeap::reduce( int size ) { 1279 1273 #ifdef VbyteDebug 1280 serr | "enter:reduce, size:" | size;1281 #endif // VbyteDebug 1282 char *OldStartVbyte;1283 1284 NoOfReductions += 1;1285 OldStartVbyte = StartVbyte; // save previous byte area1286 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 heap1291 delete OldStartVbyte; // release old heap1292 #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; 1294 1288 #endif // VbyteDebug 1295 1289 } // reduce -
libcfa/src/collections/string_res.hfa
ra4da45e r0522ebe 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 7 21:24:40202413 // Update Count : 5912 // Last Modified On : Thu Jan 4 11:28:06 2024 13 // Update Count : 27 14 14 // 15 15 … … 123 123 void ?|?(ofstream & out, const string_res & s); 124 124 ifstream & ?|?(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 125 void ?|?( ifstream & in, string_res & s ); 136 126 137 127 struct _Istream_Rstr { 138 128 string_res * s; 139 129 inline _Istream_str_base; 140 // _Istream_Rwidth rstr;141 130 }; // _Istream_Rstr 142 131 143 132 static inline { 144 133 // read width does not include null terminator 145 _Istream_R width 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}} }; } 146 135 _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}} }; 149 137 } 150 _Istream_Rstr & getline( _Istream_R width & 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; 152 140 } 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; } 168 147 } // distribution 169 ifstream & ?|?( ifstream & is, _Istream_Rquoted f );170 148 ifstream & ?|?( ifstream & is, _Istream_Rstr f ); 171 static inline ifstream & ?|?( ifstream & is, _Istream_Rwidth f ) { return is | *(_Istream_Rstr *)&f; } 149 void ?|?( ifstream & is, _Istream_Rstr t ); 172 150 173 151 // Concatenation -
libcfa/src/fstream.cfa
ra4da45e r0522ebe 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 11 20:55:45202413 // Update Count : 5 8012 // Last Modified On : Sun Jan 28 09:56:08 2024 13 // Update Count : 554 14 14 // 15 15 … … 121 121 } // for 122 122 if ( file == 0p ) { 123 throw Resume(open_failure){ os };123 throw (open_failure){ os }; 124 124 // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno ); 125 125 } // if … … 241 241 } // for 242 242 if ( file == 0p ) { 243 throw Resume(open_failure){ is };243 throw (open_failure){ is }; 244 244 // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno ); 245 245 } // if … … 295 295 va_start( args, format ); 296 296 297 int nargs , tmperrno;297 int nargs; 298 298 for () { // no check for EINTR limit waiting for keyboard input 299 errno = 0; 299 300 disable_interrupts(); 300 errno = 0;301 301 nargs = vfscanf( (FILE *)(is.file$), format, args ); 302 tmperrno = errno;303 302 enable_interrupts(); 304 303 if ( nargs != EOF || errno != EINTR ) break; // timer interrupt ? … … 309 308 } // if 310 309 } // if 311 if ( tmperrno == ERANGE ) throwResume ExceptionInst( data_range );312 310 va_end( args ); 313 311 return nargs; … … 320 318 // *********************************** exceptions *********************************** 321 319 320 321 static vtable(open_failure) open_failure_vt; 322 322 323 323 // exception I/O constructors … … 335 335 336 336 337 static vtable(close_failure) close_failure_vt; 338 337 339 // exception I/O constructors 338 340 void ?{}( close_failure & ex, ofstream & ostream ) with( ex ) { … … 349 351 350 352 353 static vtable(write_failure) write_failure_vt; 354 351 355 // exception I/O constructors 352 356 void ?{}( write_failure & ex, ofstream & ostream ) with( ex ) { … … 363 367 364 368 369 static vtable(read_failure) read_failure_vt; 370 365 371 // exception I/O constructors 366 372 void ?{}( read_failure & ex, ofstream & ostream ) with( ex ) { -
libcfa/src/fstream.hfa
ra4da45e r0522ebe 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Feb 11 20:35:00 202413 // Update Count : 2 7412 // Last Modified On : Wed Oct 18 20:30:12 2023 13 // Update Count : 261 14 14 // 15 15 … … 139 139 140 140 141 ExceptionDecl( open_failure, 141 exception open_failure { 142 142 union { 143 143 ofstream * ostream; … … 146 146 // TEMPORARY: need polymorphic exceptions 147 147 int tag; // 1 => ostream; 0 => istream 148 );148 }; 149 149 150 150 void ?{}( open_failure & this, ofstream & ); 151 151 void ?{}( open_failure & this, ifstream & ); 152 152 153 ExceptionDecl( close_failure, 153 exception close_failure { 154 154 union { 155 155 ofstream * ostream; … … 158 158 // TEMPORARY: need polymorphic exceptions 159 159 int tag; // 1 => ostream; 0 => istream 160 );160 }; 161 161 162 162 void ?{}( close_failure & this, ofstream & ); 163 163 void ?{}( close_failure & this, ifstream & ); 164 164 165 ExceptionDecl( write_failure, 165 exception write_failure { 166 166 union { 167 167 ofstream * ostream; … … 170 170 // TEMPORARY: need polymorphic exceptions 171 171 int tag; // 1 => ostream; 0 => istream 172 );172 }; 173 173 174 174 void ?{}( write_failure & this, ofstream & ); 175 175 void ?{}( write_failure & this, ifstream & ); 176 176 177 ExceptionDecl( read_failure, 177 exception read_failure { 178 178 union { 179 179 ofstream * ostream; … … 182 182 // TEMPORARY: need polymorphic exceptions 183 183 int tag; // 1 => ostream; 0 => istream 184 );184 }; 185 185 186 186 void ?{}( read_failure & this, ofstream & ); -
libcfa/src/iostream.cfa
ra4da45e r0522ebe 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Feb 12 09:26:05202413 // Update Count : 19 6612 // Last Modified On : Sun Jan 28 11:58:54 2024 13 // Update Count : 1917 14 14 // 15 15 … … 774 774 forall( istype & | basic_istream( istype ) ) { 775 775 istype & ?|?( istype & is, bool & b ) { 776 if ( eof( is ) ) throwResume ExceptionInst( missing_data );777 776 char val[6]; 778 777 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 ); 780 779 if ( strcmp( val, "true" ) == 0 ) b = true; 781 780 else if ( strcmp( val, "false" ) == 0 ) b = false; … … 788 787 789 788 istype & ?|?( istype & is, char & c ) { 790 if ( eof( is ) ) throwResume ExceptionInst( missing_data );791 789 char temp; 792 790 for () { 793 791 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 ); 795 793 // do not overwrite parameter with newline unless appropriate 796 794 if ( temp != '\n' || getANL$( is ) ) { c = temp; break; } … … 801 799 802 800 istype & ?|?( istype & is, signed char & sc ) { 803 if ( eof( is ) ) throwResume ExceptionInst( missing_data );804 801 int args = fmt( is, "%hhi", &sc ); 805 if ( args != 1 ) throwResume ExceptionInst( missing_data );802 if ( args != -1 && args != 1 ) throwResume ExceptionInst( missing_data ); 806 803 return is; 807 804 } // ?|? 808 805 809 806 istype & ?|?( istype & is, unsigned char & usc ) { 810 if ( eof( is ) ) throwResume ExceptionInst( missing_data );811 807 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 ); 813 809 return is; 814 810 } // ?|? 815 811 816 812 istype & ?|?( istype & is, short int & si ) { 817 if ( eof( is ) ) throwResume ExceptionInst( missing_data );818 813 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 ); 820 815 return is; 821 816 } // ?|? 822 817 823 818 istype & ?|?( istype & is, unsigned short int & usi ) { 824 if ( eof( is ) ) throwResume ExceptionInst( missing_data );825 819 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 ); 827 821 return is; 828 822 } // ?|? 829 823 830 824 istype & ?|?( istype & is, int & i ) { 831 if ( eof( is ) ) throwResume ExceptionInst( missing_data );832 825 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 ); 834 827 return is; 835 828 } // ?|? 836 829 837 830 istype & ?|?( istype & is, unsigned int & ui ) { 838 if ( eof( is ) ) throwResume ExceptionInst( missing_data );839 831 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 ); 841 833 return is; 842 834 } // ?|? 843 835 844 836 istype & ?|?( istype & is, long int & li ) { 845 if ( eof( is ) ) throwResume ExceptionInst( missing_data );846 837 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 ); 848 839 return is; 849 840 } // ?|? 850 841 851 842 istype & ?|?( istype & is, unsigned long int & ulli ) { 852 if ( eof( is ) ) throwResume ExceptionInst( missing_data );853 843 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 ); 855 845 return is; 856 846 } // ?|? 857 847 858 848 istype & ?|?( istype & is, long long int & lli ) { 859 if ( eof( is ) ) throwResume ExceptionInst( missing_data );860 849 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 ); 862 851 return is; 863 852 } // ?|? 864 853 865 854 istype & ?|?( istype & is, unsigned long long int & ulli ) { 866 if ( eof( is ) ) throwResume ExceptionInst( missing_data );867 855 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 ); 869 857 return is; 870 858 } // ?|? … … 893 881 894 882 istype & ?|?( istype & is, float & f ) { 895 if ( eof( is ) ) throwResume ExceptionInst( missing_data );896 883 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 ); 898 885 return is; 899 886 } // ?|? 900 887 901 888 istype & ?|?( istype & is, double & d ) { 902 if ( eof( is ) ) throwResume ExceptionInst( missing_data );903 889 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 ); 905 891 return is; 906 892 } // ?|? 907 893 908 894 istype & ?|?( istype & is, long double & ld ) { 909 if ( eof( is ) ) throwResume ExceptionInst( missing_data );910 895 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 ); 912 897 return is; 913 898 } // ?|? 914 899 915 900 istype & ?|?( istype & is, float _Complex & fc ) { 916 if ( eof( is ) ) throwResume ExceptionInst( missing_data );917 901 float re, im; 918 902 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 ); 920 904 fc = re + im * _Complex_I; 921 905 return is; … … 923 907 924 908 istype & ?|?( istype & is, double _Complex & dc ) { 925 if ( eof( is ) ) throwResume ExceptionInst( missing_data );926 909 double re, im; 927 910 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 ); 929 912 dc = re + im * _Complex_I; 930 913 return is; … … 932 915 933 916 istype & ?|?( istype & is, long double _Complex & ldc ) { 934 if ( eof( is ) ) throwResume ExceptionInst( missing_data );935 917 long double re, im; 936 918 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 ); 938 920 ldc = re + im * _Complex_I; 939 921 return is; … … 941 923 942 924 istype & ?|?( istype & is, const char fmt[] ) { 943 if ( eof( is ) ) throwResume ExceptionInst( missing_data );944 925 size_t len = strlen( fmt ); 945 char fmt str[len + 16];946 strcpy( fmt str, fmt ); // copy format and add %n947 strcpy( &fmt str[len], "%n" );926 char fmt2[len + 16]; 927 strcpy( fmt2, fmt ); // copy format and add %n 928 strcpy( &fmt2[len], "%n" ); 948 929 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 ); 951 932 return is; 952 933 } // ?|? … … 982 963 forall( istype & | basic_istream( istype ) ) { 983 964 istype & ?|?( istype & is, _Istream_Cskip f ) { 984 if ( eof( is ) ) throwResume ExceptionInst( missing_data);965 // printf( "skip %s %d\n", f.scanset, f.wd ); 985 966 if ( f.scanset ) { 986 967 int nscanset = strlen(f.scanset); … … 990 971 strcpy( &fmtstr[pos], f.scanset ); pos += nscanset; 991 972 strcpy( &fmtstr[pos], "]" ); 992 fmt( is, fmtstr, "" ); // skip scanset , zero or more973 fmt( is, fmtstr, "" ); // skip scanset 993 974 } else { 994 975 char ch; 976 // fprintf( stderr, "skip " ); 995 977 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 ); 998 981 } // for 999 982 } // if … … 1002 985 1003 986 istype & ?|?( istype & is, _Istream_Cquoted f ) with( f.cstr ) { 1004 if ( eof( is ) ) throwResume ExceptionInst( missing_data );1005 987 int args; 1006 988 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; 1011 995 1012 996 // Change the remainder of the read into a getline by reseting the closing delimiter. … … 1027 1011 1028 1012 istype & ?|?( istype & is, _Istream_Cstr f ) with( f.cstr ) { 1029 if ( eof( is ) ) throwResume ExceptionInst( missing_data );1030 1013 const char * scanset; 1031 1014 size_t nscanset = 0; … … 1058 1041 if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*' 1059 1042 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 ); 1061 1044 if ( check && len >= rwd && ! eof( is ) ) { // might not fit 1062 1045 char peek; 1063 1046 fmt( is, "%c", &peek ); // check for whitespace terminator 1064 1047 // fprintf( stderr, "peek %d '%c'\n", args, peek ); 1065 if ( ! eof( is ) ) { // can only fail at eof1048 if ( ! eof( is ) ) { 1066 1049 ungetc( is, peek ); 1067 1050 if ( ! isspace( peek ) ) throwResume ExceptionInst( cstring_length ); … … 1069 1052 } // if 1070 1053 // 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 1073 1055 } else { 1074 1056 if ( flags.delimiter ) { // getline 1075 1057 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 ? 1077 1059 sprintf( &fmtstr[pos], "c%%n" ); 1078 1060 } else { … … 1081 1063 if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*' 1082 1064 else args = fmt( is, fmtstr, s, &len ); 1083 1084 1065 if ( check && len == rwd && ! eof( is ) ) { // might not fit 1085 1066 char peek; … … 1115 1096 } // if 1116 1097 if ( args == 1 && eof( is ) ) { // data but scan ended at EOF 1098 // fprintf( stderr, "clear\n" ); 1117 1099 clear( is ); // => reset EOF => detect again on next read 1118 1100 } // if -
libcfa/src/iostream.hfa
ra4da45e r0522ebe 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 6 18:35:54202413 // Update Count : 7 4312 // Last Modified On : Sun Jan 28 11:56:29 2024 13 // Update Count : 733 14 14 // 15 15 … … 370 370 // *********************************** exceptions *********************************** 371 371 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 372 ExceptionDecl( cstring_length ); 373 ExceptionDecl( missing_data ); 375 374 376 375 // *********************************** manipulators *********************************** … … 407 406 char * s; 408 407 inline _Istream_str_base; 409 }; // _Istream_C width408 }; // _Istream_Cstr 410 409 411 410 // Restrict nesting of input manipulators to those combinations that make sense. 411 412 struct _Istream_Cstr { 413 _Istream_Cwidth cstr; 414 }; // _Istream_Cstr 412 415 413 416 struct _Istream_Cquoted { … … 415 418 }; // _Istream_Cquoted 416 419 417 struct _Istream_Cstr {418 _Istream_Cwidth cstr;419 }; // _Istream_Cstr420 421 420 static inline { 422 421 // 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); 423 422 _Istream_Cwidth wdi( unsigned int wd, char s[] ) { 424 if ( wd <= 1 ) throw Resume ExceptionInst( cstring_length ); // minimum 1 character and null terminator423 if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator 425 424 return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } }; 426 425 } 427 426 _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) { 428 if ( wd <= 1 || wd <= rwd ) throw Resume ExceptionInst( cstring_length ); // minimum 1 character, null terminator, plus subset427 if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset 429 428 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;433 429 } 434 430 _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) { … … 439 435 return (_Istream_Cquoted &)f; 440 436 } 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 } 441 440 _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; } 442 441 _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_C str)@{ { .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} } }; } 444 443 _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; } 445 444 _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; } … … 451 450 istype & ?|?( istype & is, _Istream_Cquoted f ); 452 451 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 454 455 } // distribution 455 456 -
src/AST/Attribute.hpp
ra4da45e r0522ebe 9 9 // Author : Aaron B. Moss 10 10 // Created On : Fri May 10 10:30:00 2019 11 // Last Modified By : Peter A. Buhr11 // Last Modified By : Aaron B. Moss 12 12 // Created On : Fri May 10 10:30:00 2019 13 // Update Count : 213 // Update Count : 1 14 14 // 15 15 … … 34 34 35 35 Attribute( const std::string & name = "", std::vector<ptr<Expr>> && params = {}) 36 : name( name ), params( params ) {}36 : name( name ), params( params ) {} 37 37 virtual ~Attribute() = default; 38 38 -
src/GenPoly/Box.cpp
ra4da45e r0522ebe 366 366 367 367 /// Passes extra layout arguments for sized polymorphic type parameters. 368 voidpassTypeVars(368 ast::vector<ast::Expr>::iterator passTypeVars( 369 369 ast::ApplicationExpr * expr, 370 ast::vector<ast::Expr> & extraArgs,371 370 ast::FunctionType const * funcType ); 372 371 /// Wraps a function application with a new temporary for the … … 388 387 /// parameter list. arg should point into expr's argument list. 389 388 void boxParams( 390 ast::ApplicationExpr * expr,391 ast:: Type const * polyRetType,389 ast::ApplicationExpr const * expr, 390 ast::vector<ast::Expr>::iterator arg, 392 391 ast::FunctionType const * function, 393 392 const TypeVarMap & typeVars ); … … 396 395 void addInferredParams( 397 396 ast::ApplicationExpr * expr, 398 ast::vector<ast::Expr> & extraArgs,397 ast::vector<ast::Expr>::iterator arg, 399 398 ast::FunctionType const * functionType, 400 399 const TypeVarMap & typeVars ); … … 637 636 ast::Expr const * ret = expr; 638 637 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 639 642 TypeVarMap exprTypeVars; 640 643 makeTypeVarMap( function, exprTypeVars ); … … 659 662 } 660 663 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 ); 667 673 passAdapters( mutExpr, function, exprTypeVars ); 668 674 … … 760 766 } 761 767 762 voidCallAdapter::passTypeVars(768 ast::vector<ast::Expr>::iterator CallAdapter::passTypeVars( 763 769 ast::ApplicationExpr * expr, 764 ast::vector<ast::Expr> & extraArgs,765 770 ast::FunctionType const * function ) { 766 771 assert( typeSubs ); 772 ast::vector<ast::Expr>::iterator arg = expr->args.begin(); 767 773 // Pass size/align for type variables. 768 774 for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) { … … 774 780 toString( typeSubs ).c_str(), typeVar->typeString().c_str() ); 775 781 } 776 extraArgs.emplace_back(782 arg = expr->args.insert( arg, 777 783 new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) ); 778 extraArgs.emplace_back( 784 arg++; 785 arg = expr->args.insert( arg, 779 786 new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) ); 780 } 787 arg++; 788 } 789 return arg; 781 790 } 782 791 … … 904 913 905 914 void CallAdapter::boxParams( 906 ast::ApplicationExpr * expr,907 ast:: Type const * polyRetType,915 ast::ApplicationExpr const * expr, 916 ast::vector<ast::Expr>::iterator arg, 908 917 ast::FunctionType const * function, 909 918 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 914 919 for ( auto param : function->params ) { 915 920 assertf( arg != expr->args.end(), … … 923 928 void CallAdapter::addInferredParams( 924 929 ast::ApplicationExpr * expr, 925 ast::vector<ast::Expr> & extraArgs,930 ast::vector<ast::Expr>::iterator arg, 926 931 ast::FunctionType const * functionType, 927 932 TypeVarMap const & typeVars ) { 933 ast::vector<ast::Expr>::iterator cur = arg; 928 934 for ( auto assertion : functionType->assertions ) { 929 935 auto inferParam = expr->inferred.inferParams().find( … … 934 940 ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr ); 935 941 boxParam( newExpr, assertion->result, typeVars ); 936 extraArgs.emplace_back( newExpr.release() ); 942 cur = expr->args.insert( cur, newExpr.release() ); 943 ++cur; 937 944 } 938 945 } … … 1109 1116 ); 1110 1117 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 ); 1113 1123 adapteeApp->args.push_back( makeAdapterArg( 1114 1124 assertParam->var, assertArg->var->get_type(), … … 1967 1977 bool hasDynamicLayout = false; 1968 1978 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 ); 1971 1982 if ( !baseParam->isComplete() ) continue; 1972 1983 ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>(); -
src/InitTweak/FixInit.cpp
ra4da45e r0522ebe 1134 1134 ast::Expr * thisExpr = new ast::CastExpr(funcDecl->location, new ast::VariableExpr(funcDecl->location, thisParam ), thisParam->get_type()->stripReferences()); 1135 1135 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) ); 1137 1137 1138 1138 if ( callStmt ) { -
src/InitTweak/GenInit.cc
ra4da45e r0522ebe 239 239 if ( varExpr->var == retVal ) return stmt; 240 240 } 241 const ast::Stmt *ctorStmt = genCtorDtor(241 ast::ptr<ast::Stmt> ctorStmt = genCtorDtor( 242 242 retVal->location, "?{}", retVal, stmt->expr ); 243 243 assertf( ctorStmt, … … 327 327 void ManagedTypes::endScope() { managedTypes.endScope(); } 328 328 329 const ast::Stmt * genCtorDtor( const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) {329 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) { 330 330 assertf(objDecl, "genCtorDtor passed null objDecl"); 331 331 InitExpander srcParam(arg); -
src/InitTweak/GenInit.h
ra4da45e r0522ebe 33 33 34 34 /// 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);35 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr); 36 36 37 37 /// creates an appropriate ConstructorInit node which contains a constructor, destructor, and C-initializer -
src/Parser/DeclarationNode.cc
ra4da45e r0522ebe 10 10 // Created On : Sat May 16 12:34:05 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 23 18:25:57 202413 // Update Count : 1 53312 // Last Modified On : Thu Dec 14 19:05:17 2023 13 // Update Count : 1407 14 14 // 15 15 … … 159 159 160 160 if ( ! attributes.empty() ) { 161 os << string( indent + 2, ' ' ) << "with attributes " << endl;161 os << string( indent + 2, ' ' ) << "with attributes " << endl; 162 162 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; 165 164 } // for 166 165 } // if … … 538 537 } // DeclarationNode::checkSpecifiers 539 538 540 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q , bool copyattr) {539 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q ) { 541 540 funcSpecs |= q->funcSpecs; 542 541 storageClasses |= q->storageClasses; 543 542 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 ); 552 549 553 550 return this; … … 684 681 } 685 682 686 DeclarationNode * DeclarationNode::addType( DeclarationNode * o , bool copyattr) {683 DeclarationNode * DeclarationNode::addType( DeclarationNode * o ) { 687 684 if ( o ) { 688 685 checkSpecifiers( o ); 689 copySpecifiers( o , copyattr);686 copySpecifiers( o ); 690 687 if ( o->type ) { 691 688 if ( ! type ) { 692 689 if ( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) { 693 // Hide type information aggregate instances.694 690 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; 697 692 if ( o->type->kind == TypeData::Aggregate ) { 698 693 type->aggInst.hoistType = o->type->aggregate.body; … … 705 700 type = o->type; 706 701 } // if 707 o->type = nullptr; // change ownership702 o->type = nullptr; 708 703 } else { 709 704 addTypeToType( o->type, type ); … … 958 953 } 959 954 960 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o , bool copyattr) {955 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o ) { 961 956 if ( ! o ) return nullptr; 962 957 963 o->copySpecifiers( this , copyattr);958 o->copySpecifiers( this ); 964 959 if ( type ) { 965 960 TypeData * srcType = type; … … 1004 999 DeclarationNode * newnode = new DeclarationNode; 1005 1000 newnode->type = ret; 1006 if ( ret->kind == TypeData::Aggregate ) {1007 newnode->attributes.swap( ret->aggregate.attributes );1008 } // if1009 1001 return newnode; 1010 1002 } // if … … 1118 1110 if ( extr->type->kind == TypeData::Aggregate ) { 1119 1111 // typedef struct { int A } B is the only case? 1120 extracted_named = ! extr->type->aggregate.anon;1112 extracted_named = !extr->type->aggregate.anon; 1121 1113 } else if ( extr->type->kind == TypeData::Enum ) { 1122 1114 // typedef enum { A } B is the only case? 1123 extracted_named = ! extr->type->enumeration.anon;1115 extracted_named = !extr->type->enumeration.anon; 1124 1116 } else { 1125 1117 extracted_named = true; -
src/Parser/DeclarationNode.h
ra4da45e r0522ebe 9 9 // Author : Andrew Beach 10 10 // Created On : Wed Apr 5 11:38:00 2023 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Sat Feb 17 09:24:12 202413 // Update Count : 411 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Apr 5 11:55:00 2023 13 // Update Count : 0 14 14 // 15 15 … … 83 83 void checkQualifiers( const TypeData *, const TypeData * ); 84 84 void checkSpecifiers( DeclarationNode * ); 85 DeclarationNode * copySpecifiers( DeclarationNode * , bool = true);86 DeclarationNode * addType( DeclarationNode * , bool = true);85 DeclarationNode * copySpecifiers( DeclarationNode * ); 86 DeclarationNode * addType( DeclarationNode * ); 87 87 DeclarationNode * addTypedef(); 88 88 DeclarationNode * addEnumBase( DeclarationNode * ); … … 106 106 107 107 DeclarationNode * cloneType( std::string * newName ); 108 DeclarationNode * cloneBaseType( DeclarationNode * newdecl , bool = true);108 DeclarationNode * cloneBaseType( DeclarationNode * newdecl ); 109 109 110 110 DeclarationNode * appendList( DeclarationNode * node ) { -
src/Parser/TypeData.cc
ra4da45e r0522ebe 10 10 // Created On : Sat May 16 15:12:51 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 23 08:58:30 202413 // Update Count : 73412 // Last Modified On : Thu Dec 14 18:59:12 2023 13 // Update Count : 684 14 14 // 15 15 … … 20 20 21 21 #include "AST/Decl.hpp" // for AggregateDecl, ObjectDecl, TypeDe... 22 #include "AST/Attribute.hpp" // for Attribute23 22 #include "AST/Init.hpp" // for SingleInit, ListInit 24 23 #include "AST/Print.hpp" // for print 25 24 #include "Common/SemanticError.h" // for SemanticError 26 25 #include "Common/utility.h" // for splice, spliceBegin 27 #include "Common/Iterate.hpp" // for reverseIterate28 26 #include "Parser/ExpressionNode.h" // for ExpressionNode 29 27 #include "Parser/StatementNode.h" // for StatementNode … … 201 199 newtype->aggregate.kind = aggregate.kind; 202 200 newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr; 203 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;204 201 newtype->aggregate.params = maybeCopy( aggregate.params ); 205 202 newtype->aggregate.actuals = maybeCopy( aggregate.actuals ); 206 203 newtype->aggregate.fields = maybeCopy( aggregate.fields ); 207 newtype->aggregate.attributes = aggregate.attributes;208 204 newtype->aggregate.body = aggregate.body; 209 205 newtype->aggregate.anon = aggregate.anon; 210 206 newtype->aggregate.tagged = aggregate.tagged; 207 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr; 211 208 break; 212 209 case AggregateInst: … … 339 336 } // if 340 337 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; 349 339 } // if 350 340 break; … … 368 358 } // if 369 359 if ( enumeration.body ) { 370 os << string( indent + 2, ' ' ) << " with body" << endl;360 os << string( indent + 2, ' ' ) << " with body" << endl; 371 361 } // if 372 362 if ( base ) { … … 1098 1088 1099 1089 ast::BaseInstType * buildComAggInst( 1100 const TypeData * t d,1090 const TypeData * type, 1101 1091 std::vector<ast::ptr<ast::Attribute>> && attributes, 1102 1092 ast::Linkage::Spec linkage ) { 1103 switch ( t d->kind ) {1093 switch ( type->kind ) { 1104 1094 case TypeData::Enum: 1105 if ( t d->enumeration.body ) {1095 if ( type->enumeration.body ) { 1106 1096 ast::EnumDecl * typedecl = 1107 buildEnum( t d, std::move( attributes ), linkage );1097 buildEnum( type, std::move( attributes ), linkage ); 1108 1098 return new ast::EnumInstType( 1109 1099 typedecl, 1110 buildQualifiers( t d)1100 buildQualifiers( type ) 1111 1101 ); 1112 1102 } else { 1113 1103 return new ast::EnumInstType( 1114 *t d->enumeration.name,1115 buildQualifiers( t d)1104 *type->enumeration.name, 1105 buildQualifiers( type ) 1116 1106 ); 1117 1107 } // if 1118 1108 break; 1119 1109 case TypeData::Aggregate: 1120 if ( t d->aggregate.body ) {1110 if ( type->aggregate.body ) { 1121 1111 ast::AggregateDecl * typedecl = 1122 buildAggregate( t d, std::move( attributes ), linkage );1123 switch ( t d->aggregate.kind ) {1112 buildAggregate( type, std::move( attributes ), linkage ); 1113 switch ( type->aggregate.kind ) { 1124 1114 case ast::AggregateDecl::Struct: 1125 1115 case ast::AggregateDecl::Coroutine: … … 1128 1118 return new ast::StructInstType( 1129 1119 strict_dynamic_cast<ast::StructDecl *>( typedecl ), 1130 buildQualifiers( t d)1120 buildQualifiers( type ) 1131 1121 ); 1132 1122 case ast::AggregateDecl::Union: 1133 1123 return new ast::UnionInstType( 1134 1124 strict_dynamic_cast<ast::UnionDecl *>( typedecl ), 1135 buildQualifiers( t d)1125 buildQualifiers( type ) 1136 1126 ); 1137 1127 case ast::AggregateDecl::Trait: … … 1142 1132 } // switch 1143 1133 } else { 1144 switch ( t d->aggregate.kind ) {1134 switch ( type->aggregate.kind ) { 1145 1135 case ast::AggregateDecl::Struct: 1146 1136 case ast::AggregateDecl::Coroutine: … … 1148 1138 case ast::AggregateDecl::Thread: 1149 1139 return new ast::StructInstType( 1150 *t d->aggregate.name,1151 buildQualifiers( t d)1140 *type->aggregate.name, 1141 buildQualifiers( type ) 1152 1142 ); 1153 1143 case ast::AggregateDecl::Union: 1154 1144 return new ast::UnionInstType( 1155 *t d->aggregate.name,1156 buildQualifiers( t d)1145 *type->aggregate.name, 1146 buildQualifiers( type ) 1157 1147 ); 1158 1148 case ast::AggregateDecl::Trait: 1159 1149 return new ast::TraitInstType( 1160 *t d->aggregate.name,1161 buildQualifiers( t d)1150 *type->aggregate.name, 1151 buildQualifiers( type ) 1162 1152 ); 1163 1153 default: -
src/Parser/TypeData.h
ra4da45e r0522ebe 9 9 // Author : Peter A. Buhr 10 10 // Created On : Sat May 16 15:18:36 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Thu Feb 22 16:30:31 202413 // Update Count : 2 1011 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Mar 1 10:44:00 2023 13 // Update Count : 206 14 14 // 15 15 … … 30 30 ast::AggregateDecl::Aggregate kind; 31 31 const std::string * name = nullptr; 32 const std::string * parent = nullptr;33 32 DeclarationNode * params = nullptr; 34 33 ExpressionNode * actuals = nullptr; // holds actual parameters later applied to AggInst 35 34 DeclarationNode * fields = nullptr; 36 std::vector<ast::ptr<ast::Attribute>> attributes;37 35 bool body; 38 36 bool anon; 39 37 bool tagged; 38 const std::string * parent = nullptr; 40 39 }; 41 40 -
src/Parser/parser.yy
ra4da45e r0522ebe 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 23 18:25:46 202413 // Update Count : 6 48412 // Last Modified On : Sun Nov 26 13:18:06 2023 13 // Update Count : 6398 14 14 // 15 15 … … 102 102 103 103 DeclarationNode * 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__. 105 105 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 129 111 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 modified112 cl->cloneBaseType( cur ); 131 113 } // 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 ); 136 116 return declList; 137 117 } // distAttr … … 212 192 fieldList = DeclarationNode::newName( nullptr ); 213 193 } // if 194 // return distAttr( typeSpec, fieldList ); // mark all fields in list 214 195 215 196 // printf( "fieldDecl3 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout, 0 ); 216 DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list197 DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list 217 198 // printf( "fieldDecl4 temp %p\n", temp ); temp->print( std::cout, 0 ); 218 199 return temp; … … 780 761 | string_literal '`' identifier // CFA, postfix call 781 762 { $$ = 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 qualified784 // name, it is assumed all name-tokens after the first are identifiers, regardless of how the lexer identifies785 // 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 TYPEDEFname795 // z.T; // lexer returns T is TYPEGENname796 // z.Z; // lexer returns Z is TYPEDEFname797 // z.U; // lexer returns U is TYPEDEFname798 // z.E; // lexer returns E is TYPEDEFname799 // }800 763 | postfix_expression '.' identifier 801 764 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); } 802 | postfix_expression '.' TYPEDEFname // CFA, SKULLDUGGERY803 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); }804 | postfix_expression '.' TYPEGENname // CFA, SKULLDUGGERY805 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); }806 807 765 | postfix_expression '.' INTEGERconstant // CFA, tuple index 808 766 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_constantInteger( yylloc, *$3 ) ) ); } … … 1081 1039 | logical_OR_expression '?' comma_expression ':' conditional_expression 1082 1040 { $$ = new ExpressionNode( build_cond( yylloc, $1, $3, $5 ) ); } 1041 // FIX ME: computes $1 twice 1083 1042 | 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 ) ); } 1085 1044 ; 1086 1045 … … 1897 1856 declaration_list: 1898 1857 declaration 1899 | declaration_list declaration { $$ = $1->appendList( $2 ); } 1858 | declaration_list declaration 1859 { $$ = $1->appendList( $2 ); } 1900 1860 ; 1901 1861 … … 1930 1890 declaration: // old & new style declarations 1931 1891 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 } 1932 1898 | cfa_declaration ';' // CFA 1933 1899 | static_assert // C11 … … 2382 2348 sue_declaration_specifier: // struct, union, enum + storage class + type specifier 2383 2349 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 } 2384 2356 | declaration_qualifier_list sue_type_specifier 2385 2357 { $$ = $2->addQualifiers( $1 ); } … … 2392 2364 sue_type_specifier: // struct, union, enum + type specifier 2393 2365 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 } 2394 2372 | type_qualifier_list 2395 2373 { if ( $1->type != nullptr && $1->type->forall ) forall = true; } // remember generic type … … 2464 2442 elaborated_type: // struct, union, enum 2465 2443 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 } 2466 2450 | enum_type 2467 2451 ; … … 2695 2679 { $$ = DeclarationNode::newEnum( nullptr, $4, true, false )->addQualifiers( $2 ); } 2696 2680 | 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; } 2698 2682 | ENUM attribute_list_opt identifier 2699 2683 { typedefTable.makeTypedef( *$3, "enum_type 1" ); } … … 2710 2694 } 2711 2695 | 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; } 2713 2697 | ENUM '(' ')' attribute_list_opt '{' enumerator_list comma_opt '}' 2714 2698 { … … 2716 2700 } 2717 2701 | 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; } 2719 2703 | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt identifier attribute_list_opt 2720 2704 { … … 3193 3177 // unit, which is a dubious task, especially because C uses name rather than structural typing; hence it is 3194 3178 // 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 ) { 3197 3180 if ( $1->type->aggInst.aggregate->kind == TypeData::Enum && $1->type->aggInst.aggregate->enumeration.anon ) { 3198 3181 SemanticError( yylloc, "extern anonymous enumeration is currently unimplemented." ); $$ = nullptr; -
src/ResolvExpr/CandidateFinder.cpp
ra4da45e r0522ebe 1018 1018 } 1019 1019 1020 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer); 1020 if (argType.as<ast::PointerType>()) funcFinder.otypeKeys.insert(Mangle::Encoding::pointer); 1021 1021 else funcFinder.otypeKeys.insert(Mangle::mangle(argType, Mangle::NoGenericParams | Mangle::Type)); 1022 1022 } … … 1283 1283 restructureCast( cand->expr, toType, castExpr->isGenerated ), 1284 1284 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. 1286 1286 // if this somehow changes in the future (e.g. delayed by indeterminate return type) 1287 1287 // we may need to revisit the logic. … … 1618 1618 void Finder::postvisit( const ast::ConditionalExpr * conditionalExpr ) { 1619 1619 // candidates for condition 1620 CandidateFinder finder1( context, tenv ); 1620 1621 ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 ); 1621 CandidateFinder finder1( context, tenv );1622 1622 finder1.find( arg1, ResolveMode::withAdjustment() ); 1623 1623 if ( finder1.candidates.empty() ) return; 1624 1624 1625 1625 // 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();1629 1626 CandidateFinder finder2( context, tenv ); 1630 1627 finder2.allowVoid = true; 1631 finder2.find( arg2, ResolveMode::withAdjustment() );1628 finder2.find( conditionalExpr->arg2, ResolveMode::withAdjustment() ); 1632 1629 if ( finder2.candidates.empty() ) return; 1633 1630 … … 1900 1897 CandidateRef newCand = 1901 1898 std::make_shared<Candidate>( 1902 newExpr, copy( tenv ), ast::OpenVarSet{}, 1899 newExpr, copy( tenv ), ast::OpenVarSet{}, 1903 1900 ast::AssertionSet{}, Cost::zero, cost 1904 1901 ); 1905 1902 1906 1903 if (newCand->expr->env) { 1907 1904 newCand->env.add(*newCand->expr->env); -
src/ResolvExpr/ResolveTypeof.cc
ra4da45e r0522ebe 35 35 36 36 struct ResolveTypeof : public ast::WithShortCircuiting { 37 const ResolveContext & context;37 const ResolveContext & context; 38 38 39 ResolveTypeof( const ResolveContext & context ) : context( context ) {} 39 ResolveTypeof( const ResolveContext & context ) : 40 context( context ) {} 40 41 41 void previsit( const ast::TypeofType * ) { visit_children = false; }42 void previsit( const ast::TypeofType * ) { visit_children = false; } 42 43 43 const ast::Type * postvisit( const ast::TypeofType * typeofType ) {44 // pass on null expression45 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; 46 47 47 ast::ptr< ast::Type > newType;48 if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {49 // typeof wrapping type50 newType = tyExpr->type;51 } else {52 // typeof wrapping expression53 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 } 59 60 60 // clear qualifiers for base, combine with typeoftype quals regardless61 if ( typeofType->kind == ast::TypeofType::Basetypeof ) {62 // replace basetypeof(<enum>) by int63 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 } 73 74 74 return newType.release();75 }75 return newType.release(); 76 } 76 77 }; 77 78 … … 110 111 111 112 const 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 } 115 116 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 } 123 124 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 } 128 129 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; 132 133 } 133 134 134 const ast::ObjectDecl *fixObjectInit( 135 const ast::ObjectDecl *decl,const ResolveContext &context) {136 if ( decl->isTypeFixed) {137 return decl;138 }135 const ast::ObjectDecl *fixObjectInit(const ast::ObjectDecl *decl, 136 const ResolveContext &context) { 137 if (decl->isTypeFixed) { 138 return decl; 139 } 139 140 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); 146 142 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; 180 189 } 181 190 182 } // namespace ResolvExpr191 } // namespace ResolvExpr 183 192 184 193 // Local Variables: // -
src/ResolvExpr/Resolver.cc
ra4da45e r0522ebe 50 50 51 51 namespace ResolvExpr { 52 53 namespace { 54 /// Finds deleted expressions in an expression tree55 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 } 68 68 } 69 69 } 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 designations81 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 } 95 95 } 96 96 } 97 97 } 98 visit_children = false; 98 99 } 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 ); 162 153 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; 468 162 } 469 163 } 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 }; 473 603 } 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 } 625 1176 } 626 1177 } 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 } 1242 1246 1243 1247 } // namespace ResolvExpr -
src/SymTab/GenImplicitCall.cpp
ra4da45e r0522ebe 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // GenImplicitCall. cpp -- Generate code for implicit operator calls.7 // GenImplicitCall.hpp -- 8 8 // 9 9 // Author : Andrew Beach … … 31 31 namespace { 32 32 33 using Inserter = std::back_insert_iterator<std::list<ast::ptr<ast::Stmt>>>; 34 33 template< typename OutIter > 35 34 ast::ptr< ast::Stmt > genCall( 36 35 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, 38 37 const ast::Type * type, const ast::Type * addCast, LoopDirection forward = LoopForward ); 39 38 … … 42 41 /// optionally returns a statement which must be inserted prior to the containing loop, if 43 42 /// there is one 43 template< typename OutIter > 44 44 ast::ptr< ast::Stmt > genScalarCall( 45 45 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, 47 47 const ast::Type * addCast = nullptr 48 48 ) { … … 97 97 /// Store in out a loop which calls fname on each element of the array with srcParam and 98 98 /// dstParam as arguments. If forward is true, loop goes from 0 to N-1, else N-1 to 0 99 template< typename OutIter > 99 100 void genArrayCall( 100 101 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, 102 103 const ast::ArrayType * array, const ast::Type * addCast = nullptr, 103 104 LoopDirection forward = LoopForward … … 165 166 } 166 167 168 template< typename OutIter > 167 169 ast::ptr< ast::Stmt > genCall( 168 170 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, 170 172 const ast::Type * type, const ast::Type * addCast, LoopDirection forward 171 173 ) { 172 174 if ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) { 173 175 genArrayCall( 174 srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out), at, addCast,176 srcParam, dstParam, loc, fname, std::forward< OutIter >(out), at, addCast, 175 177 forward ); 176 178 return {}; 177 179 } else { 178 180 return genScalarCall( 179 srcParam, dstParam, loc, fname, std::forward< Inserter&&>( out ), type, addCast );181 srcParam, dstParam, loc, fname, std::forward< OutIter >( out ), type, addCast ); 180 182 } 181 183 } … … 183 185 } // namespace 184 186 185 const ast::Stmt *genImplicitCall(187 ast::ptr< ast::Stmt > genImplicitCall( 186 188 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 187 189 const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj, … … 189 191 ) { 190 192 // unnamed bit fields are not copied as they cannot be accessed 191 if ( isUnnamedBitfield( obj ) ) return nullptr;193 if ( isUnnamedBitfield( obj ) ) return {}; 192 194 193 195 ast::ptr< ast::Type > addCast; … … 197 199 } 198 200 199 std:: list< ast::ptr< ast::Stmt > > stmts;201 std::vector< ast::ptr< ast::Stmt > > stmts; 200 202 genCall( 201 203 srcParam, dstParam, loc, fname, back_inserter( stmts ), obj->type, addCast, forward ); 202 204 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 } 213 219 } 214 220 … … 220 226 // compile-command: "make install" // 221 227 // End: // 228 229 -
src/SymTab/GenImplicitCall.hpp
ra4da45e r0522ebe 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // GenImplicitCall.hpp -- Generate code for implicit operator calls.7 // GenImplicitCall.hpp -- 8 8 // 9 9 // Author : Andrew Beach … … 25 25 /// Returns a generated call expression to function fname with srcParam and 26 26 /// dstParam. Intended to be used with generated ?=?, ?{}, and ^?{} calls. 27 const ast::Stmt *genImplicitCall(27 ast::ptr<ast::Stmt> genImplicitCall( 28 28 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 29 29 const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj, -
src/Validate/Autogen.cpp
ra4da45e r0522ebe 133 133 /// Generates a single struct member operation. 134 134 /// (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( 136 137 const CodeLocation& location, 137 138 const ast::ObjectDecl * dstParam, const ast::Expr * src, … … 524 525 } 525 526 526 const ast::Stmt *StructFuncGenerator::makeMemberOp(527 ast::ptr<ast::Stmt> StructFuncGenerator::makeMemberOp( 527 528 const CodeLocation& location, const ast::ObjectDecl * dstParam, 528 529 const ast::Expr * src, const ast::ObjectDecl * field, … … 539 540 ) 540 541 ); 541 const ast::Stmt *stmt = genImplicitCall(542 auto stmt = genImplicitCall( 542 543 srcParam, dstSelect, location, func->name, 543 544 field, direction … … 597 598 location, field, new ast::VariableExpr( location, srcParam ) 598 599 ) : nullptr; 599 const ast::Stmt *stmt =600 ast::ptr<ast::Stmt> stmt = 600 601 makeMemberOp( location, dstParam, srcSelect, field, func, direction ); 601 602 602 603 if ( nullptr != stmt ) { 603 stmts->kids. emplace_back( stmt );604 stmts->kids.push_back( stmt ); 604 605 } 605 606 } … … 622 623 for ( auto param = params.begin() + 1 ; current != end ; ++current ) { 623 624 const ast::ptr<ast::Decl> & member = *current; 624 const ast::Stmt *stmt = nullptr;625 ast::ptr<ast::Stmt> stmt = nullptr; 625 626 auto field = member.as<ast::ObjectDecl>(); 626 627 // Not sure why it could be null. … … 640 641 641 642 if ( nullptr != stmt ) { 642 stmts->kids. emplace_back( stmt );643 stmts->kids.push_back( stmt ); 643 644 } 644 645 } -
src/main.cc
ra4da45e r0522ebe 181 181 182 182 static void _Signal(struct sigaction & act, int sig, int flags ) { 183 sigemptyset( &act.sa_mask );184 183 act.sa_flags = flags; 185 184 -
tests/.expect/attributes.arm64.txt
ra4da45e r0522ebe 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); -
tests/.expect/attributes.x64.txt
ra4da45e r0522ebe 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); -
tests/.expect/attributes.x86.txt
ra4da45e r0522ebe 6 6 7 7 } 8 struct __a ttribute__ ((unused)) __anonymous0 {8 struct __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); -
tests/collections/.expect/string-istream-manip.txt
ra4da45e r0522ebe 70 70 14 71 71 15 72 16 get this line73 17 @# this line 1)-{}74 18 @# this line 1)-{}75 19 abc76 20 abc77 21 d d78 79 d80 22 ZC44%81 23 ZC44%82 72 1 yyyyyyyyyyyyyyyyyyyy 83 73 2 abcxxx … … 95 85 14 96 86 15 97 16 get this line98 17 @# this line 1)-{}99 18 @# this line 1)-{}100 19 abc101 20 abc102 21 d d103 104 d105 22 ZC44%106 23 ZC44% -
tests/collections/.in/string-istream-manip.txt
ra4da45e r0522ebe 38 38 aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww 39 39 uuuuu 40 get this line41 @# this line 1)-{}%42 @# this line 2)-{}%43 "abc"44 'abc '45 { d d46 47 d }48 X ZC44%Y49 X ZC55%Y50 40 abc 51 41 cccccb … … 55 45 aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww 56 46 uuuuu 57 get this line58 @# this line 1)-{}%59 @# this line 2)-{}%60 "abc"61 'abc '62 { d d63 64 d }65 X ZC44%Y66 X ZC55%Y -
tests/collections/string-istream-manip.cfa
ra4da45e r0522ebe 10 10 // The test cases that use plainjane(-) are exercising the via-manipulator code path, 11 11 // just with trivial manipulation. 12 static _Istream_Sstr plainjane( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.rwd : false}} }; }12 static _Istream_Sstr plainjane( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.rwd : false}} }; } 13 13 static _Istream_Rstr plainjane( string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.rwd : false}} }; } 14 14 15 15 static 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 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 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"; 20 175 } 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 } 22 203 } 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 assures28 // no spurious splitting merging.29 // The lengths of the strings are chosen to match white-box knowledge of when the string layer30 // has tor drive the cstring layer through a second iteration:31 // - for no-manip, lengths are near the room at end of string heap32 // (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 12834 // 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 manipulator38 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 alone42 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-op56 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-op71 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) delimiter134 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 10 10 // Author : Thierry Delisle 11 11 // Created On : Wed Oct 12 15:28:01 2022 12 // Last Modified By : Peter A. Buhr13 // Last Modified On : Sun Feb 11 09:26:13 202414 // Update Count : 212 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 15 // 16 16 17 17 #include <fstream.hfa> 18 18 19 #include "../meta/fork+exec.hfa" 19 20 … … 21 22 #include <parseargs.hfa> 22 23 23 int main( int argc, char * argv[]) {24 int main(int argc, char * argv[]) { 24 25 check_main(argv[0]); 25 26 … … 40 41 41 42 char **left; 42 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );43 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left); 43 44 44 45 sout | "yes/no :" | YN; … … 50 51 } 51 52 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);53 int true_main(const char * path, char * env[]) { 54 printf("no arg:\n"); 55 if(pid_t child = strict_fork(); child == 0) { 56 int ret = execle(path, "parsebools", (const char*)0p, env); 57 if(ret < 0) { 58 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 59 exit(1); 59 60 } 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 } 64 66 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 } 76 79 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 } 88 92 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 } 100 105 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 } 112 118 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 } 124 131 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 } 136 144 137 printf( "All Done!\n");145 printf("All Done!\n"); 138 146 139 147 return 0; -
tests/configs/parsenums.cfa
ra4da45e r0522ebe 32 32 #endif 33 33 34 int true_main( const char * exec);35 36 int main( int argc, char * argv[]) {37 check_main( argv[0]);34 int true_main(const char * exec); 35 36 int main(int argc, char * argv[]) { 37 check_main(argv[0]); 38 38 39 39 int i = -3; … … 52 52 53 53 char **left; 54 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );54 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left); 55 55 56 56 sout | "int :" | i; … … 61 61 } 62 62 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" ); 63 int true_main(const char * path, char * env[]) { 64 printf("no arg:\n"); 65 if(pid_t child = strict_fork(); child == 0) { 66 int ret = execle(path, "parsenums", (const char*)0p, env); 67 if(ret < 0) { 68 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 69 exit(1); 70 } 71 } 72 else { 73 int status = do_wait(child); 74 print_status(status); 75 } 76 77 printf("all 0 arg:\n"); 78 if(pid_t child = strict_fork(); child == 0) { 79 int ret = execle(path, "parsenums", "-i=0", "-u=0", "-l=0", "-L=0", "-d=0", (const char*)0p, env); 80 if(ret < 0) { 81 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 82 exit(1); 83 } 84 } 85 else { 86 int status = do_wait(child); 87 print_status(status); 88 } 89 90 printf("negative vals arg:\n"); 91 if(pid_t child = strict_fork(); child == 0) { 92 int ret = execle(path, "parsenums", "-i=-1", "-d=-1", (const char*)0p, env); 93 if(ret < 0) { 94 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 95 exit(1); 96 } 97 } 98 else { 99 int status = do_wait(child); 100 print_status(status); 101 } 102 103 printf("funky notation arg:\n"); 104 if(pid_t child = strict_fork(); child == 0) { 105 int ret = execle(path, "parsenums", "-i=0x10", "-u=0x20", "-l=0x300", "-L=0x4000", "-d=5e6", (const char*)0p, env); 106 if(ret < 0) { 107 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 108 exit(1); 109 } 110 } 111 else { 112 int status = do_wait(child); 113 print_status(status); 114 } 115 116 printf("big values arg:\n"); 117 if(pid_t child = strict_fork(); child == 0) { 118 int ret = execle(path, "parsenums", "-i=2147483647", "-u=4294967295", "-l=" BIG_UNSIGNED_LONG, "-L=18446744073709551615", "-d=5e6", (const char*)0p, env); 119 if(ret < 0) { 120 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 121 exit(1); 122 } 123 } 124 else { 125 int status = do_wait(child); 126 print_status(status); 127 } 128 129 printf("too big values arg:\n"); 130 if(pid_t child = strict_fork(); child == 0) { 131 int ret = execle(path, "parsenums", "-i=2147483648", (const char*)0p, env); 132 if(ret < 0) { 133 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 134 exit(1); 135 } 136 } 137 else { 138 int status = do_wait(child); 139 print_status(status); 140 } 141 142 if(pid_t child = strict_fork(); child == 0) { 143 int ret = execle(path, "parsenums", "-u=4294967296", (const char*)0p, env); 144 if(ret < 0) { 145 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 146 exit(1); 147 } 148 } 149 else { 150 int status = do_wait(child); 151 print_status(status); 152 } 153 154 if(pid_t child = strict_fork(); child == 0) { 155 int ret = execle(path, "parsenums", "-l=" TOO_BIG_UNSIGNED_LONG, (const char*)0p, env); 156 if(ret < 0) { 157 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 158 exit(1); 159 } 160 } 161 else { 162 int status = do_wait(child); 163 print_status(status); 164 } 165 166 if(pid_t child = strict_fork(); child == 0) { 167 int ret = execle(path, "parsenums", "-L=18446744073709551616", (const char*)0p, env); 168 if(ret < 0) { 169 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 170 exit(1); 171 } 172 } 173 else { 174 int status = do_wait(child); 175 print_status(status); 176 } 177 178 printf("negative errors arg:\n"); 179 if(pid_t child = strict_fork(); child == 0) { 180 int ret = execle(path, "parsenums", "-u=-1", (const char*)0p, env); 181 if(ret < 0) { 182 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 183 exit(1); 184 } 185 } 186 else { 187 int status = do_wait(child); 188 print_status(status); 189 } 190 191 if(pid_t child = strict_fork(); child == 0) { 192 int ret = execle(path, "parsenums", "-l=-1", (const char*)0p, env); 193 if(ret < 0) { 194 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 195 exit(1); 196 } 197 } 198 else { 199 int status = do_wait(child); 200 print_status(status); 201 } 202 203 if(pid_t child = strict_fork(); child == 0) { 204 int ret = execle(path, "parsenums", "-L=-1", (const char*)0p, env); 205 if(ret < 0) { 206 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 207 exit(1); 208 } 209 } 210 else { 211 int status = do_wait(child); 212 print_status(status); 213 } 214 215 printf("All Done!\n"); 204 216 205 217 return 0; -
tests/configs/usage.cfa
ra4da45e r0522ebe 10 10 // Author : Thierry Delisle 11 11 // Created On : Wed Oct 12 15:28:01 2022 12 // Last Modified By : Peter A. Buhr13 // Last Modified On : Sun Feb 11 09:29:51 202414 // Update Count : 112 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 15 // 16 16 … … 24 24 25 25 sout | "No args, no errors"; 26 if ( pid_t child = strict_fork(); child == 0) {26 if(pid_t child = strict_fork(); child == 0) { 27 27 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 } 33 34 34 35 sout | "No args, with errors"; 35 if ( pid_t child = strict_fork(); child == 0) {36 if(pid_t child = strict_fork(); child == 0) { 36 37 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 } 42 44 43 45 sout | "Args with short names only:"; 44 if ( pid_t child = strict_fork(); child == 0) {46 if(pid_t child = strict_fork(); child == 0) { 45 47 int a, b, c; 46 48 array( cfa_option, 3 ) opts; … … 48 50 opts[1] = (cfa_option){'b', "", "Second arg", b }; 49 51 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 } 55 58 56 59 sout | "Args with long names only:"; 57 if ( pid_t child = strict_fork(); child == 0) {60 if(pid_t child = strict_fork(); child == 0) { 58 61 int a, b, c; 59 62 array( cfa_option, 3 ) opts; … … 61 64 opts[1] = (cfa_option){'\0', "BB", "Second arg", b }; 62 65 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 } 68 72 69 73 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) { 71 75 int a, b, c; 72 76 array( cfa_option, 3 ) opts; … … 74 78 opts[1] = (cfa_option){'b', "BBBB", "Second arg", b }; 75 79 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 } 81 86 82 87 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) { 84 89 int a, b, c; 85 90 array( cfa_option, 3 ) opts; … … 87 92 opts[1] = (cfa_option){'b', "BBBB", "", b }; 88 93 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 } 94 100 95 101 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) { 97 103 int a, b, c; 98 104 array( cfa_option, 3 ) opts; … … 100 106 opts[1] = (cfa_option){'b', "BBBB", "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", b }; 101 107 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 } 107 114 } 108 115 109 116 // no used 110 static int true_main( const char * path, char * env[]) { return 0; }117 static int true_main(const char * path, char * env[]) { return 0; } -
tests/errors/.expect/declaration.txt
ra4da45e r0522ebe 8 8 with members 9 9 i: int 10 with body10 with body 11 11 12 12 … … 14 14 with members 15 15 i: int 16 with body16 with body 17 17 18 18 -
tests/exceptions/pingpong_nonlocal.cfa
ra4da45e r0522ebe 2 2 #include <thread.hfa> 3 3 #include <mutex_stmt.hfa> 4 #include <Exception.hfa>5 4 6 ExceptionDecl( num_ping_pongs, int num; ); 5 exception num_ping_pongs { int num; }; 6 vtable(num_ping_pongs) num_ping_pongs_vt; 7 7 8 8 thread Ping_Pong { … … 16 16 this.name = name; 17 17 cnt = 0; 18 ?{}( except, ExceptionArgs( num_ping_pongs, 0 ));18 ?{}( except, &num_ping_pongs_vt, 0 ); 19 19 } 20 20 … … 29 29 for () { 30 30 while( ! poll( this ) ) { yield(); } 31 inc_resume_at( cnt );31 inc_resume_at( cnt ); 32 32 } 33 33 } catchResume( num_ping_pongs * e; e->num < numtimes ) { … … 37 37 mutex( sout ) sout | name | "catch" | cnt | e->num; 38 38 if ( e->num == numtimes ) { 39 inc_resume_at( e->num );39 inc_resume_at( e->num ); 40 40 } 41 41 } … … 49 49 &ping.partner = &pong; // create cycle 50 50 &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 ); 52 53 } 53 54 sout | "main end"; -
tests/io/.in/manipulatorsInput.txt
ra4da45e r0522ebe 13 13 @# this line 1)-{}% 14 14 @# this line 2)-{}% 15 "abc"15 "abc" 16 16 'abc ' 17 17 { d d … … 35 35 @# this line 1)-{}% 36 36 @# this line 2)-{}% 37 "abc"37 "abc" 38 38 'abc ' 39 39 { d d -
tests/io/manipulatorsInput.cfa
ra4da45e r0522ebe 7 7 // Created On : Sat Jun 8 17:58:54 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Mon Feb 5 21:54:49202410 // Update Count : 13 49 // Last Modified On : Sun Jan 28 11:59:55 2024 10 // Update Count : 133 11 11 // 12 12 … … 114 114 sin | wdi( sizeof(s), s ); sout | "2" | s; 115 115 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; 117 117 sin | ignore( wdi( sizeof(s), 8, s ) ); sout | "5" | s; 118 118 … … 131 131 s[0] = 'q'; s[1] = '\0'; 132 132 sin | excl( "u", wdi( sizeof(s), s ) ); sout | "15" | s; 133 sin | skip( "u" ) | nl;133 sin | skip( "u" ) | "\n"; 134 134 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; 137 137 138 138 sin | quoted( wdi( sizeof(s), s ) ); sout | "19" | s; -
tools/cfa.nanorc
ra4da45e r0522ebe 10 10 color green "\<(forall|trait|(o|d|f|t)type|mutex|_Bool|volatile|virtual)\>" 11 11 color green "\<(float|double|bool|char|int|short|long|enum|void|auto)\>" 12 color green "\<(static|const|extern|(un)?signed|inline|sizeof|vtable)\>" 12 13 color 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)\>"15 14 16 15 # Declarations
Note:
See TracChangeset
for help on using the changeset viewer.