Changeset a4da45e
- Timestamp:
- Feb 26, 2024, 3:53:42 AM (20 months ago)
- Branches:
- master
- Children:
- 3f9a8d0
- Parents:
- 0522ebe (diff), 022bce0 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - Files:
-
- 3 added
- 2 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 (added)
-
doc/theses/jiada_liang_MMath/background.tex (modified) (1 diff)
-
doc/theses/jiada_liang_MMath/content1.tex (deleted)
-
doc/theses/jiada_liang_MMath/content2.tex (deleted)
-
doc/theses/jiada_liang_MMath/implementation.tex (added)
-
doc/theses/jiada_liang_MMath/intro.tex (modified) (1 diff)
-
doc/theses/jiada_liang_MMath/relatedwork.tex (added)
-
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
r0522ebe ra4da45e 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sun Jan 21 13:17:48202414 %% Update Count : 6 3313 %% Last Modified On : Sun Feb 25 17:37:46 2024 14 %% Update Count : 640 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{C11}\xspace} % C11 symbolic name 42 \newcommand{\Celeven}{\textrm{C1\!1}\xspace} % C11 symbolic name 43 43 44 \newcommand{\CCIcon}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}} % C++ icon 44 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name45 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym46 45 % numbers disallowed in latex variables names => use number names 47 \newcommand{\CCeleven}{\protect\CCIcon{1 1}\xspace}% C++11 symbolic name46 \newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace} % C++11 symbolic name 48 47 \newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace} % C++14 symbolic name 49 48 \newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace} % C++17 symbolic name 50 49 \newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace} % C++20 symbolic name 50 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name 51 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym 52 51 53 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name 52 54 … … 293 295 xleftmargin=\parindentlnth, % indent code to paragraph indentation 294 296 extendedchars=true, % allow ASCII characters in the range 128-255 295 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'296 297 mathescape=false, % disable LaTeX math escape in CFA code $...$ 297 298 keepspaces=true, % … … 304 305 literate= 305 306 % {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 306 {-}{\raisebox{-1pt}{\t exttt{-}}}1307 {-}{\raisebox{-1pt}{\ttfamily-}}1 307 308 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 308 309 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 310 {'}{\ttfamily'\!}1 309 311 {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1 310 312 {<-}{$\leftarrow$}2 … … 318 320 \lstset{ 319 321 language=CFA, 320 %moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 322 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 321 323 moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 322 324 %moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ … … 328 330 % inline code ©...© (copyright symbol) emacs: C-q M-) 329 331 \lstMakeShortInline© % single-character for \lstinline 332 330 333 \else% regular ASCI characters 334 331 335 \lstnewenvironment{cfa}[1][]{% necessary 332 336 \lstset{ 333 337 language=CFA, 334 338 escapechar=\$, % LaTeX escape in CFA code 335 mathescape=false, % LaTeX math escape in CFA code $...$336 339 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 337 340 }% lstset -
doc/LaTeXmacros/common.tex
r0522ebe ra4da45e 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Wed Jan 24 08:43:57202414 %% Update Count : 59313 %% Last Modified On : Sun Feb 25 18:11:56 2024 14 %% Update Count : 614 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{C11}\xspace} % C11 symbolic name 42 \newcommand{\Celeven}{\textrm{C1\!1}\xspace} % C11 symbolic name 43 43 44 \newcommand{\CCIcon}{\textrm{C}\kern-.1em\hbox{+\kern-.25em+}} % C++ icon 44 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name45 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym46 45 % numbers disallowed in latex variables names => use number names 47 \newcommand{\CCeleven}{\protect\CCIcon{1 1}\xspace}% C++11 symbolic name46 \newcommand{\CCeleven}{\protect\CCIcon{1\!1}\xspace} % C++11 symbolic name 48 47 \newcommand{\CCfourteen}{\protect\CCIcon{14}\xspace} % C++14 symbolic name 49 48 \newcommand{\CCseventeen}{\protect\CCIcon{17}\xspace} % C++17 symbolic name 50 49 \newcommand{\CCtwenty}{\protect\CCIcon{20}\xspace} % C++20 symbolic name 50 \newcommand{\CC}[1][]{\protect\CCIcon{#1}\xspace} % C++ symbolic name 51 \newcommand{\Cpp}[1][]{\CC{#1}} % C++ synonym 52 51 53 \newcommand{\Csharp}{C\raisebox{-0.7ex}{\relsize{2}$^\sharp$}\xspace} % C# symbolic name 52 54 … … 297 299 xleftmargin=\parindentlnth, % indent code to paragraph indentation 298 300 extendedchars=true, % allow ASCII characters in the range 128-255 299 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-'300 301 mathescape=false, % disable LaTeX math escape in CFA code $...$ 301 302 keepspaces=true, % … … 308 309 literate= 309 310 % {-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 310 {-}{\raisebox{-1pt}{\t exttt{-}}}1311 {-}{\raisebox{-1pt}{\ttfamily-}}1 311 312 {^}{\raisebox{0.6ex}{$\scriptstyle\land\,$}}1 312 313 {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 314 {'}{\ttfamily'\!}1 313 315 {`}{\ttfamily\upshape\hspace*{-0.3ex}`}1 314 316 {<-}{$\leftarrow$}2 … … 322 324 \lstset{ 323 325 language=CFA, 324 %moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 326 escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' 325 327 moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. 326 328 %moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ … … 332 334 % inline code ©...© (copyright symbol) emacs: C-q M-) 333 335 \lstMakeShortInline© % single-character for \lstinline 336 334 337 \else% regular ASCI characters 338 335 339 \lstnewenvironment{cfa}[1][]{% necessary 336 340 \lstset{ 337 341 language=CFA, 338 342 escapechar=\$, % LaTeX escape in CFA code 339 mathescape=false, % LaTeX math escape in CFA code $...$340 343 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 341 344 }% lstset -
doc/LaTeXmacros/lstlang.sty
r0522ebe ra4da45e 8 8 %% Created On : Sat May 13 16:34:42 2017 9 9 %% Last Modified By : Peter A. Buhr 10 %% Last Modified On : Thu Sep 21 08:40:05 202311 %% Update Count : 3 110 %% Last Modified On : Fri Feb 16 07:59:29 2024 11 %% Update Count : 37 12 12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 13 … … 125 125 } 126 126 127 % C++ programming language128 \lstdefinelanguage{C++}[ANSI]{C++}{129 morekeywords={nullptr,}130 }131 132 127 % uC++ programming language, based on ANSI C++ 133 \lstdefinelanguage{uC++}[ ANSI]{C++}{128 \lstdefinelanguage{uC++}[GNU]{C++}{ 134 129 morekeywords={ 135 130 _Accept, _AcceptReturn, _AcceptWait, _Actor, _At, _Catch, _CatchResume, _CorActor, _Cormonitor, _Coroutine, -
doc/bibliography/pl.bib
r0522ebe ra4da45e 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}}, 3744 3754 } 3745 3755 … … 8243 8253 } 8244 8254 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 8245 8268 @inproceedings{Burns81, 8246 8269 keywords = {N-thread software-solution mutual exclusion}, -
doc/proposals/enum.tex
r0522ebe ra4da45e 117 117 \end{abstract} 118 118 119 \section{ Background}119 \section{Introduction} 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), time(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), specific times (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 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}: 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. 147 132 \begin{cquote} 148 133 \small\sf\setlength{\tabcolsep}{3pt} 149 134 \begin{tabular}{rccccccccccc} 150 @enum@ Weekday \{ & Monday, & Tuesday, & Wednesday, & Thursday = 10,& Friday, & Saturday, & Sunday \}; \\ 151 \it\color{red}position & 0 & 1 & 2 & 3 & 4 & 5 & 6 \\ 152 \it\color{red}label & Monday & Tuesday & Wednesday & Thursday & Friday & Saturday & Sunday \\ 153 \it\color{red}value & 0 & 1 & 2 & {\color{red}10}& 11 & 12 & 13 135 \it\color{red}enumeration & \multicolumn{7}{c}{\it\color{red}enumerators} \\ 136 $\downarrow$\hspace*{25pt} & \multicolumn{7}{c}{$\downarrow$} \\ 137 @enum@ Weekday \{ & Monday, & Tuesday, & Wednesday, & Thursday,& Friday, & Saturday, & Sunday \}; \\ 138 \it\color{red}position & 0 & 1 & 2 & 3 & 4 & 5 & 6 \\ 139 \it\color{red}label & Monday & Tuesday & Wednesday & Thursday & Friday & Saturday & Sunday \\ 140 \it\color{red}value & 0 & 1 & 2 & 3 & 4 & 5 & 6 154 141 \end{tabular} 155 142 \end{cquote} 156 Finally, C enumerators are \newterm{unscoped}, i.e., enumerators declared inside of an @enum@ are visible in the enclosing scope of the @enum@ type. 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. 157 171 158 172 In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values. 159 In practice, since integral constants in Chave type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables.160 F urthermore, there is an implicit bidirectional conversion between an enumeration and integral types.173 In practice, since integral constants are used, which have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables. 174 Finally, there is an implicit bidirectional conversion between an enumeration and integral types. 161 175 \begin{lstlisting}[label=lst:enum_scope] 162 176 { 163 enum Weekday { ... };$\C{// enumerators implicitly projected into local scope}$177 enum Weekday { /* as above */ }; $\C{// enumerators implicitly projected into local scope}$ 164 178 Weekday weekday = Monday; $\C{// weekday == 0}$ 165 179 weekday = Friday; $\C{// weekday == 11}$ 166 int i = Sunday $\C{// implicit conversion to int, i == 13}$180 int i = Sunday; $\C{// implicit conversion to int, i == 13}$ 167 181 weekday = 10000; $\C{// UNDEFINED! implicit conversion to Weekday}$ 168 182 } … … 171 185 The implicit conversion from @int@ to an enumeration type is an unnecessary source of error. 172 186 187 It is common for C programmers to ``believe'' there are 3 equivalent forms of constant enumeration. 188 \begin{lstlisting}[label=lst:enum_scope] 189 #define Monday 0 190 static const int Monday = 0; 191 enum { Monday }; 192 \end{lstlisting} 193 For @#define@, the programmer has to play compiler and explicitly manage the enumeration values; 194 furthermore, these are independent constants outside of any language type mechanism. 195 The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{ 196 C allows variable-length array-declarations (VLA), so this case does work, but it fails in \CC, which does not support VLAs, unless it is \lstinline{g++}.} and immediate operands of assembler instructions. 197 Only the @enum@ form is managed by the compiler, is part of the language type-system, and works in all C constant-expression locations. 198 199 173 200 \section{\CFA-Style Enum} 174 201 … … 176 203 \CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages. 177 204 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 178 254 \subsection{Enumerator Typing} 179 255 180 \CFA extends the enumeration by parameterizing the enumeration with a type for the enumerators, allowing enumerators to be assigned any values from the declared type. 181 Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's constants used to set the enumerators. 182 183 Typed enumerates deals with \emph{harmonizing} problem between an enumeration and its companion data. 184 The following example is from the \CFA compiler, written in \CC. 185 \begin{lstlisting} 186 enum integral_types { chr, schar, uschar, sshort, ushort, sint, usint, ..., NO_OF_ITYPES }; 187 char * integral_names[NO_OF_ITYPES] = { 188 "char", "signed char", "unsigned char", 189 "signed short int", "unsigned short int", 190 "signed int", "unsigned int", 191 ... 192 }; 193 \end{lstlisting} 194 The \emph{harmonizing} problem occurs because the enumeration declaration is in one header file and the names are declared in another translation unit. 195 It is up to the programmer to ensure changes made in one location are harmonized with the other location (by identifying this requirement within a comment). 196 The typed enumeration largely solves this problem by combining and managing the two data types. 197 \begin{lstlisting} 198 enum( char * ) integral_types { 199 chr = "char", schar = "signed char", uschar = "unsigned char", 200 sshort = "signed short int", ushort = "unsigned short int", 201 sint = "signed int", usint = "unsigned int", 202 ... 203 }; 256 \CFA extends the enumeration declaration by parameterizing with a type (like a generic type), allowing enumerators to be assigned any values from the declared type. 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; 204 266 \end{lstlisting} 205 267 … … 223 285 enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i }; 224 286 // pointer 225 enum( @char *@ ) Names { Fred = "F red", Mary = "Mary", Jane = "Jane" };287 enum( @char *@ ) Names { Fred = "FRED", Mary = "MARY", Jane = "JANE" }; 226 288 int i, j, k; 227 289 enum( @int *@ ) ptr { I = &i, J = &j, K = &k }; 228 290 enum( @int &@ ) ref { I = i, J = j, K = k }; 229 291 // tuple 230 enum( @[int, int]@ ) { T = [ 1, 2 ] }; 292 enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$ 231 293 // function 232 294 void f() {...} void g() {...} … … 234 296 // aggregate 235 297 struct Person { char * name; int age, height; }; 236 enum( @Person@ ) friends { Liz = { "Elizabeth", 22, 170 }, Beth = Liz, Jon = { "Jonathan", 35, 190 } };298 @***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz, Jon = { "JONATHAN", 35, 190 } }; 237 299 \end{lstlisting} 238 300 \caption{Enumerator Typing} … … 240 302 \end{figure} 241 303 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 242 329 \subsection{Pure Enumerators} 243 330 244 An empty type, @enum()@, implies the enumerators are pure symbols without values;331 An empty enumerator type, @enum()@, implies the enumerators are pure symbols without values but set properties; 245 332 hence, there is no default conversion to @int@. 246 333 247 334 \begin{lstlisting} 248 335 enum() Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND }; 249 Mode iomode = O_RDONLY; 336 @***@Mode iomode = O_RDONLY; 337 bool b = iomode == O_RDONLY || iomode < O_APPEND; 250 338 int i = iomode; $\C{\color{red}// disallowed}$ 251 sout | O_TRUNC; $\C{\color{red}// disallowed}$252 339 \end{lstlisting} 253 340 254 341 \subsection{Enumerator Subset} 255 342 256 If follows from enumerator typing that the type of the enumerators can be another enumerator. 257 \begin{lstlisting} 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 258 347 enum( char ) Letter { A = 'A', B = 'B', C = 'C', ..., Z = 'Z' }; 259 enum( Letter ) Greek { Alph = A, Beta = B, ..., Zeta = Z }; // alphabet intersection 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} 260 353 Letter letter = A; 261 Greak greek = Alph;262 letter = Alph; $\C{// allowed}$354 @***@Greak greek = Beta; 355 letter = Beta; $\C{// allowed, letter == B}$ 263 356 greek = A; $\C{\color{red}// disallowed}$ 264 357 \end{lstlisting} 265 Enumeration @Greek@ may have more or less enumerators than @Letter@, but the enumerator values must be from @Letter@. 266 Therefore, @Greek@ enumerators are a subset of type @Letter@ and are type compatible with enumeration @Letter@, but @Letter@ enumerators are not type compatible with enumeration @Greek@. 358 267 359 268 360 \subsection{Enumeration Inheritance} 269 361 270 \CFA Plan-9 inheritance may be used with enumerations. 271 \begin{lstlisting} 272 enum( char * ) Name2 { @inline Name@, Jack = "Jack", Jill = "Jill" }; 273 enum /* inferred */ Name3 { @inline Name2@, Sue = "Sue", Tom = "Tom" }; 274 \end{lstlisting} 275 Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Name@ by containment, and a @Name@ enumeration is a subtype of enumeration @Name2@. 362 \CFA Plan-9 inheritance may be used with enumerations, where Plan-9 inheritance is containment inheritance with implicit unscoping (like a nested unnamed @struct@/@union@ in C). 363 \begin{lstlisting} 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@. 276 369 Note, enumerators must be unique in inheritance but enumerator values may be repeated. 277 370 278 371 The enumeration type for the inheriting type must be the same as the inherited type; 279 372 hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@. 280 When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important. 281 282 Specifically, the inheritance relationship for Names is: 283 \begin{lstlisting} 284 Name $\(\subset\)$ Name2 $\(\subset\)$ Name3 $\(\subset\)$ const char * // enum type of Name 373 % When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important. 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}$ 285 377 \end{lstlisting} 286 378 For the given function prototypes, the following calls are valid. … … 288 380 \begin{tabular}{ll} 289 381 \begin{lstlisting} 290 void f( Name );291 void g( Name 2 );292 void h( Name 3 );382 void f( Names ); 383 void g( Names2 ); 384 void h( Names3 ); 293 385 void j( const char * ); 294 386 \end{lstlisting} … … 298 390 g( Fred ); g( Jill ); 299 391 h( Fred ); h( Jill ); h( Sue ); 300 j( Fred ); j( Jill ); j( Sue ); j( "W ill" );392 j( Fred ); j( Jill ); j( Sue ); j( "WILL" ); 301 393 \end{lstlisting} 302 394 \end{tabular} 303 395 \end{cquote} 304 Note, the validity of calls is the same for call-by-reference as for call-by-value, and const restrictions are the same as for other types. 305 306 \subsection{Enumerator Scoping} 307 308 A \CFA-enum can be scoped, meaning the enumerator constants are not projected into the enclosing scope. 309 \begin{lstlisting} 310 enum Weekday @!@ { /* as above */ }; 311 enum Colour( char * ) @!@ { /* as above */ }; 312 \end{lstlisting} 313 where the @'!'@ implies the enumerators are \emph{not} projected. 314 The enumerators of a scoped enumeration are accessed using qualifications, like the fields of an aggregate. 315 % The syntax of $qualified\_expression$ for \CFA-enum is the following: 316 % $$<qualified\_expression> := <enum\_type>.<enumerator>$$ 317 \begin{lstlisting} 318 Weekday weekday = @Weekday.Monday@; $\C{// qualification}$ 319 Colour colour = @Colour.@Red; 320 colour = @Colour.@Blue; 321 \end{lstlisting} 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 322 398 323 399 \subsection{Enumeration Pseudo-functions} 324 400 325 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@ .401 Pseudo-functions are function-like operators that do not result in any run-time computations, i.e., like @sizeof@, @offsetof@, @typeof@. 326 402 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.. 327 403 328 \subsubsection{Enumerator Attributes}329 404 The attributes of an enumerator are accessed by pseudo-functions @position@, @value@, and @label@. 330 405 \begin{lstlisting} 331 int green_pos = @position@( Colour.Green ); $\C{// 1}$ 332 char * green_value = @value@( Colour.Green ); $\C{// "G"}$ 333 char * green_label = @label@( Colour.Green ); $\C{// "Green"}$ 334 \end{lstlisting} 335 336 Enumeration Greek may have more or less enumerators than Letter, but the enumerator values must be from Letter. 337 Therefore, Greek enumerators are a subset of type Letter and are type compatible with enumeration Letter, but Letter enumerators are not type compatible with enumeration Greek. 338 339 % An instance of \CFA-enum (denoted as @<enum_instance>@) is a label for the defined enum name. 340 % The label can be retrieved by calling the function @label( <enum_instance> )@. 341 % Similarly, the @value()@ function returns the value used to initialize the \CFA-enum. 342 343 \subsubsection{\lstinline{enumerate()}} 344 345 \begin{lstlisting}[label=lst:c_switch] 346 enum(int) C_ENUM { First, Second, Third = First, Fourth }; 347 int v( C_ENUM e ) { 348 switch( e ) { 349 case First: return 0; break; 350 case Second: return 1; break; 351 // case Third: return 2; break; 352 // case Fourth: return 3; break; 353 }; 354 }; 355 \end{lstlisting} 356 In the @C_ENUM@ example, @Third@ is an alias of @First@ and @Fourth@ is an alias of @Second@. 357 Programmers cannot make case branches for @Third@ and @Fourth@ because the switch statement matches cases by the enumerator's value. 358 Case @First@ and @Third@, or @Second@ and @Fourth@, has duplicate case values. 359 360 @enumerate()@ is a pseudo-function that makes the switch statement match by an enumerator instead. 361 \begin{lstlisting}[label=lst:c_switch_enumerate] 362 enum(double) C_ENUM { First, Second, Third = First, Fourth }; 363 C_ENUM variable_a = First, variable_b = Second, variable_c = Third, variable_d = Fourth; 364 int v(C_ENUM e) { 365 switch( enumeratate( e ) ) { 366 case First: return e; break; 367 case Second: return value( e ); break; 368 case Third: return label( e ); break; 369 case Fourth: return position( e ); break; 370 }; 371 }; 406 @***@int jane_pos = @position@( Names.Jane ); $\C{// 2}$ 407 @***@char * jane_value = @value@( Names.Jane ); $\C{// "JANE"}$ 408 @***@char * jane_label = @label@( Names.Jane ); $\C{// "Jane"}$ 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; 372 468 p(variable_a); // 0 373 469 p(variable_b); // 1 … … 1015 1111 If the @aggregation_name@ is identified as a \CFA enumeration, the compiler checks if @field@ presents in the declared \CFA enumeration. 1016 1112 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 1038 1113 \subsection{Instance Declaration} 1039 1114 … … 1218 1293 1219 1294 \subsection{\CC} 1220 1221 \CC is backwards compatible with C, so it inherited C's enumerations, except there is no implicit conversion from an integral value to an enumeration; 1222 hence, the values in a \CC enumeration can only be its enumerators (without a cast). 1223 There is no mechanism to iterate through an enumeration. 1224 1225 \CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), so the enumerators are local to the enumeration and must be accessed using type qualification, e.g., @Weekday::Monday@. 1295 \label{s:C++RelatedWork} 1296 1297 \CC is backwards compatible with C, so it inherited C's enumerations. 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. 1336 \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} 1226 1341 \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:1230 1342 \begin{lstlisting}[language=c++,{moredelim=**[is][\color{red}]{@}{@}}] 1231 enum class RGB : @long@ { Red, Green, Blue }; 1232 enum class rgb : @char@ { Red = 'r', Green = 'g', Blue = 'b' }; 1233 enum class srgb : @signed char@ { Red = -1, Green = 0, Blue = 1 }; 1234 RGB colour1 = @RGB::@Red; 1235 rgb colour2 = @rgb::@Red; 1236 srgb colour3 = @srgb::@Red; 1237 \end{lstlisting} 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 1238 1361 1239 1362 \subsection{Go} -
doc/theses/jiada_liang_MMath/background.tex
r0522ebe ra4da45e 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 comma 21 }; 22 \end{cfa} 23 This feature allow enumerator lines to be interchanged without moving a comma.\footnote{ 24 A terminating comma appears in other C syntax, \eg the initializer list.} 25 Finally, C enumerators are \Newterm{unscoped}, \ie enumerators declared inside of an @enum@ are visible (projected) into the enclosing scope of the @enum@ type. 26 27 In theory, a C enumeration \emph{variable} is an implementation-defined integral type large enough to hold all enumerated values. 28 In practice, since integral constants are used, which have type @int@ (unless qualified with a size suffix), C uses @int@ as the underlying type for enumeration variables. 29 Finally, there is an implicit bidirectional conversion between an enumeration and its integral type. 30 \begin{cfa} 31 { 32 enum Weekday { /* as above */ }; $\C{// enumerators implicitly projected into local scope}$ 33 Weekday weekday = Monday; $\C{// weekday == 0}$ 34 weekday = Friday; $\C{// weekday == 11}$ 35 int i = Sunday; $\C{// implicit conversion to int, i == 13}$ 36 weekday = 10000; $\C{// UNDEFINED! implicit conversion to Weekday}$ 37 } 38 int j = Wednesday; $\C{// ERROR! Wednesday is not declared in this scope}$ 39 \end{cfa} 40 The implicit conversion from @int@ to an enumeration type is an unnecessary source of error. 41 42 It is common for C programmers to ``believe'' there are 3 equivalent forms of constant enumeration. 43 \begin{cfa} 44 #define Monday 0 45 static const int Monday = 0; 46 enum { Monday }; 47 \end{cfa} 48 For @#define@, the programmer has to play compiler and explicitly manage the enumeration values; 49 furthermore, these are independent constants outside of any language type mechanism. 50 The same explicit management is true for @const@ declarations, and the @const@ variable cannot appear in constant-expression locations, like @case@ labels, array dimensions,\footnote{ 51 C allows variable-length array-declarations (VLA), so this case does work, but it fails in \CC, which does not support VLAs, unless it is \lstinline{g++}.} and immediate operands of assembler instructions. 52 Only the @enum@ form is managed by the compiler, is part of the language type-system, and works in all C constant-expression locations. -
doc/theses/jiada_liang_MMath/intro.tex
r0522ebe ra4da45e 1 1 \chapter{Introduction} 2 2 3 Testing glossy abbreviations \gls{foo} and \gls{bar}, and glossy definitions \gls{git} and \gls{gulp}. 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. 4 8 5 And use the glossy abbreviations \gls{foo} and \gls{bar}, and definitions \gls{git} and \gls{gulp} again. 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} -
doc/theses/jiada_liang_MMath/uw-ethesis-frontpgs.tex
r0522ebe ra4da45e 131 131 \begin{center}\textbf{Abstract}\end{center} 132 132 133 Enumerated type ... 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. 134 137 135 138 \cleardoublepage -
doc/theses/jiada_liang_MMath/uw-ethesis.tex
r0522ebe ra4da45e 91 91 % cfa macros used in the document 92 92 \input{common} 93 %\usepackage input{common}93 %\usepackage{common} 94 94 \CFAStyle % CFA code-style 95 \lstset{language= CFA} % default language96 \lstset{basicstyle=\linespread{0.9}\sf} % CFA typewriter font 95 \lstset{language=cfa,belowskip=-1pt} % set default language to CFA 96 97 97 \newcommand{\newtermFont}{\emph} 98 98 \newcommand{\Newterm}[1]{\newtermFont{#1}} … … 211 211 \input{intro} 212 212 \input{background} 213 \input{content1} 214 \input{content2} 213 \input{CFAenum} 214 \input{implementation} 215 \input{relatedwork} 215 216 \input{performance} 216 217 \input{conclusion} -
doc/user/user.tex
r0522ebe ra4da45e 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Tue Jan 30 09:02:41202414 %% Update Count : 6 04613 %% Last Modified On : Mon Feb 12 11:50:26 2024 14 %% Update Count : 6199 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 4179 4182 For implicit formatted output, the common case is printing a series of variables separated by whitespace. 4180 4183 \begin{cquote} … … 4255 4258 Note, \CFA stream variables ©stdin©, ©stdout©, ©stderr©, ©exit©, and ©abort© overload C variables ©stdin©, ©stdout©, ©stderr©, and functions ©exit© and ©abort©, respectively. 4256 4259 4260 4261 \subsubsection{Stream Input} 4262 4257 4263 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. 4258 4264 \begin{cquote} 4259 4265 \begin{lrbox}{\myboxA} 4260 4266 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4261 int x; double y char z; 4267 char c; int i; double d 4262 4268 \end{cfa} 4263 4269 \end{lrbox} … … 4266 4272 \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CFA}} & \multicolumn{1}{c@{\hspace{2em}}}{\textbf{\CC}} & \multicolumn{1}{c}{\textbf{Python}} \\ 4267 4273 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4268 sin | x | y | z;4274 sin | c | i | d; 4269 4275 \end{cfa} 4270 4276 & 4271 4277 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4272 cin >> x >> y >> z;4278 cin >> c >> i >> d; 4273 4279 \end{cfa} 4274 4280 & 4275 4281 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 4276 x = int(input()); y = float(input()); z = input();4282 c = input(); i = int(input()); d = float(input()); 4277 4283 \end{cfa} 4278 4284 \\ 4279 4285 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4280 ® 1® ®2.5® ®A®4286 ®A® ®1® ®2.5® 4281 4287 4282 4288 … … 4284 4290 & 4285 4291 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4286 ® 1® ®2.5® ®A®4292 ®A® ®1® ®2.5® 4287 4293 4288 4294 … … 4290 4296 & 4291 4297 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4298 ®A® 4292 4299 ®1® 4293 4300 ®2.5® 4294 ®A®4295 4301 \end{cfa} 4296 4302 \end{tabular} … … 4309 4315 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. 4310 4316 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©. 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©. 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©. 4319 4356 \begin{itemize}[topsep=4pt,itemsep=2pt,parsep=0pt] 4320 4357 \item … … 4932 4969 4933 4970 \subsection{Input Manipulators} 4934 4935 The following \Index{manipulator}s control scanning of input values (reading), and only affect the format of the argument. 4936 4937 Certain manipulators support a \newterm{scanset}, which is a simple regular expression, where the matching set contains any Latin-1 character (8-bits) or character ranges using minus. 4971 \label{s:InputManipulators} 4972 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. 4938 4981 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) ©'§'©. 4939 4982 The following string is matched by this scanset: 4940 4983 \begin{cfa} 4941 !&%$ abAA () ZZZ ?? xx§\S\S\S§ 4942 \end{cfa} 4943 To match a minus, put it as the first character, ©"-0-9"©. 4944 Other complex forms of regular-expression matching are not supported. 4945 4946 A string variable \emph{must} be large enough to contain the input sequence. 4947 To force programmers to consider buffer overruns for C-string input, C-strings can only be read with a width field, which should specify a size less than or equal to the C-string size, \eg: 4948 \begin{cfa} 4949 char line[64]; 4950 sin | wdi( ®sizeof(line)®, line ); // must specify size 4951 \end{cfa} 4952 4953 Currently, there is no mechanism to detect if a value read exceeds the capwhen Most types are finite sized, \eg integral types only store value that fit into their corresponding storage, 8, 16, 32, 64, 128 bits. 4954 Hence, an input value may be too large, and the result of the read is often considered undefined, which leads to difficlt to locate runtime errors. 4955 All reads in \CFA check if values do not fit into the argument variable's type and raise the exception 4956 All types are 4984 !&%$ abAA () ZZZ ??§\S§ xx§\S\S§ 4985 \end{cfa} 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. 4957 4990 4958 4991 \begin{enumerate} 4959 4992 \item 4960 \Indexc{skip}( scanset )\index{manipulator!skip@©skip©}, ©skip©( $N$ ) 4961 The first form uses a scanset to skip matching characters. 4962 The second form skips the next $N$ characters, including newline. 4963 If the match successes, the input characters are discarded, and input continues with the next character. 4993 \Indexc{skip}( \textit{scanset} )\index{manipulator!skip@©skip©}, ©skip©( $N$ ) 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. 4964 4996 If the match fails, the input characters are left unread. 4965 4997 \begin{cfa}[belowskip=0pt] 4966 char s k[§\,§] = "abc";4967 sin | "abc " | skip( sk ) | skip( 5 ); // match input sequence4998 char scanset[§\,§] = "abc"; 4999 sin | "abc§\textvisiblespace§" | skip( scanset ) | skip( 5 ); §\C{// match and skip input sequence}§ 4968 5000 \end{cfa} 4969 5001 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 4970 ®abc ® 4971 ®abc ® 4972 ®xx® 4973 \end{cfa} 4974 4975 \item 4976 \Indexc{wdi}( maximum, variable )\index{manipulator!wdi@©wdi©} 4977 For all types except ©char *©, whitespace is skipped until an appropriate value is found for the specified variable type. 4978 maximum is the maximum number of characters read for the current operation. 5002 ®abc abc xxx® 5003 \end{cfa} 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. 4979 5010 \begin{cfa}[belowskip=0pt] 4980 5011 char ch; char ca[3]; int i; double d; … … 4985 5016 \end{cfa} 4986 5017 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. 4987 5019 4988 5020 Note, input ©wdi© cannot be overloaded with output ©wd© because both have the same parameters but return different types. … … 4990 5022 4991 5023 \item 4992 \Indexc{wdi}( maximum size, ©char s[]© )\index{manipulator!wdi@©wdi©} 4993 For type ©char *©, maximum is the maximum number of characters read for the current operation. 4994 Any number of non-whitespace characters, stopping at the first whitespace character found. A terminating null character is automatically added at the end of the stored sequence 5024 \Indexc{wdi}( $maximum\ size$, ©char s[]© )\index{manipulator!wdi@©wdi©} 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. 4995 5028 \begin{cfa}[belowskip=0pt] 4996 char cs tr[10];4997 sin | wdi( sizeof(cs tr), cstr);5029 char cs[10]; 5030 sin | wdi( sizeof(cs), cs ); 4998 5031 \end{cfa} 4999 5032 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 5000 ®abcd1233.456E+2® 5001 \end{cfa} 5002 5003 \item 5004 \Indexc{wdi}( maximum size, maximum read, ©char s[]© )\index{manipulator!wdi@©wdi©} 5005 For type ©char *©, maximum is the maximum number of characters read for the current operation. 5033 ®012345678® 5034 \end{cfa} 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$. 5006 5040 \begin{cfa}[belowskip=0pt] 5007 char c h; char ca[3]; int i; double d;5008 sin | wdi( sizeof(c h), ch ) | wdi( sizeof(ca), ca[0] ) | wdi( 3, i ) | wdi( 8, d ); // c == 'a', ca == "bcd", i == 123, d == 345.65041 char cs[10]; 5042 sin | wdi( sizeof(cs), 9, cs ); 5009 5043 \end{cfa} 5010 5044 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 5011 ®abcd1233.456E+2® 5012 \end{cfa} 5013 5014 \item 5015 \Indexc{ignore}( reference-value )\index{manipulator!ignore@©ignore©} 5016 For all types, the data is read from the stream depending on the argument type but ignored, \ie it is not stored in the argument. 5045 ®012345678®9 5046 \end{cfa} 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). 5017 5053 \begin{cfa}[belowskip=0pt] 5018 double d; 5019 sin | ignore( d ); // d is unchanged 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}§ 5020 5057 \end{cfa} 5021 5058 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] 5022 ® -75.35e-4® 25 5023 \end{cfa} 5024 5025 \item 5026 \Indexc{incl}( scanset, wdi-input-string )\index{manipulator!incl@©incl©} 5027 For C-string types only, the scanset matches any number of characters \emph{in} the set. 5028 Matching characters are read into the C input-string and null terminated. 5059 ®abc ?? #@%® 5060 ®abc ?? #@%X® w 5061 \end{cfa} 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). 5029 5068 \begin{cfa}[belowskip=0pt] 5030 char s[10]; 5031 sin | incl( "abc", s ); 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 ); 5032 5098 \end{cfa} 5033 5099 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 5036 5102 5037 5103 \item 5038 \Indexc{excl}( scanset, wdi-input-string )\index{manipulator!excl@©excl©} 5039 For C-string types, the scanset matches any number of characters \emph{not in} the set. 5040 Non-matching characters are read into the C input-string and null terminated. 5104 \Indexc{excl}( scanset, $wdi\ manipulator$ )\index{manipulator!excl@©excl©} 5105 consumes the \emph{not} scanset, which reads all the scanned characters into the string variable (null terminated). 5041 5106 \begin{cfa}[belowskip=0pt] 5042 char s[10];5043 sin | excl( "abc", s );5107 char cs[10]; 5108 sin | excl( "abc", cs ); 5044 5109 \end{cfa} 5045 5110 \begin{cfa}[showspaces=true,aboveskip=0pt,belowskip=0pt] … … 5047 5112 \end{cfa} 5048 5113 5049 \Indexc{quoted}( char delimit, wdi-input-string )\index{manipulator!quoted@©quoted©} 5050 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character. 5051 If the delimit character is omitted, it defaults to ©'\n'© (newline). 5052 5053 \item 5054 \Indexc{getline}( char delimit, wdi-input-string )\index{manipulator!getline@©getline©} 5055 Is an ©excl© with scanset ©"delimit"©, which consumes all characters up to the delimit character. 5056 If the delimit character is omitted, it defaults to ©'\n'© (newline). 5114 \item 5115 \Indexc{ignore}( ©T & v© or ©const char cs[]© or $string\ manipulator$ )\index{manipulator!ignore@©ignore©} 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} 5057 5127 \end{enumerate} 5058 5128 -
libcfa/src/Exception.hfa
r0522ebe ra4da45e 2 2 3 3 // TEMPORARY 4 #define ExceptionDecl( name, fields... ) exception name{ fields }; __attribute__(( cfa_linkonce )) vtable( name ) name ## _vt 5 #define ExceptionInst( name, values... ) (name){ &name ## _vt, values } 4 #define ExceptionDecl( name, fields... ) exception name{ fields }; \ 5 __attribute__(( cfa_linkonce )) vtable( name ) name ## _vt 6 #define ExceptionArgs( name, args... ) &name ## _vt, args 7 #define ExceptionInst( name, args... ) (name){ ExceptionArgs( name, args ) } -
libcfa/src/bits/signal.hfa
r0522ebe ra4da45e 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
r0522ebe ra4da45e 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 14 12:03:47202413 // Update Count : 2 4012 // Last Modified On : Wed Feb 7 21:17:06 2024 13 // Update Count : 259 14 14 // 15 15 … … 210 210 } 211 211 212 void ?|?( ifstream & in, string & s ) { 213 in | (*s.inner); 214 } 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 } // ?|? 215 216 216 217 ifstream & ?|?( ifstream & is, _Istream_Sstr f ) { 218 // _Istream_Rstr f2 = {f.sstr.s.inner, (_Istream_str_base)f.sstr}; 217 219 _Istream_Rstr f2 = {f.s.inner, (_Istream_str_base)f}; 218 220 return is | f2; 219 221 } // ?|? 220 221 void ?|?( ifstream & in, _Istream_Sstr f ) {222 (ifstream &)(in | f);223 }224 222 225 223 //////////////////////////////////////////////////////// -
libcfa/src/collections/string.hfa
r0522ebe ra4da45e 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 14 12:03:46202413 // Update Count : 8112 // Last Modified On : Tue Feb 6 20:59:18 2024 13 // Update Count : 118 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 );83 82 84 83 static inline { … … 96 95 void ?|?( ofstream & os, _Ostream_Manip(string) ); 97 96 97 struct _Istream_Swidth { 98 string & s; 99 inline _Istream_str_base; 100 }; // _Istream_Swidth 101 102 struct _Istream_Squoted { 103 _Istream_Swidth sstr; 104 }; // _Istream_Squoted 105 98 106 struct _Istream_Sstr { 99 107 string & s; 100 108 inline _Istream_str_base; 109 // _Istream_Swidth sstr; 101 110 }; // _Istream_Sstr 102 111 103 112 static inline { 104 113 // read width does not include null terminator 105 _Istream_S str wdi( unsigned int rwd, string & s ) { return (_Istream_Sstr)@{ s, {{0p}, rwd, {.flags.rwd : true}} }; }114 _Istream_Swidth wdi( unsigned int rwd, string & s ) { return (_Istream_Swidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; } 106 115 _Istream_Sstr getline( string & s, const char delimiter = '\n' ) { 107 return (_Istream_Sstr)@{ s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} }; 108 } 109 _Istream_Sstr & getline( _Istream_Sstr & fmt, const char delimiter = '\n' ) { 110 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt; 111 } 112 _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : false}} }; } 113 _Istream_Sstr & incl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; } 114 _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : true}} }; } 115 _Istream_Sstr & excl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; } 116 _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.ignore : true}} }; } 117 _Istream_Sstr & ignore( _Istream_Sstr & fmt ) { fmt.flags.ignore = true; return fmt; } 116 // return (_Istream_Sstr)@{ { .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } }; 117 return (_Istream_Sstr)@{ .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } }; 118 } 119 _Istream_Sstr & getline( _Istream_Swidth & f, const char delimiter = '\n' ) { 120 f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Sstr &)f; 121 } 122 _Istream_Squoted quoted( string & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) { 123 return (_Istream_Squoted)@{ { .s : s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } }; 124 } 125 _Istream_Squoted & quoted( _Istream_Swidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) { 126 f.delimiters[0] = Ldelimiter; f.delimiters[1] = Rdelimiter; f.delimiters[2] = '\0'; 127 return (_Istream_Squoted &)f; 128 } 129 // _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } } }; } 130 _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; } 131 _Istream_Sstr & incl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Sstr &)f; } 132 // _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } } }; } 133 _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; } 134 _Istream_Sstr & excl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Sstr &)f; } 135 // _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; } 136 _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; } 137 _Istream_Sstr & ignore( _Istream_Swidth & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; } 138 _Istream_Squoted & ignore( _Istream_Squoted & f ) { f.sstr.flags.ignore = true; return (_Istream_Squoted &)f; } 139 // _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.sstr.flags.ignore = true; return (_Istream_Sstr &)f; } 140 _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; } 118 141 } // distribution 142 ifstream & ?|?( ifstream & is, _Istream_Squoted f ); 119 143 ifstream & ?|?( ifstream & is, _Istream_Sstr f ); 120 void ?|?( ifstream & is, _Istream_Sstr t ); 144 static inline ifstream & ?|?( ifstream & is, _Istream_Swidth f ) { return is | *(_Istream_Sstr *)&f; } 121 145 122 146 // Concatenation -
libcfa/src/collections/string_res.cfa
r0522ebe ra4da45e 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 22 23:12:42 202413 // Update Count : 4312 // Last Modified On : Sat Feb 10 17:47:22 2024 13 // Update Count : 83 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 void ?|?( ifstream & in, string_res & s ) { 254 (ifstream &)(in | s); 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; 255 276 } 256 277 … … 295 316 } // ?|? 296 317 297 void ?|?( ifstream & in, _Istream_Rstr f ) {298 (ifstream &)(in | f);299 }300 301 302 318 // Empty constructor 303 319 void ?{}(string_res & s) with(s) { 304 if( ambient_string_sharectx->activeHeap ) {305 (Handle){ * ambient_string_sharectx->activeHeap };306 (shareEditSet_owns_ulink){ false };307 verify( Handle.s == 0p && Handle.lnth == 0 );308 } else {309 (Handle){ * new( (size_t) 10 ) }; // TODO: can I lazily avoid allocating for empty string310 (shareEditSet_owns_ulink){ true };311 Handle.s = Handle.ulink->StartVbyte;312 verify( Handle.lnth == 0 );313 }314 s.shareEditSet_prev = &s;315 s.shareEditSet_next = &s;316 }320 if( ambient_string_sharectx->activeHeap ) { 321 (Handle){ * ambient_string_sharectx->activeHeap }; 322 (shareEditSet_owns_ulink){ false }; 323 verify( Handle.s == 0p && Handle.lnth == 0 ); 324 } else { 325 (Handle){ * new( (size_t) 10 ) }; // TODO: can I lazily avoid allocating for empty string 326 (shareEditSet_owns_ulink){ true }; 327 Handle.s = Handle.ulink->StartVbyte; 328 verify( Handle.lnth == 0 ); 329 } 330 s.shareEditSet_prev = &s; 331 s.shareEditSet_next = &s; 332 } 317 333 318 334 static void eagerCopyCtorHelper(string_res & s, const char * rhs, size_t rhslnth) with(s) { 319 if( ambient_string_sharectx->activeHeap ) {320 (Handle){ * ambient_string_sharectx->activeHeap };321 (shareEditSet_owns_ulink){ false };322 } else {323 (Handle){ * new( rhslnth ) };324 (shareEditSet_owns_ulink){ true };325 }326 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);327 Handle.lnth = rhslnth;328 memmove( Handle.s, rhs, rhslnth );329 s.shareEditSet_prev = &s;330 s.shareEditSet_next = &s;335 if( ambient_string_sharectx->activeHeap ) { 336 (Handle){ * ambient_string_sharectx->activeHeap }; 337 (shareEditSet_owns_ulink){ false }; 338 } else { 339 (Handle){ * new( rhslnth ) }; 340 (shareEditSet_owns_ulink){ true }; 341 } 342 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth); 343 Handle.lnth = rhslnth; 344 memmove( Handle.s, rhs, rhslnth ); 345 s.shareEditSet_prev = &s; 346 s.shareEditSet_next = &s; 331 347 } 332 348 333 349 // Constructor from a raw buffer and size 334 350 void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) { 335 eagerCopyCtorHelper(s, rhs, rhslnth);351 eagerCopyCtorHelper(s, rhs, rhslnth); 336 352 } 337 353 338 354 void ?{}( string_res & s, ssize_t rhs ) { 339 char buf[64];340 int len;341 snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );342 ( s ){ buf, len };355 char buf[64]; 356 int len; 357 snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len ); 358 ( s ){ buf, len }; 343 359 } 344 360 void ?{}( string_res & s, size_t rhs ) { 345 char buf[64];346 int len;347 snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );348 ( s ){ buf, len };361 char buf[64]; 362 int len; 363 snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len ); 364 ( s ){ buf, len }; 349 365 } 350 366 void ?{}( string_res & s, double rhs ) { 351 char buf[64];352 int len;353 snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );354 ( s ){ buf, len };367 char buf[64]; 368 int len; 369 snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len ); 370 ( s ){ buf, len }; 355 371 } 356 372 void ?{}( string_res & s, long double rhs ) { 357 char buf[64];358 int len;359 snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );360 ( s ){ buf, len };373 char buf[64]; 374 int len; 375 snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len ); 376 ( s ){ buf, len }; 361 377 } 362 378 void ?{}( string_res & s, double _Complex rhs ) { 363 char buf[64];364 int len;365 snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );366 ( s ){ buf, len };379 char buf[64]; 380 int len; 381 snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len ); 382 ( s ){ buf, len }; 367 383 } 368 384 void ?{}( string_res & s, long double _Complex rhs ) { 369 char buf[64];370 int len;371 snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );372 ( s ){ buf, len };385 char buf[64]; 386 int len; 387 snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len ); 388 ( s ){ buf, len }; 373 389 } 374 390 375 391 // private ctor (not in header): use specified heap (ignore ambient) and copy chars in 376 392 void ?{}( string_res & s, VbyteHeap & heap, const char * rhs, size_t rhslnth ) with(s) { 377 (Handle){ heap };378 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);379 Handle.lnth = rhslnth;380 (s.shareEditSet_owns_ulink){ false };381 memmove( Handle.s, rhs, rhslnth );382 s.shareEditSet_prev = &s;383 s.shareEditSet_next = &s;393 (Handle){ heap }; 394 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth); 395 Handle.lnth = rhslnth; 396 (s.shareEditSet_owns_ulink){ false }; 397 memmove( Handle.s, rhs, rhslnth ); 398 s.shareEditSet_prev = &s; 399 s.shareEditSet_next = &s; 384 400 } 385 401 … … 387 403 // General copy constructor 388 404 void ?{}(string_res & s, const string_res & s2, StrResInitMode mode, size_t start, size_t len ) { 389 390 size_t end = start + len; 391 verify( start <= end && end <= s2.Handle.lnth ); 392 393 if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) { 394 // crossing heaps (including private): copy eagerly 395 eagerCopyCtorHelper(s, s2.Handle.s + start, end - start); 396 verify(s.shareEditSet_prev == &s); 397 verify(s.shareEditSet_next == &s); 398 } else { 399 (s.Handle){}; 400 s.Handle.s = s2.Handle.s + start; 401 s.Handle.lnth = end - start; 402 s.Handle.ulink = s2.Handle.ulink; 403 404 AddThisAfter(s.Handle, s2.Handle ); // insert this handle after rhs handle 405 // ^ bug? skip others at early point in string 406 407 if (mode == COPY_VALUE) { 408 verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap); 409 // requested logical copy in same heap: defer copy until write 410 411 (s.shareEditSet_owns_ulink){ false }; 412 413 // make s alone in its shareEditSet 414 s.shareEditSet_prev = &s; 415 s.shareEditSet_next = &s; 416 } else { 417 verify( mode == SHARE_EDITS ); 418 // sharing edits with source forces same heap as source (ignore context) 419 420 (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink }; 421 422 // s2 is logically const but not implementation const 423 string_res & s2mod = (string_res &) s2; 424 425 // insert s after s2 on shareEditSet 426 s.shareEditSet_next = s2mod.shareEditSet_next; 427 s.shareEditSet_prev = &s2mod; 428 s.shareEditSet_next->shareEditSet_prev = &s; 429 s.shareEditSet_prev->shareEditSet_next = &s; 430 } 431 } 405 size_t end = start + len; 406 verify( start <= end && end <= s2.Handle.lnth ); 407 408 if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) { 409 // crossing heaps (including private): copy eagerly 410 eagerCopyCtorHelper(s, s2.Handle.s + start, end - start); 411 verify(s.shareEditSet_prev == &s); 412 verify(s.shareEditSet_next == &s); 413 } else { 414 (s.Handle){}; 415 s.Handle.s = s2.Handle.s + start; 416 s.Handle.lnth = end - start; 417 s.Handle.ulink = s2.Handle.ulink; 418 419 AddThisAfter(s.Handle, s2.Handle ); // insert this handle after rhs handle 420 // ^ bug? skip others at early point in string 421 422 if (mode == COPY_VALUE) { 423 verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap); 424 // requested logical copy in same heap: defer copy until write 425 426 (s.shareEditSet_owns_ulink){ false }; 427 428 // make s alone in its shareEditSet 429 s.shareEditSet_prev = &s; 430 s.shareEditSet_next = &s; 431 } else { 432 verify( mode == SHARE_EDITS ); 433 // sharing edits with source forces same heap as source (ignore context) 434 435 (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink }; 436 437 // s2 is logically const but not implementation const 438 string_res & s2mod = (string_res &) s2; 439 440 // insert s after s2 on shareEditSet 441 s.shareEditSet_next = s2mod.shareEditSet_next; 442 s.shareEditSet_prev = &s2mod; 443 s.shareEditSet_next->shareEditSet_prev = &s; 444 s.shareEditSet_prev->shareEditSet_next = &s; 445 } 446 } 432 447 } 433 448 434 449 static void assignEditSet(string_res & s, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer, 435 char * resultSesStart,436 size_t resultSesLnth,437 HandleNode * resultPadPosition, size_t bsize ) {438 439 char * beforeBegin = shareEditSetStartPeer->Handle.s;440 size_t beforeLen = s.Handle.s - beforeBegin;441 442 char * afterBegin = s.Handle.s + s.Handle.lnth;443 size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;444 445 size_t oldLnth = s.Handle.lnth;446 447 s.Handle.s = resultSesStart + beforeLen;448 s.Handle.lnth = bsize;449 if (resultPadPosition)450 MoveThisAfter( s.Handle, *resultPadPosition );451 452 // adjust all substring string and handle locations, and check if any substring strings are outside the new base string453 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 edit460 // take start and end as end-anchored461 size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;462 p->Handle.s = limit - startOffsetFromEnd;463 // p->Handle.lnth unaffected464 } else if ( p->Handle.s <= beforeBegin + beforeLen ) {465 // p starts before, or at the start of, the edit466 if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {467 // p ends before the edit468 // take end as start-anchored too469 // p->Handle.lnth unaffected470 } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {471 // p ends during the edit; p does not include the last character replaced472 // clip end of p to end at start of edit473 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );474 } else {475 // p ends after the edit476 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );477 // take end as end-anchored478 // stretch-shrink p according to the edit479 p->Handle.lnth += s.Handle.lnth;480 p->Handle.lnth -= oldLnth;481 }482 // take start as start-anchored483 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 edit488 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 replaced491 // set p to empty string at start of edit492 p->Handle.s = s.Handle.s;493 p->Handle.lnth = 0;494 } else {495 // p includes the end of the edit496 // clip start of p to start at end of edit497 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 position504 }450 char * resultSesStart, 451 size_t resultSesLnth, 452 HandleNode * resultPadPosition, size_t bsize ) { 453 454 char * beforeBegin = shareEditSetStartPeer->Handle.s; 455 size_t beforeLen = s.Handle.s - beforeBegin; 456 457 char * afterBegin = s.Handle.s + s.Handle.lnth; 458 size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin; 459 460 size_t oldLnth = s.Handle.lnth; 461 462 s.Handle.s = resultSesStart + beforeLen; 463 s.Handle.lnth = bsize; 464 if (resultPadPosition) 465 MoveThisAfter( s.Handle, *resultPadPosition ); 466 467 // adjust all substring string and handle locations, and check if any substring strings are outside the new base string 468 char *limit = resultSesStart + resultSesLnth; 469 for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) { 470 verify (p->Handle.s >= beforeBegin); 471 if ( p->Handle.s >= afterBegin ) { 472 verify ( p->Handle.s <= afterBegin + afterLen ); 473 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen ); 474 // p starts after the edit 475 // take start and end as end-anchored 476 size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s; 477 p->Handle.s = limit - startOffsetFromEnd; 478 // p->Handle.lnth unaffected 479 } else if ( p->Handle.s <= beforeBegin + beforeLen ) { 480 // p starts before, or at the start of, the edit 481 if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) { 482 // p ends before the edit 483 // take end as start-anchored too 484 // p->Handle.lnth unaffected 485 } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) { 486 // p ends during the edit; p does not include the last character replaced 487 // clip end of p to end at start of edit 488 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin ); 489 } else { 490 // p ends after the edit 491 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen ); 492 // take end as end-anchored 493 // stretch-shrink p according to the edit 494 p->Handle.lnth += s.Handle.lnth; 495 p->Handle.lnth -= oldLnth; 496 } 497 // take start as start-anchored 498 size_t startOffsetFromStart = p->Handle.s - beforeBegin; 499 p->Handle.s = resultSesStart + startOffsetFromStart; 500 } else { 501 verify ( p->Handle.s < afterBegin ); 502 // p starts during the edit 503 verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen ); 504 if ( p->Handle.s + p->Handle.lnth < afterBegin ) { 505 // p ends during the edit; p does not include the last character replaced 506 // set p to empty string at start of edit 507 p->Handle.s = s.Handle.s; 508 p->Handle.lnth = 0; 509 } else { 510 // p includes the end of the edit 511 // clip start of p to start at end of edit 512 int charsToClip = afterBegin - p->Handle.s; 513 p->Handle.s = s.Handle.s + s.Handle.lnth; 514 p->Handle.lnth -= charsToClip; 515 } 516 } 517 if (resultPadPosition) 518 MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position 519 } 505 520 } 506 521 507 522 // traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs 508 523 static void locateInShareEditSet( string_res & s, string_res *& shareEditSetStartPeer, string_res *& shareEditSetEndPeer ) { 509 shareEditSetStartPeer = & s;510 shareEditSetEndPeer = & s;511 for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {512 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {513 shareEditSetStartPeer = editPeer;514 }515 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {516 shareEditSetEndPeer = editPeer;517 }518 }524 shareEditSetStartPeer = & s; 525 shareEditSetEndPeer = & s; 526 for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) { 527 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) { 528 shareEditSetStartPeer = editPeer; 529 } 530 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) { 531 shareEditSetEndPeer = editPeer; 532 } 533 } 519 534 } 520 535 521 536 static string_res & assign_(string_res & s, const char * buffer, size_t bsize, const string_res & valSrc) { 522 523 string_res * shareEditSetStartPeer; 524 string_res * shareEditSetEndPeer; 525 locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer ); 526 527 verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s ); 528 size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s; 529 verify( origEditSetLength >= s.Handle.lnth ); 530 531 if ( s.shareEditSet_owns_ulink ) { // assigning to private context 532 // ok to overwrite old value within LHS 533 char * prefixStartOrig = shareEditSetStartPeer->Handle.s; 534 int prefixLen = s.Handle.s - prefixStartOrig; 535 char * suffixStartOrig = s.Handle.s + s.Handle.lnth; 536 int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig; 537 538 int delta = bsize - s.Handle.lnth; 539 if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) { 540 // growing: copy from old to new 541 char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta ); 542 char *destCursor = dest; memcpy(destCursor, prefixStartOrig, prefixLen); 543 destCursor += prefixLen; memcpy(destCursor, buffer , bsize ); 544 destCursor += bsize; memcpy(destCursor, suffixStartOrig, suffixLen); 545 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 546 dest, 547 origEditSetLength + delta, 548 0p, bsize); 549 free( oldBytes ); 550 } else { 551 // room is already allocated in-place: bubble suffix and overwite middle 552 memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen ); 553 memcpy( s.Handle.s, buffer, bsize ); 554 555 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 556 shareEditSetStartPeer->Handle.s, 557 origEditSetLength + delta, 558 0p, bsize); 559 } 560 561 } else if ( // assigning to shared context 562 s.Handle.lnth == origEditSetLength && // overwriting entire run of SES 563 & valSrc && // sourcing from a managed string 564 valSrc.Handle.ulink == s.Handle.ulink ) { // sourcing from same heap 565 566 // SES's result will only use characters from the source string => reuse source 567 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 568 valSrc.Handle.s, 569 valSrc.Handle.lnth, 570 &((string_res&)valSrc).Handle, bsize); 571 572 } else { 573 // overwriting a proper substring of some string: mash characters from old and new together (copy on write) 574 // OR we are importing characters: need to copy eagerly (can't refer to source) 575 576 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer 577 // `s` occurs in the middle of it, to be replaced 578 // build up the new text in `pasting` 579 580 string_res pasting = { 581 * s.Handle.ulink, // maintain same heap, regardless of context 582 shareEditSetStartPeer->Handle.s, // start of SES 583 s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s 584 append( pasting, 585 buffer, // start of replacement for s 586 bsize ); // length of replacement for s 587 append( pasting, 588 s.Handle.s + s.Handle.lnth, // start of SES after s 589 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - 590 (s.Handle.s + s.Handle.lnth) ); // length of SES, after s 591 592 // The above string building can trigger compaction. 593 // The reference points (that are arguments of the string building) may move during that building. 594 // From s point on, they are stable. 595 596 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 597 pasting.Handle.s, 598 pasting.Handle.lnth, 599 &pasting.Handle, bsize); 600 } 601 602 return s; 537 string_res * shareEditSetStartPeer; 538 string_res * shareEditSetEndPeer; 539 locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer ); 540 541 verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s ); 542 size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s; 543 verify( origEditSetLength >= s.Handle.lnth ); 544 545 if ( s.shareEditSet_owns_ulink ) { // assigning to private context 546 // ok to overwrite old value within LHS 547 char * prefixStartOrig = shareEditSetStartPeer->Handle.s; 548 int prefixLen = s.Handle.s - prefixStartOrig; 549 char * suffixStartOrig = s.Handle.s + s.Handle.lnth; 550 int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig; 551 552 int delta = bsize - s.Handle.lnth; 553 if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) { 554 // growing: copy from old to new 555 char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta ); 556 char *destCursor = dest; memcpy(destCursor, prefixStartOrig, prefixLen); 557 destCursor += prefixLen; memcpy(destCursor, buffer , bsize ); 558 destCursor += bsize; memcpy(destCursor, suffixStartOrig, suffixLen); 559 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 560 dest, 561 origEditSetLength + delta, 562 0p, bsize); 563 free( oldBytes ); 564 } else { 565 // room is already allocated in-place: bubble suffix and overwite middle 566 memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen ); 567 memcpy( s.Handle.s, buffer, bsize ); 568 569 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 570 shareEditSetStartPeer->Handle.s, 571 origEditSetLength + delta, 572 0p, bsize); 573 } 574 575 } else if ( // assigning to shared context 576 s.Handle.lnth == origEditSetLength && // overwriting entire run of SES 577 & valSrc && // sourcing from a managed string 578 valSrc.Handle.ulink == s.Handle.ulink ) { // sourcing from same heap 579 580 // SES's result will only use characters from the source string => reuse source 581 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 582 valSrc.Handle.s, 583 valSrc.Handle.lnth, 584 &((string_res&)valSrc).Handle, bsize); 585 586 } else { 587 // overwriting a proper substring of some string: mash characters from old and new together (copy on write) 588 // OR we are importing characters: need to copy eagerly (can't refer to source) 589 590 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer 591 // `s` occurs in the middle of it, to be replaced 592 // build up the new text in `pasting` 593 594 string_res pasting = { 595 * s.Handle.ulink, // maintain same heap, regardless of context 596 shareEditSetStartPeer->Handle.s, // start of SES 597 s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s 598 append( pasting, 599 buffer, // start of replacement for s 600 bsize ); // length of replacement for s 601 append( pasting, 602 s.Handle.s + s.Handle.lnth, // start of SES after s 603 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - 604 (s.Handle.s + s.Handle.lnth) ); // length of SES, after s 605 606 // The above string building can trigger compaction. 607 // The reference points (that are arguments of the string building) may move during that building. 608 // From s point on, they are stable. 609 610 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer, 611 pasting.Handle.s, 612 pasting.Handle.lnth, 613 &pasting.Handle, bsize); 614 } 615 616 return s; 603 617 } 604 618 605 619 string_res & assign(string_res & s, const string_res & src, size_t maxlen) { 606 return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);620 return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p); 607 621 } 608 622 609 623 string_res & assign(string_res & s, const char * buffer, size_t bsize) { 610 return assign_(s, buffer, bsize, *0p);624 return assign_(s, buffer, bsize, *0p); 611 625 } 612 626 613 627 string_res & ?=?(string_res & s, char c) { 614 return assign(s, &c, 1);628 return assign(s, &c, 1); 615 629 } 616 630 617 631 string_res & ?=?( string_res & s, ssize_t rhs ) { 618 string_res rhs2 = rhs;619 s = rhs2;620 return s;632 string_res rhs2 = rhs; 633 s = rhs2; 634 return s; 621 635 } 622 636 string_res & ?=?( string_res & s, size_t rhs ) { 623 string_res rhs2 = rhs;624 s = rhs2;625 return s;637 string_res rhs2 = rhs; 638 s = rhs2; 639 return s; 626 640 } 627 641 string_res & ?=?( string_res & s, double rhs ) { 628 string_res rhs2 = rhs;629 s = rhs2;630 return s;642 string_res rhs2 = rhs; 643 s = rhs2; 644 return s; 631 645 } 632 646 string_res & ?=?( string_res & s, long double rhs ) { 633 string_res rhs2 = rhs;634 s = rhs2;635 return s;647 string_res rhs2 = rhs; 648 s = rhs2; 649 return s; 636 650 } 637 651 string_res & ?=?( string_res & s, double _Complex rhs ) { 638 string_res rhs2 = rhs;639 s = rhs2;640 return s;652 string_res rhs2 = rhs; 653 s = rhs2; 654 return s; 641 655 } 642 656 string_res & ?=?( string_res & s, long double _Complex rhs ) { 643 string_res rhs2 = rhs;644 s = rhs2;645 return s;657 string_res rhs2 = rhs; 658 s = rhs2; 659 return s; 646 660 } 647 661 648 662 // Copy assignment operator 649 663 string_res & ?=?(string_res & s, const string_res & rhs) with( s ) { 650 return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);664 return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs); 651 665 } 652 666 653 667 string_res & ?=?(string_res & s, string_res & rhs) with( s ) { 654 const string_res & rhs2 = rhs;655 return s = rhs2;668 const string_res & rhs2 = rhs; 669 return s = rhs2; 656 670 } 657 671 … … 659 673 // Destructor 660 674 void ^?{}(string_res & s) with(s) { 661 // much delegated to implied ^VbyteSM662 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 out670 delete( s.Handle.ulink );671 }675 // much delegated to implied ^VbyteSM 676 677 // sever s from its share-edit peers, if any (four no-ops when already solo) 678 s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next; 679 s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev; 680 // s.shareEditSet_next = &s; 681 // s.shareEditSet_prev = &s; 682 683 if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out 684 delete( s.Handle.ulink ); 685 } 672 686 } 673 687 … … 677 691 // offset from the start of the string. 678 692 char ?[?](const string_res & s, size_t index) with(s) { 679 //TODO: Check if index is valid (no exceptions yet)680 return Handle.s[index];693 //TODO: Check if index is valid (no exceptions yet) 694 return Handle.s[index]; 681 695 } 682 696 683 697 void assignAt(const string_res & s, size_t index, char val) { 684 // caution: not tested (not reachable by string-api-coverage interface)685 // equivalent form at string level is `s[index] = val`,686 // which uses the overload that returns a length-1 string687 string_res editZone = { s, SHARE_EDITS, index, 1 };688 assign(editZone, &val, 1);698 // caution: not tested (not reachable by string-api-coverage interface) 699 // equivalent form at string level is `s[index] = val`, 700 // which uses the overload that returns a length-1 string 701 string_res editZone = { s, SHARE_EDITS, index, 1 }; 702 assign(editZone, &val, 1); 689 703 } 690 704 … … 694 708 695 709 void append(string_res & str1, const char * buffer, size_t bsize) { 696 size_t clnth = str1.Handle.lnth + bsize;697 if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?698 // no-op699 } else {// must copy some text700 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 area702 } else {// copy the two parts703 char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );704 char * str1oldBuf = str1.Handle.s;// must read after VbyteAlloc call in case it gs's705 str1.Handle.s = str1newBuf;706 memcpy( str1.Handle.s, str1oldBuf, str1.Handle.lnth );707 } // if708 memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );709 } // if710 str1.Handle.lnth = clnth;710 size_t clnth = str1.Handle.lnth + bsize; 711 if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ? 712 // no-op 713 } else { // must copy some text 714 if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ? 715 VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area 716 } else { // copy the two parts 717 char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth ); 718 char * str1oldBuf = str1.Handle.s; // must read after VbyteAlloc call in case it gs's 719 str1.Handle.s = str1newBuf; 720 memcpy( str1.Handle.s, str1oldBuf, str1.Handle.lnth ); 721 } // if 722 memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize ); 723 } // if 724 str1.Handle.lnth = clnth; 711 725 } 712 726 713 727 void ?+=?(string_res & str1, const string_res & str2) { 714 append( str1, str2.Handle.s, str2.Handle.lnth );728 append( str1, str2.Handle.s, str2.Handle.lnth ); 715 729 } 716 730 717 731 void append(string_res & str1, const string_res & str2, size_t maxlen) { 718 append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );732 append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) ); 719 733 } 720 734 721 735 void ?+=?(string_res & s, char c) { 722 append( s, & c, 1 );736 append( s, & c, 1 ); 723 737 } 724 738 void ?+=?(string_res & s, const char * c) { 725 append( s, c, strlen(c) );739 append( s, c, strlen(c) ); 726 740 } 727 741 … … 730 744 731 745 void ?*=?(string_res & s, size_t factor) { 732 string_res s2 = { s, COPY_VALUE };733 s = "";734 for (factor) s += s2;746 string_res s2 = { s, COPY_VALUE }; 747 s = ""; 748 for (factor) s += s2; 735 749 } 736 750 … … 739 753 740 754 int strcmp(const string_res & s1, const string_res & s2) { 741 // return 0;742 int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));743 if (ans1 != 0) return ans1;744 return s1.Handle.lnth - s2.Handle.lnth;755 // return 0; 756 int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth)); 757 if (ans1 != 0) return ans1; 758 return s1.Handle.lnth - s2.Handle.lnth; 745 759 } 746 760 … … 753 767 754 768 int strcmp (const string_res & s1, const char * s2) { 755 string_res s2x = s2;756 return strcmp(s1, s2x);769 string_res s2x = s2; 770 return strcmp(s1, s2x); 757 771 } 758 772 … … 765 779 766 780 int strcmp (const char * s1, const string_res & s2) { 767 string_res s1x = s1;768 return strcmp(s1x, s2);781 string_res s1x = s1; 782 return strcmp(s1x, s2); 769 783 } 770 784 … … 777 791 778 792 779 780 793 ////////////////////////////////////////////////////////// 781 794 // Search 782 795 783 796 bool contains(const string_res & s, char ch) { 784 for ( i; size(s) ) {785 if (s[i] == ch) return true;786 }787 return false;797 for ( i; size(s) ) { 798 if (s[i] == ch) return true; 799 } 800 return false; 788 801 } 789 802 790 803 int find(const string_res & s, char search) { 791 return findFrom(s, 0, search);804 return findFrom(s, 0, search); 792 805 } 793 806 794 807 int findFrom(const string_res & s, size_t fromPos, char search) { 795 // FIXME: This paricular overload (find of single char) is optimized to use memchr.796 // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.797 char * searchFrom = s.Handle.s + fromPos;798 size_t searchLnth = s.Handle.lnth - fromPos;799 int searchVal = search;800 char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);801 if (foundAt == 0p) return s.Handle.lnth;802 else return foundAt - s.Handle.s;808 // FIXME: This paricular overload (find of single char) is optimized to use memchr. 809 // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match. 810 char * searchFrom = s.Handle.s + fromPos; 811 size_t searchLnth = s.Handle.lnth - fromPos; 812 int searchVal = search; 813 char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth); 814 if (foundAt == 0p) return s.Handle.lnth; 815 else return foundAt - s.Handle.s; 803 816 } 804 817 805 818 int find(const string_res & s, const string_res & search) { 806 return findFrom(s, 0, search);819 return findFrom(s, 0, search); 807 820 } 808 821 809 822 int findFrom(const string_res & s, size_t fromPos, const string_res & search) { 810 return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);823 return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth); 811 824 } 812 825 813 826 int find(const string_res & s, const char * search) { 814 return findFrom(s, 0, search);827 return findFrom(s, 0, search); 815 828 } 816 829 int findFrom(const string_res & s, size_t fromPos, const char * search) { 817 return findFrom(s, fromPos, search, strlen(search));830 return findFrom(s, fromPos, search, strlen(search)); 818 831 } 819 832 820 833 int find(const string_res & s, const char * search, size_t searchsize) { 821 return findFrom(s, 0, search, searchsize);834 return findFrom(s, 0, search, searchsize); 822 835 } 823 836 824 837 int findFrom(const string_res & s, size_t fromPos, const char * search, size_t searchsize) { 825 826 /* Remaining implementations essentially ported from Sunjay's work */ 827 828 829 // FIXME: This is a naive algorithm. We probably want to switch to someting 830 // like Boyer-Moore in the future. 831 // https://en.wikipedia.org/wiki/String_searching_algorithm 832 833 // Always find the empty string 834 if (searchsize == 0) { 835 return 0; 836 } 837 838 for ( i; fromPos ~ s.Handle.lnth ) { 839 size_t remaining = s.Handle.lnth - i; 840 // Never going to find the search string if the remaining string is 841 // smaller than search 842 if (remaining < searchsize) { 843 break; 844 } 845 846 bool matched = true; 847 for ( j; searchsize ) { 848 if (search[j] != s.Handle.s[i + j]) { 849 matched = false; 850 break; 851 } 852 } 853 if (matched) { 854 return i; 855 } 856 } 857 858 return s.Handle.lnth; 838 /* Remaining implementations essentially ported from Sunjay's work */ 839 840 // FIXME: This is a naive algorithm. We probably want to switch to someting 841 // like Boyer-Moore in the future. 842 // https://en.wikipedia.org/wiki/String_searching_algorithm 843 844 // Always find the empty string 845 if (searchsize == 0) { 846 return 0; 847 } 848 849 for ( i; fromPos ~ s.Handle.lnth ) { 850 size_t remaining = s.Handle.lnth - i; 851 // Never going to find the search string if the remaining string is 852 // smaller than search 853 if (remaining < searchsize) { 854 break; 855 } 856 857 bool matched = true; 858 for ( j; searchsize ) { 859 if (search[j] != s.Handle.s[i + j]) { 860 matched = false; 861 break; 862 } 863 } 864 if (matched) { 865 return i; 866 } 867 } 868 return s.Handle.lnth; 859 869 } 860 870 861 871 bool includes(const string_res & s, const string_res & search) { 862 return includes(s, search.Handle.s, search.Handle.lnth);872 return includes(s, search.Handle.s, search.Handle.lnth); 863 873 } 864 874 865 875 bool includes(const string_res & s, const char * search) { 866 return includes(s, search, strlen(search));876 return includes(s, search, strlen(search)); 867 877 } 868 878 869 879 bool includes(const string_res & s, const char * search, size_t searchsize) { 870 return find(s, search, searchsize) < s.Handle.lnth;880 return find(s, search, searchsize) < s.Handle.lnth; 871 881 } 872 882 873 883 bool startsWith(const string_res & s, const string_res & prefix) { 874 return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);884 return startsWith(s, prefix.Handle.s, prefix.Handle.lnth); 875 885 } 876 886 877 887 bool startsWith(const string_res & s, const char * prefix) { 878 return startsWith(s, prefix, strlen(prefix));888 return startsWith(s, prefix, strlen(prefix)); 879 889 } 880 890 881 891 bool startsWith(const string_res & s, const char * prefix, size_t prefixsize) { 882 if (s.Handle.lnth < prefixsize) {883 return false;884 }885 return memcmp(s.Handle.s, prefix, prefixsize) == 0;892 if (s.Handle.lnth < prefixsize) { 893 return false; 894 } 895 return memcmp(s.Handle.s, prefix, prefixsize) == 0; 886 896 } 887 897 888 898 bool endsWith(const string_res & s, const string_res & suffix) { 889 return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);899 return endsWith(s, suffix.Handle.s, suffix.Handle.lnth); 890 900 } 891 901 892 902 bool endsWith(const string_res & s, const char * suffix) { 893 return endsWith(s, suffix, strlen(suffix));903 return endsWith(s, suffix, strlen(suffix)); 894 904 } 895 905 896 906 bool endsWith(const string_res & s, const char * suffix, size_t suffixsize) { 897 if (s.Handle.lnth < suffixsize) { 898 return false; 899 } 900 // Amount to offset the bytes pointer so that we are comparing the end of s 901 // to suffix. s.bytes + offset should be the first byte to compare against suffix 902 size_t offset = s.Handle.lnth - suffixsize; 903 return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0; 904 } 905 906 /* Back to Mike's work */ 907 907 if (s.Handle.lnth < suffixsize) { 908 return false; 909 } 910 // Amount to offset the bytes pointer so that we are comparing the end of s 911 // to suffix. s.bytes + offset should be the first byte to compare against suffix 912 size_t offset = s.Handle.lnth - suffixsize; 913 return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0; 914 } 915 916 /* Back to Mike's work */ 908 917 909 918 /////////////////////////////////////////////////////////////////////////// … … 911 920 912 921 void ?{}( charclass_res & s, const string_res & chars) { 913 (s){ chars.Handle.s, chars.Handle.lnth };922 (s){ chars.Handle.s, chars.Handle.lnth }; 914 923 } 915 924 916 925 void ?{}( charclass_res & s, const char * chars ) { 917 (s){ chars, strlen(chars) };926 (s){ chars, strlen(chars) }; 918 927 } 919 928 920 929 void ?{}( charclass_res & s, const char * chars, size_t charssize ) { 921 (s.chars){ chars, charssize };922 // now sort it ?930 (s.chars){ chars, charssize }; 931 // now sort it ? 923 932 } 924 933 925 934 void ^?{}( charclass_res & s ) { 926 ^(s.chars){};935 ^(s.chars){}; 927 936 } 928 937 929 938 static bool test( const charclass_res & mask, char c ) { 930 // instead, use sorted char list?931 return contains( mask.chars, c );939 // instead, use sorted char list? 940 return contains( mask.chars, c ); 932 941 } 933 942 934 943 int exclude(const string_res & s, const charclass_res & mask) { 935 for ( i; size(s) ) {936 if ( test(mask, s[i]) ) return i;937 }938 return size(s);944 for ( i; size(s) ) { 945 if ( test(mask, s[i]) ) return i; 946 } 947 return size(s); 939 948 } 940 949 941 950 int include(const string_res & s, const charclass_res & mask) { 942 for ( i; size(s) ) {943 if ( ! test(mask, s[i]) ) return i;944 }945 return size(s);951 for ( i; size(s) ) { 952 if ( ! test(mask, s[i]) ) return i; 953 } 954 return size(s); 946 955 } 947 956 … … 953 962 static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) { 954 963 #ifdef VbyteDebug 955 serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;956 #endif // VbyteDebug 957 // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be).958 verify( n.ulink != 0p );959 verify( s.ulink == n.ulink );960 flink = n.flink;961 blink = &n;962 n.flink->blink = &s;963 n.flink = &s;964 #ifdef VbyteDebug 965 {964 serr | "enter:AddThisAfter, s:" | &s | " n:" | &n; 965 #endif // VbyteDebug 966 // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be). 967 verify( n.ulink != 0p ); 968 verify( s.ulink == n.ulink ); 969 flink = n.flink; 970 blink = &n; 971 n.flink->blink = &s; 972 n.flink = &s; 973 #ifdef VbyteDebug 974 { 966 975 serr | "HandleList:"; 967 976 serr | nlOff; … … 974 983 } // for 975 984 serr | nlOn; 976 }977 serr | "exit:AddThisAfter";985 } 986 serr | "exit:AddThisAfter"; 978 987 #endif // VbyteDebug 979 988 } // AddThisAfter … … 984 993 static void DeleteNode( HandleNode & s ) with(s) { 985 994 #ifdef VbyteDebug 986 serr | "enter:DeleteNode, s:" | &s;987 #endif // VbyteDebug 988 flink->blink = blink;989 blink->flink = flink;990 #ifdef VbyteDebug 991 serr | "exit:DeleteNode";995 serr | "enter:DeleteNode, s:" | &s; 996 #endif // VbyteDebug 997 flink->blink = blink; 998 blink->flink = flink; 999 #ifdef VbyteDebug 1000 serr | "exit:DeleteNode"; 992 1001 #endif // VbyteDebug 993 1002 } // DeleteNode … … 999 1008 static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) { 1000 1009 #ifdef VbyteDebug 1001 serr | "enter:VbyteAlloc, size:" | size;1002 #endif // VbyteDebug 1003 uintptr_t NoBytes;1004 char *r;1005 1006 NoBytes = ( uintptr_t )EndVbyte + size;1007 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ?1010 serr | "enter:VbyteAlloc, size:" | size; 1011 #endif // VbyteDebug 1012 uintptr_t NoBytes; 1013 char *r; 1014 1015 NoBytes = ( uintptr_t )EndVbyte + size; 1016 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ? 1008 1017 garbage( s, size ); // firer up the garbage collector 1009 1018 verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte && "garbage run did not free up required space" ); 1010 } // if1011 r = EndVbyte;1012 EndVbyte += size;1013 #ifdef VbyteDebug 1014 serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;1015 #endif // VbyteDebug 1016 return r;1019 } // if 1020 r = EndVbyte; 1021 EndVbyte += size; 1022 #ifdef VbyteDebug 1023 serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte; 1024 #endif // VbyteDebug 1025 return r; 1017 1026 } // VbyteAlloc 1018 1027 … … 1027 1036 1028 1037 static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) { 1029 if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {1030 // room available1031 EndVbyte += delta;1032 return 0p;1033 }1034 1035 char *oldBytes = StartVbyte;1036 1037 NoOfExtensions += 1;1038 CurrSize *= 2;1039 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);1040 ExtVbyte = StartVbyte + CurrSize;1041 1042 return oldBytes;1038 if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) { 1039 // room available 1040 EndVbyte += delta; 1041 return 0p; 1042 } 1043 1044 char *oldBytes = StartVbyte; 1045 1046 NoOfExtensions += 1; 1047 CurrSize *= 2; 1048 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize); 1049 ExtVbyte = StartVbyte + CurrSize; 1050 1051 return oldBytes; 1043 1052 } 1044 1053 … … 1049 1058 static void MoveThisAfter( HandleNode & s, const HandleNode & h ) with(s) { 1050 1059 #ifdef VbyteDebug 1051 serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;1052 #endif // VbyteDebug 1053 verify( h.ulink != 0p );1054 verify( s.ulink == h.ulink );1055 if ( s < h.s ) { // check argument values1060 serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h; 1061 #endif // VbyteDebug 1062 verify( h.ulink != 0p ); 1063 verify( s.ulink == h.ulink ); 1064 if ( s < h.s ) { // check argument values 1056 1065 // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:" 1057 // | ( h->s ) | " and keep handles in ascending order";1066 // | ( h->s ) | " and keep handles in ascending order"; 1058 1067 // exit(-1 ); 1059 1068 verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order"); 1060 } // if1061 1062 HandleNode *i;1063 for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h1064 if ( & s != i->blink ) {1069 } // if 1070 1071 HandleNode *i; 1072 for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h 1073 if ( & s != i->blink ) { 1065 1074 DeleteNode( s ); 1066 1075 AddThisAfter( s, *i->blink ); 1067 } // if1068 #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 } // for1077 serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;1078 } // for1079 serr | nlOn;1080 }1081 serr | "exit:MoveThisAfter";1076 } // if 1077 #ifdef VbyteDebug 1078 { 1079 serr | "HandleList:"; 1080 serr | nlOff; 1081 for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) { 1082 serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\""; 1083 for ( i; n->lnth ) { 1084 serr | n->s[i]; 1085 } // for 1086 serr | "\" flink:" | n->flink | " blink:" | n->blink | nl; 1087 } // for 1088 serr | nlOn; 1089 } 1090 serr | "exit:MoveThisAfter"; 1082 1091 #endif // VbyteDebug 1083 1092 } // MoveThisAfter 1084 1085 1086 1087 1093 1088 1094 … … 1097 1103 int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) { 1098 1104 #ifdef VbyteDebug 1099 serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth; 1100 #endif // VbyteDebug 1101 int cmp; 1102 1103 CharZip: for ( int i = 0; ; i += 1 ) { 1104 if ( i == Src2Lnth - 1 ) { 1105 for ( ; ; i += 1 ) { 1105 serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth; 1106 #endif // VbyteDebug 1107 int cmp; 1108 1109 CharZip: for ( int i = 0; ; i += 1 ) { 1110 if ( i == Src2Lnth - 1 ) { 1111 for ( ; ; i += 1 ) { 1112 if ( i == Src1Lnth - 1 ) { 1113 cmp = 0; 1114 break CharZip; 1115 } // exit 1116 if ( Src1[Src1Start + i] != ' ') { 1117 // SUSPECTED BUG: this could be be why Peter got the bug report about == " " (why is this case here at all?) 1118 cmp = 1; 1119 break CharZip; 1120 } // exit 1121 } // for 1122 } // exit 1106 1123 if ( i == Src1Lnth - 1 ) { 1107 cmp = 0; 1108 break CharZip; 1124 for ( ; ; i += 1 ) { 1125 if ( i == Src2Lnth - 1 ) { 1126 cmp = 0; 1127 break CharZip; 1128 } // exit 1129 if ( Src2[Src2Start + i] != ' ') { 1130 cmp = -1; 1131 break CharZip; 1132 } // exit 1133 } // for 1109 1134 } // exit 1110 if ( Src1[Src1Start + i] != ' ') { 1111 // SUSPECTED BUG: this could be be why Peter got the bug report about == " " (why is this case here at all?) 1112 cmp = 1; 1113 break CharZip; 1135 if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) { 1136 cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1; 1137 break CharZip; 1114 1138 } // exit 1115 } // for 1116 } // exit 1117 if ( i == Src1Lnth - 1 ) { 1118 for ( ; ; i += 1 ) { 1119 if ( i == Src2Lnth - 1 ) { 1120 cmp = 0; 1121 break CharZip; 1122 } // exit 1123 if ( Src2[Src2Start + i] != ' ') { 1124 cmp = -1; 1125 break CharZip; 1126 } // exit 1127 } // for 1128 } // exit 1129 if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) { 1130 cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1; 1131 break CharZip; 1132 } // exit 1133 } // for 1134 #ifdef VbyteDebug 1135 serr | "exit:ByteCmp, cmp:" | cmp; 1136 #endif // VbyteDebug 1137 return cmp; 1139 } // for 1140 #ifdef VbyteDebug 1141 serr | "exit:ByteCmp, cmp:" | cmp; 1142 #endif // VbyteDebug 1143 return cmp; 1138 1144 } // ByteCmp 1139 1145 … … 1145 1151 1146 1152 void compaction(VbyteHeap & s) with(s) { 1147 HandleNode *h;1148 char *obase, *nbase, *limit;1149 1150 NoOfCompactions += 1;1151 EndVbyte = StartVbyte;1152 h = Header.flink; // ignore header node1153 for () {1153 HandleNode *h; 1154 char *obase, *nbase, *limit; 1155 1156 NoOfCompactions += 1; 1157 EndVbyte = StartVbyte; 1158 h = Header.flink; // ignore header node 1159 for () { 1154 1160 memmove( EndVbyte, h->s, h->lnth ); 1155 1161 obase = h->s; … … 1169 1175 } // for 1170 1176 if ( h == &Header ) break; // end of header list ? 1171 } // for1177 } // for 1172 1178 } // compaction 1173 1179 1174 1180 1175 1181 static double heap_expansion_freespace_threshold = 0.1; // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage) 1176 // probably an unreasonable default, but need to assess early-round tests on changing it1182 // probably an unreasonable default, but need to assess early-round tests on changing it 1177 1183 1178 1184 void TUNING_set_string_heap_liveness_threshold( double val ) { 1179 heap_expansion_freespace_threshold = 1.0 - val;1185 heap_expansion_freespace_threshold = 1.0 - val; 1180 1186 } 1181 1187 … … 1186 1192 void garbage(VbyteHeap & s, int minreq ) with(s) { 1187 1193 #ifdef VbyteDebug 1188 serr | "enter:garbage";1189 {1194 serr | "enter:garbage"; 1195 { 1190 1196 serr | "HandleList:"; 1191 1197 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) { … … 1198 1204 serr | "\" flink:" | n->flink | " blink:" | n->blink; 1199 1205 } // for 1200 }1201 #endif // VbyteDebug 1202 int AmountUsed, AmountFree;1203 1204 AmountUsed = 0;1205 for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used1206 } 1207 #endif // VbyteDebug 1208 int AmountUsed, AmountFree; 1209 1210 AmountUsed = 0; 1211 for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used 1206 1212 AmountUsed += i->lnth; 1207 } // for1208 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 request1213 } // for 1214 AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed; 1215 1216 if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) { // free space less than threshold or not enough to serve cur request 1211 1217 1212 1218 extend( s, max( CurrSize, minreq ) ); // extend the heap 1213 1219 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 memory1217 1218 // `extend` implies a `compaction` during the copy1219 1220 } else {1221 compaction(s); // in-place1222 }// if1223 #ifdef VbyteDebug 1224 {1220 // Peter says, "This needs work before it should be used." 1221 // } else if ( AmountFree > CurrSize / 2 ) { // free space greater than 3 times the initial allocation ? 1222 // reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory 1223 1224 // `extend` implies a `compaction` during the copy 1225 1226 } else { 1227 compaction(s); // in-place 1228 }// if 1229 #ifdef VbyteDebug 1230 { 1225 1231 serr | "HandleList:"; 1226 1232 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) { … … 1233 1239 serr | "\" flink:" | n->flink | " blink:" | n->blink; 1234 1240 } // for 1235 }1236 serr | "exit:garbage";1241 } 1242 serr | "exit:garbage"; 1237 1243 #endif // VbyteDebug 1238 1244 } // garbage … … 1247 1253 void extend( VbyteHeap & s, int size ) with (s) { 1248 1254 #ifdef VbyteDebug 1249 serr | "enter:extend, size:" | size;1250 #endif // VbyteDebug 1251 char *OldStartVbyte;1252 1253 NoOfExtensions += 1;1254 OldStartVbyte = StartVbyte; // save previous byte area1255 1256 CurrSize += size > InitSize ? size : InitSize; // minimum extension, initial size1257 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);1258 ExtVbyte = (void *)( StartVbyte + CurrSize );1259 compaction(s); // copy from old heap to new & adjust pointers to new heap1260 free( OldStartVbyte ); // release old heap1261 #ifdef VbyteDebug 1262 serr | "exit:extend, CurrSize:" | CurrSize;1255 serr | "enter:extend, size:" | size; 1256 #endif // VbyteDebug 1257 char *OldStartVbyte; 1258 1259 NoOfExtensions += 1; 1260 OldStartVbyte = StartVbyte; // save previous byte area 1261 1262 CurrSize += size > InitSize ? size : InitSize; // minimum extension, initial size 1263 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize); 1264 ExtVbyte = (void *)( StartVbyte + CurrSize ); 1265 compaction(s); // copy from old heap to new & adjust pointers to new heap 1266 free( OldStartVbyte ); // release old heap 1267 #ifdef VbyteDebug 1268 serr | "exit:extend, CurrSize:" | CurrSize; 1263 1269 #endif // VbyteDebug 1264 1270 } // extend … … 1272 1278 void VbyteHeap::reduce( int size ) { 1273 1279 #ifdef VbyteDebug 1274 serr | "enter:reduce, size:" | size;1275 #endif // VbyteDebug 1276 char *OldStartVbyte;1277 1278 NoOfReductions += 1;1279 OldStartVbyte = StartVbyte; // save previous byte area1280 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 heap1285 delete OldStartVbyte; // release old heap1286 #ifdef VbyteDebug 1287 serr | "exit:reduce, CurrSize:" | CurrSize;1280 serr | "enter:reduce, size:" | size; 1281 #endif // VbyteDebug 1282 char *OldStartVbyte; 1283 1284 NoOfReductions += 1; 1285 OldStartVbyte = StartVbyte; // save previous byte area 1286 1287 CurrSize -= size; 1288 StartVbyte = EndVbyte = new char[CurrSize]; 1289 ExtVbyte = (void *)( StartVbyte + CurrSize ); 1290 compaction(); // copy from old heap to new & adjust pointers to new heap 1291 delete OldStartVbyte; // release old heap 1292 #ifdef VbyteDebug 1293 !serr | "exit:reduce, CurrSize:" | CurrSize; 1288 1294 #endif // VbyteDebug 1289 1295 } // reduce -
libcfa/src/collections/string_res.hfa
r0522ebe ra4da45e 10 10 // Created On : Fri Sep 03 11:00:00 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jan 4 11:28:06202413 // Update Count : 2712 // Last Modified On : Wed Feb 7 21:24:40 2024 13 // Update Count : 59 14 14 // 15 15 … … 123 123 void ?|?(ofstream & out, const string_res & s); 124 124 ifstream & ?|?(ifstream & in, string_res & s); 125 void ?|?( 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 126 136 127 137 struct _Istream_Rstr { 128 138 string_res * s; 129 139 inline _Istream_str_base; 140 // _Istream_Rwidth rstr; 130 141 }; // _Istream_Rstr 131 142 132 143 static inline { 133 144 // read width does not include null terminator 134 _Istream_R str wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, rwd, {.flags.rwd : true}} }; }145 _Istream_Rwidth wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rwidth)@{ .s : &s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; } 135 146 _Istream_Rstr getline( string_res & s, const char delimiter = '\n' ) { 136 return (_Istream_Rstr)@{ &s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} }; 137 } 138 _Istream_Rstr & getline( _Istream_Rstr & fmt, const char delimiter = '\n' ) { 139 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt; 140 } 141 _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : false}} }; } 142 _Istream_Rstr & incl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; } 143 _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : true}} }; } 144 _Istream_Rstr & excl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; } 145 _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.ignore : true}} }; } 146 _Istream_Rstr & ignore( _Istream_Rstr & fmt ) { fmt.flags.ignore = true; return fmt; } 147 // return (_Istream_Rstr)@{ { .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } }; 148 return (_Istream_Rstr)@{ .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } }; 149 } 150 _Istream_Rstr & getline( _Istream_Rwidth & f, const char delimiter = '\n' ) { 151 f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Rstr &)f; 152 } 153 _Istream_Rquoted quoted( string_res & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) { 154 return (_Istream_Rquoted)@{ { .s : &s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } }; 155 } 156 _Istream_Rquoted & quoted( _Istream_Rwidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) { 157 f.delimiters[0] = Ldelimiter; f.delimiters[1] = Rdelimiter; f.delimiters[2] = '\0'; 158 return (_Istream_Rquoted &)f; 159 } 160 _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; } 161 _Istream_Rstr & incl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Rstr &)f; } 162 _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; } 163 _Istream_Rstr & excl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Rstr &)f; } 164 _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; } 165 _Istream_Rstr & ignore( _Istream_Rwidth & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; } 166 _Istream_Rquoted & ignore( _Istream_Rquoted & f ) { f.rstr.flags.ignore = true; return (_Istream_Rquoted &)f; } 167 _Istream_Rstr & ignore( _Istream_Rstr & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; } 147 168 } // distribution 169 ifstream & ?|?( ifstream & is, _Istream_Rquoted f ); 148 170 ifstream & ?|?( ifstream & is, _Istream_Rstr f ); 149 void ?|?( ifstream & is, _Istream_Rstr t ); 171 static inline ifstream & ?|?( ifstream & is, _Istream_Rwidth f ) { return is | *(_Istream_Rstr *)&f; } 150 172 151 173 // Concatenation -
libcfa/src/fstream.cfa
r0522ebe ra4da45e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 28 09:56:08202413 // Update Count : 5 5412 // Last Modified On : Sun Feb 11 20:55:45 2024 13 // Update Count : 580 14 14 // 15 15 … … 121 121 } // for 122 122 if ( file == 0p ) { 123 throw (open_failure){ os };123 throwResume (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 (open_failure){ is };243 throwResume (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 ;297 int nargs, tmperrno; 298 298 for () { // no check for EINTR limit waiting for keyboard input 299 errno = 0;300 299 disable_interrupts(); 300 errno = 0; 301 301 nargs = vfscanf( (FILE *)(is.file$), format, args ); 302 tmperrno = errno; 302 303 enable_interrupts(); 303 304 if ( nargs != EOF || errno != EINTR ) break; // timer interrupt ? … … 308 309 } // if 309 310 } // if 311 if ( tmperrno == ERANGE ) throwResume ExceptionInst( data_range ); 310 312 va_end( args ); 311 313 return nargs; … … 318 320 // *********************************** exceptions *********************************** 319 321 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 339 337 // exception I/O constructors 340 338 void ?{}( close_failure & ex, ofstream & ostream ) with( ex ) { … … 351 349 352 350 353 static vtable(write_failure) write_failure_vt;354 355 351 // exception I/O constructors 356 352 void ?{}( write_failure & ex, ofstream & ostream ) with( ex ) { … … 367 363 368 364 369 static vtable(read_failure) read_failure_vt;370 371 365 // exception I/O constructors 372 366 void ?{}( read_failure & ex, ofstream & ostream ) with( ex ) { -
libcfa/src/fstream.hfa
r0522ebe ra4da45e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Oct 18 20:30:12 202313 // Update Count : 2 6112 // Last Modified On : Sun Feb 11 20:35:00 2024 13 // Update Count : 274 14 14 // 15 15 … … 139 139 140 140 141 exception open_failure { 141 ExceptionDecl( 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 exception close_failure { 153 ExceptionDecl( 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 exception write_failure { 165 ExceptionDecl( 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 exception read_failure { 177 ExceptionDecl( 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
r0522ebe ra4da45e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 28 11:58:54202413 // Update Count : 19 1712 // Last Modified On : Mon Feb 12 09:26:05 2024 13 // Update Count : 1966 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 ); 776 777 char val[6]; 777 778 int args = fmt( is, "%5s", val ); 778 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );779 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 779 780 if ( strcmp( val, "true" ) == 0 ) b = true; 780 781 else if ( strcmp( val, "false" ) == 0 ) b = false; … … 787 788 788 789 istype & ?|?( istype & is, char & c ) { 790 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 789 791 char temp; 790 792 for () { 791 793 int args = fmt( is, "%c", &temp ); 792 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );794 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 793 795 // do not overwrite parameter with newline unless appropriate 794 796 if ( temp != '\n' || getANL$( is ) ) { c = temp; break; } … … 799 801 800 802 istype & ?|?( istype & is, signed char & sc ) { 803 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 801 804 int args = fmt( is, "%hhi", &sc ); 802 if ( args != -1 && args !=1 ) throwResume ExceptionInst( missing_data );805 if ( args != 1 ) throwResume ExceptionInst( missing_data ); 803 806 return is; 804 807 } // ?|? 805 808 806 809 istype & ?|?( istype & is, unsigned char & usc ) { 810 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 807 811 int args = fmt( is, "%hhi", &usc ); 808 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );812 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 809 813 return is; 810 814 } // ?|? 811 815 812 816 istype & ?|?( istype & is, short int & si ) { 817 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 813 818 int args = fmt( is, "%hi", &si ); 814 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );819 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 815 820 return is; 816 821 } // ?|? 817 822 818 823 istype & ?|?( istype & is, unsigned short int & usi ) { 824 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 819 825 int args = fmt( is, "%hi", &usi ); 820 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );826 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 821 827 return is; 822 828 } // ?|? 823 829 824 830 istype & ?|?( istype & is, int & i ) { 831 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 825 832 int args = fmt( is, "%i", &i ); 826 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );833 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 827 834 return is; 828 835 } // ?|? 829 836 830 837 istype & ?|?( istype & is, unsigned int & ui ) { 838 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 831 839 int args = fmt( is, "%i", &ui ); 832 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );840 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 833 841 return is; 834 842 } // ?|? 835 843 836 844 istype & ?|?( istype & is, long int & li ) { 845 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 837 846 int args = fmt( is, "%li", &li ); 838 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );847 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 839 848 return is; 840 849 } // ?|? 841 850 842 851 istype & ?|?( istype & is, unsigned long int & ulli ) { 852 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 843 853 int args = fmt( is, "%li", &ulli ); 844 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );854 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 845 855 return is; 846 856 } // ?|? 847 857 848 858 istype & ?|?( istype & is, long long int & lli ) { 859 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 849 860 int args = fmt( is, "%lli", &lli ); 850 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );861 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 851 862 return is; 852 863 } // ?|? 853 864 854 865 istype & ?|?( istype & is, unsigned long long int & ulli ) { 866 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 855 867 int args = fmt( is, "%lli", &ulli ); 856 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );868 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 857 869 return is; 858 870 } // ?|? … … 881 893 882 894 istype & ?|?( istype & is, float & f ) { 895 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 883 896 int args = fmt( is, "%f", &f ); 884 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );897 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 885 898 return is; 886 899 } // ?|? 887 900 888 901 istype & ?|?( istype & is, double & d ) { 902 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 889 903 int args = fmt( is, "%lf", &d ); 890 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );904 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 891 905 return is; 892 906 } // ?|? 893 907 894 908 istype & ?|?( istype & is, long double & ld ) { 909 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 895 910 int args = fmt( is, "%Lf", &ld ); 896 if ( args != -1&& args != 1 ) throwResume ExceptionInst( missing_data );911 if ( ! eof( is ) && args != 1 ) throwResume ExceptionInst( missing_data ); 897 912 return is; 898 913 } // ?|? 899 914 900 915 istype & ?|?( istype & is, float _Complex & fc ) { 916 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 901 917 float re, im; 902 918 int args = fmt( is, "%f%fi", &re, &im ); 903 if ( args != -1&& args != 2 ) throwResume ExceptionInst( missing_data );919 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data ); 904 920 fc = re + im * _Complex_I; 905 921 return is; … … 907 923 908 924 istype & ?|?( istype & is, double _Complex & dc ) { 925 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 909 926 double re, im; 910 927 int args = fmt( is, "%lf%lfi", &re, &im ); 911 if ( args != -1&& args != 2 ) throwResume ExceptionInst( missing_data );928 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data ); 912 929 dc = re + im * _Complex_I; 913 930 return is; … … 915 932 916 933 istype & ?|?( istype & is, long double _Complex & ldc ) { 934 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 917 935 long double re, im; 918 936 int args = fmt( is, "%Lf%Lfi", &re, &im ); 919 if ( args != -1&& args != 2 ) throwResume ExceptionInst( missing_data );937 if ( ! eof( is ) && args != 2 ) throwResume ExceptionInst( missing_data ); 920 938 ldc = re + im * _Complex_I; 921 939 return is; … … 923 941 924 942 istype & ?|?( istype & is, const char fmt[] ) { 943 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 925 944 size_t len = strlen( fmt ); 926 char fmt 2[len + 16];927 strcpy( fmt 2, fmt ); // copy format and add %n928 strcpy( &fmt 2[len], "%n" );945 char fmtstr[len + 16]; 946 strcpy( fmtstr, fmt ); // copy format and add %n 947 strcpy( &fmtstr[len], "%n" ); 929 948 int len2 = -1; 930 int args = fmt( is, fmt2, &len2 );931 if ( args != -1&& len2 == -1 ) throwResume ExceptionInst( missing_data );949 fmt( is, fmtstr, &len2 ); 950 if ( ! eof( is ) && len2 == -1 ) throwResume ExceptionInst( missing_data ); 932 951 return is; 933 952 } // ?|? … … 963 982 forall( istype & | basic_istream( istype ) ) { 964 983 istype & ?|?( istype & is, _Istream_Cskip f ) { 965 // printf( "skip %s %d\n", f.scanset, f.wd);984 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 966 985 if ( f.scanset ) { 967 986 int nscanset = strlen(f.scanset); … … 971 990 strcpy( &fmtstr[pos], f.scanset ); pos += nscanset; 972 991 strcpy( &fmtstr[pos], "]" ); 973 fmt( is, fmtstr, "" ); // skip scanset 992 fmt( is, fmtstr, "" ); // skip scanset, zero or more 974 993 } else { 975 994 char ch; 976 // fprintf( stderr, "skip " );977 995 for ( f.wd ) { // skip N characters 978 if ( eof( is ) ) break; 979 fmt( is, "%c", &ch ); 980 // fprintf( stderr, "`%c' ", ch ); 996 int args = fmt( is, "%c", &ch ); 997 if ( args != 1 ) throwResume ExceptionInst( missing_data ); 981 998 } // for 982 999 } // if … … 985 1002 986 1003 istype & ?|?( istype & is, _Istream_Cquoted f ) with( f.cstr ) { 1004 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 987 1005 int args; 988 1006 fini: { 989 args = fmt( is, "%*[ \f\n\r\t\v]" ); // remove leading whitespace 990 if ( eof( is ) ) break fini; 991 char rfmt[4] = { delimiters[0], '%', 'n', '\0' }; 992 int len = 0; // may not be set in fmt 993 args = fmt( is, rfmt, &len ); // remove leading quote 994 if ( len == 0 || eof( is ) ) break fini; 1007 char rfmt[5] = { ' ', delimiters[0], '%', 'n', '\0' }; 1008 int len = -1; // may not be set in fmt 1009 args = fmt( is, rfmt, &len ); // remove leading whitespace and quote 1010 if ( eof( is ) || len == -1 ) break fini; 995 1011 996 1012 // Change the remainder of the read into a getline by reseting the closing delimiter. … … 1011 1027 1012 1028 istype & ?|?( istype & is, _Istream_Cstr f ) with( f.cstr ) { 1029 if ( eof( is ) ) throwResume ExceptionInst( missing_data ); 1013 1030 const char * scanset; 1014 1031 size_t nscanset = 0; … … 1041 1058 if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*' 1042 1059 else args = fmt( is, fmtstr, s, &len ); 1043 // fprintf( stderr, "cstr %s %d %d %d %s\n", fmtstr, args, len, wd, s);1060 // fprintf( stderr, "cstr %s %d %d %d\n", fmtstr, args, len, f.cstr.wd ); 1044 1061 if ( check && len >= rwd && ! eof( is ) ) { // might not fit 1045 1062 char peek; 1046 1063 fmt( is, "%c", &peek ); // check for whitespace terminator 1047 1064 // fprintf( stderr, "peek %d '%c'\n", args, peek ); 1048 if ( ! eof( is ) ) { 1065 if ( ! eof( is ) ) { // can only fail at eof 1049 1066 ungetc( is, peek ); 1050 1067 if ( ! isspace( peek ) ) throwResume ExceptionInst( cstring_length ); … … 1052 1069 } // if 1053 1070 // FIX ME: CFA strings need to be modified to NOT change the argument for this case, then this can be removed. 1054 if ( ! flags.ignore && args == 0 ) s[0]= '\0'; // read failed => no pattern match => set string to null 1071 //fprintf( stderr, "cstr %d %d %d %d '%s'\n", flags.ignore, args, len, eof( is ), s ); 1072 //if ( ! flags.ignore && args == 0 ) s[0]= '\0'; // read failed => no pattern match => set string to null 1055 1073 } else { 1056 1074 if ( flags.delimiter ) { // getline 1057 1075 int len = 0; // may not be set in fmt 1058 if ( delimiters[2] != '\0' ) { // read single character ?1076 if ( delimiters[2] != '\0' ) { // (quoted) read single character ? 1059 1077 sprintf( &fmtstr[pos], "c%%n" ); 1060 1078 } else { … … 1063 1081 if ( flags.ignore ) args = fmt( is, fmtstr, &len ); // no string argument for '*' 1064 1082 else args = fmt( is, fmtstr, s, &len ); 1083 1065 1084 if ( check && len == rwd && ! eof( is ) ) { // might not fit 1066 1085 char peek; … … 1096 1115 } // if 1097 1116 if ( args == 1 && eof( is ) ) { // data but scan ended at EOF 1098 // fprintf( stderr, "clear\n" );1099 1117 clear( is ); // => reset EOF => detect again on next read 1100 1118 } // if -
libcfa/src/iostream.hfa
r0522ebe ra4da45e 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Jan 28 11:56:29202413 // Update Count : 7 3312 // Last Modified On : Tue Feb 6 18:35:54 2024 13 // Update Count : 743 14 14 // 15 15 … … 370 370 // *********************************** exceptions *********************************** 371 371 372 ExceptionDecl( cstring_length ); 373 ExceptionDecl( missing_data ); 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 374 375 375 376 // *********************************** manipulators *********************************** … … 406 407 char * s; 407 408 inline _Istream_str_base; 408 }; // _Istream_C str409 }; // _Istream_Cwidth 409 410 410 411 // Restrict nesting of input manipulators to those combinations that make sense. 412 413 struct _Istream_Cquoted { 414 _Istream_Cwidth cstr; 415 }; // _Istream_Cquoted 411 416 412 417 struct _Istream_Cstr { … … 414 419 }; // _Istream_Cstr 415 420 416 struct _Istream_Cquoted {417 _Istream_Cwidth cstr;418 }; // _Istream_Cquoted419 420 421 static inline { 421 422 // 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); 422 423 _Istream_Cwidth wdi( unsigned int wd, char s[] ) { 423 if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator424 if ( wd <= 1 ) throwResume ExceptionInst( cstring_length ); // minimum 1 character and null terminator 424 425 return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } }; 425 426 } 426 427 _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) { 427 if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset428 if ( wd <= 1 || wd <= rwd ) throwResume ExceptionInst( cstring_length ); // minimum 1 character, null terminator, plus subset 428 429 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; 429 433 } 430 434 _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) { … … 435 439 return (_Istream_Cquoted &)f; 436 440 } 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 }440 441 _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; } 441 442 _Istream_Cstr & excl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Cstr &)f; } 442 _Istream_Cstr ignore( const char s[] ) { return (_Istream_C width)@{ .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }443 _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cstr)@{ { .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; } 443 444 _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; } 444 445 _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; } … … 450 451 istype & ?|?( istype & is, _Istream_Cquoted f ); 451 452 istype & ?|?( istype & is, _Istream_Cstr f ); 452 static inline { 453 istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; } 454 } // distribution 453 static inline istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; } 455 454 } // distribution 456 455 -
src/AST/Attribute.hpp
r0522ebe ra4da45e 9 9 // Author : Aaron B. Moss 10 10 // Created On : Fri May 10 10:30:00 2019 11 // Last Modified By : Aaron B. Moss11 // Last Modified By : Peter A. Buhr 12 12 // Created On : Fri May 10 10:30:00 2019 13 // Update Count : 113 // Update Count : 2 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
r0522ebe ra4da45e 366 366 367 367 /// Passes extra layout arguments for sized polymorphic type parameters. 368 ast::vector<ast::Expr>::iteratorpassTypeVars(368 void passTypeVars( 369 369 ast::ApplicationExpr * expr, 370 ast::vector<ast::Expr> & extraArgs, 370 371 ast::FunctionType const * funcType ); 371 372 /// Wraps a function application with a new temporary for the … … 387 388 /// parameter list. arg should point into expr's argument list. 388 389 void boxParams( 389 ast::ApplicationExpr const* expr,390 ast:: vector<ast::Expr>::iterator arg,390 ast::ApplicationExpr * expr, 391 ast::Type const * polyRetType, 391 392 ast::FunctionType const * function, 392 393 const TypeVarMap & typeVars ); … … 395 396 void addInferredParams( 396 397 ast::ApplicationExpr * expr, 397 ast::vector<ast::Expr> ::iterator arg,398 ast::vector<ast::Expr> & extraArgs, 398 399 ast::FunctionType const * functionType, 399 400 const TypeVarMap & typeVars ); … … 636 637 ast::Expr const * ret = expr; 637 638 638 // TODO: This entire section should probably be refactored to do less639 // pushing to the front/middle of a vector.640 ptrdiff_t initArgCount = mutExpr->args.size();641 642 639 TypeVarMap exprTypeVars; 643 640 makeTypeVarMap( function, exprTypeVars ); … … 662 659 } 663 660 664 assert( typeSubs ); 665 ast::vector<ast::Expr>::iterator argIt = 666 passTypeVars( mutExpr, function ); 667 addInferredParams( mutExpr, argIt, function, exprTypeVars ); 668 669 argIt = mutExpr->args.begin(); 670 std::advance( argIt, ( mutExpr->args.size() - initArgCount ) ); 671 672 boxParams( mutExpr, argIt, function, exprTypeVars ); 661 ast::vector<ast::Expr> prependArgs; 662 passTypeVars( mutExpr, prependArgs, function ); 663 addInferredParams( mutExpr, prependArgs, function, exprTypeVars ); 664 665 boxParams( mutExpr, dynRetType, function, exprTypeVars ); 666 spliceBegin( mutExpr->args, prependArgs ); 673 667 passAdapters( mutExpr, function, exprTypeVars ); 674 668 … … 766 760 } 767 761 768 ast::vector<ast::Expr>::iteratorCallAdapter::passTypeVars(762 void CallAdapter::passTypeVars( 769 763 ast::ApplicationExpr * expr, 764 ast::vector<ast::Expr> & extraArgs, 770 765 ast::FunctionType const * function ) { 771 766 assert( typeSubs ); 772 ast::vector<ast::Expr>::iterator arg = expr->args.begin();773 767 // Pass size/align for type variables. 774 768 for ( ast::ptr<ast::TypeInstType> const & typeVar : function->forall ) { … … 780 774 toString( typeSubs ).c_str(), typeVar->typeString().c_str() ); 781 775 } 782 arg = expr->args.insert( arg,776 extraArgs.emplace_back( 783 777 new ast::SizeofExpr( expr->location, ast::deepCopy( concrete ) ) ); 784 arg++; 785 arg = expr->args.insert( arg, 778 extraArgs.emplace_back( 786 779 new ast::AlignofExpr( expr->location, ast::deepCopy( concrete ) ) ); 787 arg++; 788 } 789 return arg; 780 } 790 781 } 791 782 … … 913 904 914 905 void CallAdapter::boxParams( 915 ast::ApplicationExpr const* expr,916 ast:: vector<ast::Expr>::iterator arg,906 ast::ApplicationExpr * expr, 907 ast::Type const * polyRetType, 917 908 ast::FunctionType const * function, 918 909 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 919 914 for ( auto param : function->params ) { 920 915 assertf( arg != expr->args.end(), … … 928 923 void CallAdapter::addInferredParams( 929 924 ast::ApplicationExpr * expr, 930 ast::vector<ast::Expr> ::iterator arg,925 ast::vector<ast::Expr> & extraArgs, 931 926 ast::FunctionType const * functionType, 932 927 TypeVarMap const & typeVars ) { 933 ast::vector<ast::Expr>::iterator cur = arg;934 928 for ( auto assertion : functionType->assertions ) { 935 929 auto inferParam = expr->inferred.inferParams().find( … … 940 934 ast::ptr<ast::Expr> newExpr = ast::deepCopy( inferParam->second.expr ); 941 935 boxParam( newExpr, assertion->result, typeVars ); 942 cur = expr->args.insert( cur, newExpr.release() ); 943 ++cur; 936 extraArgs.emplace_back( newExpr.release() ); 944 937 } 945 938 } … … 1116 1109 ); 1117 1110 1118 for ( auto group : group_iterate( realType->assertions, 1119 adapterType->assertions, adaptee->assertions ) ) { 1120 auto assertArg = std::get<0>( group ); 1121 auto assertParam = std::get<1>( group ); 1122 auto assertReal = std::get<2>( group ); 1111 for ( auto const & [assertArg, assertParam, assertReal] : group_iterate( 1112 realType->assertions, adapterType->assertions, adaptee->assertions ) ) { 1123 1113 adapteeApp->args.push_back( makeAdapterArg( 1124 1114 assertParam->var, assertArg->var->get_type(), … … 1977 1967 bool hasDynamicLayout = false; 1978 1968 1979 for ( auto pair : group_iterate( baseParams, typeParams ) ) { 1980 auto baseParam = std::get<0>( pair ); 1981 auto typeParam = std::get<1>( pair ); 1969 for ( auto const & [baseParam, typeParam] : group_iterate( 1970 baseParams, typeParams ) ) { 1982 1971 if ( !baseParam->isComplete() ) continue; 1983 1972 ast::TypeExpr const * typeExpr = typeParam.as<ast::TypeExpr>(); -
src/InitTweak/FixInit.cpp
r0522ebe ra4da45e 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 ast::ptr<ast::Stmt>callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) );1136 const ast::Stmt * callStmt = SymTab::genImplicitCall( srcParam, memberDest, loc, function->name, field, static_cast<SymTab::LoopDirection>(isCtor) ); 1137 1137 1138 1138 if ( callStmt ) { -
src/InitTweak/GenInit.cc
r0522ebe ra4da45e 239 239 if ( varExpr->var == retVal ) return stmt; 240 240 } 241 ast::ptr<ast::Stmt>ctorStmt = genCtorDtor(241 const 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 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg) {329 const 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
r0522ebe ra4da45e 33 33 34 34 /// generates a single ctor/dtor statement using objDecl as the 'this' parameter and arg as the optional argument 35 ast::ptr<ast::Stmt> genCtorDtor (const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * objDecl, const ast::Expr * arg = nullptr);35 const 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
r0522ebe ra4da45e 10 10 // Created On : Sat May 16 12:34:05 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Dec 14 19:05:17 202313 // Update Count : 1 40712 // Last Modified On : Fri Feb 23 18:25:57 2024 13 // Update Count : 1533 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, ' ' ) << attr->name.c_str() << endl; 163 os << string( indent + 4, ' ' ); 164 ast::print( os, attr, indent + 2 ); 164 165 } // for 165 166 } // if … … 537 538 } // DeclarationNode::checkSpecifiers 538 539 539 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q ) {540 DeclarationNode * DeclarationNode::copySpecifiers( DeclarationNode * q, bool copyattr ) { 540 541 funcSpecs |= q->funcSpecs; 541 542 storageClasses |= q->storageClasses; 542 543 543 std::vector<ast::ptr<ast::Attribute>> tmp; 544 tmp.reserve( q->attributes.size() ); 545 for ( auto const & attr : q->attributes ) { 546 tmp.emplace_back( ast::shallowCopy( attr.get() ) ); 547 } 548 spliceBegin( attributes, tmp ); 544 if ( copyattr ) { 545 std::vector<ast::ptr<ast::Attribute>> tmp; 546 tmp.reserve( q->attributes.size() ); 547 for ( auto const & attr : q->attributes ) { 548 tmp.emplace_back( ast::shallowCopy( attr.get() ) ); 549 } // for 550 spliceBegin( attributes, tmp ); 551 } // if 549 552 550 553 return this; … … 681 684 } 682 685 683 DeclarationNode * DeclarationNode::addType( DeclarationNode * o ) {686 DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) { 684 687 if ( o ) { 685 688 checkSpecifiers( o ); 686 copySpecifiers( o );689 copySpecifiers( o, copyattr ); 687 690 if ( o->type ) { 688 691 if ( ! type ) { 689 692 if ( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) { 693 // Hide type information aggregate instances. 690 694 type = new TypeData( TypeData::AggregateInst ); 691 type->aggInst.aggregate = o->type; 695 type->aggInst.aggregate = o->type; // change ownership 696 type->aggInst.aggregate->aggregate.attributes.swap( o->attributes ); // change ownership 692 697 if ( o->type->kind == TypeData::Aggregate ) { 693 698 type->aggInst.hoistType = o->type->aggregate.body; … … 700 705 type = o->type; 701 706 } // if 702 o->type = nullptr; 707 o->type = nullptr; // change ownership 703 708 } else { 704 709 addTypeToType( o->type, type ); … … 953 958 } 954 959 955 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o ) {960 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) { 956 961 if ( ! o ) return nullptr; 957 962 958 o->copySpecifiers( this );963 o->copySpecifiers( this, copyattr ); 959 964 if ( type ) { 960 965 TypeData * srcType = type; … … 999 1004 DeclarationNode * newnode = new DeclarationNode; 1000 1005 newnode->type = ret; 1006 if ( ret->kind == TypeData::Aggregate ) { 1007 newnode->attributes.swap( ret->aggregate.attributes ); 1008 } // if 1001 1009 return newnode; 1002 1010 } // if … … 1110 1118 if ( extr->type->kind == TypeData::Aggregate ) { 1111 1119 // typedef struct { int A } B is the only case? 1112 extracted_named = ! extr->type->aggregate.anon;1120 extracted_named = ! extr->type->aggregate.anon; 1113 1121 } else if ( extr->type->kind == TypeData::Enum ) { 1114 1122 // typedef enum { A } B is the only case? 1115 extracted_named = ! extr->type->enumeration.anon;1123 extracted_named = ! extr->type->enumeration.anon; 1116 1124 } else { 1117 1125 extracted_named = true; -
src/Parser/DeclarationNode.h
r0522ebe ra4da45e 9 9 // Author : Andrew Beach 10 10 // Created On : Wed Apr 5 11:38:00 2023 11 // Last Modified By : Andrew Beach12 // Last Modified On : Wed Apr 5 11:55:00 202313 // Update Count : 011 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Feb 17 09:24:12 2024 13 // Update Count : 4 14 14 // 15 15 … … 83 83 void checkQualifiers( const TypeData *, const TypeData * ); 84 84 void checkSpecifiers( DeclarationNode * ); 85 DeclarationNode * copySpecifiers( DeclarationNode * );86 DeclarationNode * addType( DeclarationNode * );85 DeclarationNode * copySpecifiers( DeclarationNode *, bool = true ); 86 DeclarationNode * addType( DeclarationNode *, bool = true ); 87 87 DeclarationNode * addTypedef(); 88 88 DeclarationNode * addEnumBase( DeclarationNode * ); … … 106 106 107 107 DeclarationNode * cloneType( std::string * newName ); 108 DeclarationNode * cloneBaseType( DeclarationNode * newdecl );108 DeclarationNode * cloneBaseType( DeclarationNode * newdecl, bool = true ); 109 109 110 110 DeclarationNode * appendList( DeclarationNode * node ) { -
src/Parser/TypeData.cc
r0522ebe ra4da45e 10 10 // Created On : Sat May 16 15:12:51 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Dec 14 18:59:12 202313 // Update Count : 68412 // Last Modified On : Fri Feb 23 08:58:30 2024 13 // Update Count : 734 14 14 // 15 15 … … 20 20 21 21 #include "AST/Decl.hpp" // for AggregateDecl, ObjectDecl, TypeDe... 22 #include "AST/Attribute.hpp" // for Attribute 22 23 #include "AST/Init.hpp" // for SingleInit, ListInit 23 24 #include "AST/Print.hpp" // for print 24 25 #include "Common/SemanticError.h" // for SemanticError 25 26 #include "Common/utility.h" // for splice, spliceBegin 27 #include "Common/Iterate.hpp" // for reverseIterate 26 28 #include "Parser/ExpressionNode.h" // for ExpressionNode 27 29 #include "Parser/StatementNode.h" // for StatementNode … … 199 201 newtype->aggregate.kind = aggregate.kind; 200 202 newtype->aggregate.name = aggregate.name ? new string( *aggregate.name ) : nullptr; 203 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr; 201 204 newtype->aggregate.params = maybeCopy( aggregate.params ); 202 205 newtype->aggregate.actuals = maybeCopy( aggregate.actuals ); 203 206 newtype->aggregate.fields = maybeCopy( aggregate.fields ); 207 newtype->aggregate.attributes = aggregate.attributes; 204 208 newtype->aggregate.body = aggregate.body; 205 209 newtype->aggregate.anon = aggregate.anon; 206 210 newtype->aggregate.tagged = aggregate.tagged; 207 newtype->aggregate.parent = aggregate.parent ? new string( *aggregate.parent ) : nullptr;208 211 break; 209 212 case AggregateInst: … … 336 339 } // if 337 340 if ( aggregate.body ) { 338 os << string( indent + 2, ' ' ) << " with body" << endl; 341 os << string( indent + 2, ' ' ) << "with body" << endl; 342 } // if 343 if ( ! aggregate.attributes.empty() ) { 344 os << string( indent + 2, ' ' ) << "with attributes" << endl; 345 for ( ast::ptr<ast::Attribute> const & attr : reverseIterate( aggregate.attributes ) ) { 346 os << string( indent + 4, ' ' ); 347 ast::print( os, attr, indent + 2 ); 348 } // for 339 349 } // if 340 350 break; … … 358 368 } // if 359 369 if ( enumeration.body ) { 360 os << string( indent + 2, ' ' ) << " with body" << endl;370 os << string( indent + 2, ' ' ) << "with body" << endl; 361 371 } // if 362 372 if ( base ) { … … 1088 1098 1089 1099 ast::BaseInstType * buildComAggInst( 1090 const TypeData * t ype,1100 const TypeData * td, 1091 1101 std::vector<ast::ptr<ast::Attribute>> && attributes, 1092 1102 ast::Linkage::Spec linkage ) { 1093 switch ( t ype->kind ) {1103 switch ( td->kind ) { 1094 1104 case TypeData::Enum: 1095 if ( t ype->enumeration.body ) {1105 if ( td->enumeration.body ) { 1096 1106 ast::EnumDecl * typedecl = 1097 buildEnum( t ype, std::move( attributes ), linkage );1107 buildEnum( td, std::move( attributes ), linkage ); 1098 1108 return new ast::EnumInstType( 1099 1109 typedecl, 1100 buildQualifiers( t ype)1110 buildQualifiers( td ) 1101 1111 ); 1102 1112 } else { 1103 1113 return new ast::EnumInstType( 1104 *t ype->enumeration.name,1105 buildQualifiers( t ype)1114 *td->enumeration.name, 1115 buildQualifiers( td ) 1106 1116 ); 1107 1117 } // if 1108 1118 break; 1109 1119 case TypeData::Aggregate: 1110 if ( t ype->aggregate.body ) {1120 if ( td->aggregate.body ) { 1111 1121 ast::AggregateDecl * typedecl = 1112 buildAggregate( t ype, std::move( attributes ), linkage );1113 switch ( t ype->aggregate.kind ) {1122 buildAggregate( td, std::move( attributes ), linkage ); 1123 switch ( td->aggregate.kind ) { 1114 1124 case ast::AggregateDecl::Struct: 1115 1125 case ast::AggregateDecl::Coroutine: … … 1118 1128 return new ast::StructInstType( 1119 1129 strict_dynamic_cast<ast::StructDecl *>( typedecl ), 1120 buildQualifiers( t ype)1130 buildQualifiers( td ) 1121 1131 ); 1122 1132 case ast::AggregateDecl::Union: 1123 1133 return new ast::UnionInstType( 1124 1134 strict_dynamic_cast<ast::UnionDecl *>( typedecl ), 1125 buildQualifiers( t ype)1135 buildQualifiers( td ) 1126 1136 ); 1127 1137 case ast::AggregateDecl::Trait: … … 1132 1142 } // switch 1133 1143 } else { 1134 switch ( t ype->aggregate.kind ) {1144 switch ( td->aggregate.kind ) { 1135 1145 case ast::AggregateDecl::Struct: 1136 1146 case ast::AggregateDecl::Coroutine: … … 1138 1148 case ast::AggregateDecl::Thread: 1139 1149 return new ast::StructInstType( 1140 *t ype->aggregate.name,1141 buildQualifiers( t ype)1150 *td->aggregate.name, 1151 buildQualifiers( td ) 1142 1152 ); 1143 1153 case ast::AggregateDecl::Union: 1144 1154 return new ast::UnionInstType( 1145 *t ype->aggregate.name,1146 buildQualifiers( t ype)1155 *td->aggregate.name, 1156 buildQualifiers( td ) 1147 1157 ); 1148 1158 case ast::AggregateDecl::Trait: 1149 1159 return new ast::TraitInstType( 1150 *t ype->aggregate.name,1151 buildQualifiers( t ype)1160 *td->aggregate.name, 1161 buildQualifiers( td ) 1152 1162 ); 1153 1163 default: -
src/Parser/TypeData.h
r0522ebe ra4da45e 9 9 // Author : Peter A. Buhr 10 10 // Created On : Sat May 16 15:18:36 2015 11 // Last Modified By : Andrew Beach12 // Last Modified On : Wed Mar 1 10:44:00 202313 // Update Count : 2 0611 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 22 16:30:31 2024 13 // Update Count : 210 14 14 // 15 15 … … 30 30 ast::AggregateDecl::Aggregate kind; 31 31 const std::string * name = nullptr; 32 const std::string * parent = nullptr; 32 33 DeclarationNode * params = nullptr; 33 34 ExpressionNode * actuals = nullptr; // holds actual parameters later applied to AggInst 34 35 DeclarationNode * fields = nullptr; 36 std::vector<ast::ptr<ast::Attribute>> attributes; 35 37 bool body; 36 38 bool anon; 37 39 bool tagged; 38 const std::string * parent = nullptr;39 40 }; 40 41 -
src/Parser/parser.yy
r0522ebe ra4da45e 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Nov 26 13:18:06 202313 // Update Count : 6 39812 // Last Modified On : Fri Feb 23 18:25:46 2024 13 // Update Count : 6484 14 14 // 15 15 … … 102 102 103 103 DeclarationNode * distAttr( DeclarationNode * typeSpec, DeclarationNode * declList ) { 104 // distribute declaration_specifier across all declared variables, e.g., static, const, but not__attribute__.104 // Distribute type specifier across all declared variables, e.g., static, const, __attribute__. 105 105 assert( declList ); 106 // printf( "distAttr1 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout ); 107 DeclarationNode * cl = (new DeclarationNode)->addType( typeSpec ); 108 // printf( "distAttr2 cl %p\n", cl ); cl->type->print( std::cout ); 109 // cl->type->aggregate.name = cl->type->aggInst.aggregate->aggregate.name; 110 106 107 // Do not distribute attributes for aggregates because the attributes surrounding the aggregate belong it not the 108 // variables in the declaration list, e.g., 109 // 110 // struct __attribute__(( aligned(128) )) S { ... 111 // } v1 __attribute__(( aligned(64) )), v2 __attribute__(( aligned(32) )), v3; 112 // struct S v4; 113 // 114 // v1 => 64, v2 =>32, v3 => 128, v2 => 128 115 // 116 // Anonymous aggregates are a special case because there is no aggregate to bind the attribute to; hence it floats 117 // to the declaration list. 118 // 119 // struct __attribute__(( aligned(128) )) /*anonymous */ { ... } v1; 120 // 121 // v1 => 128 122 123 bool copyattr = ! (typeSpec->type && typeSpec->type->kind == TypeData::Aggregate && ! typeSpec->type->aggregate.anon ); 124 125 // addType copies the type information for the aggregate instances from typeSpec into cl's aggInst.aggregate. 126 DeclarationNode * cl = (new DeclarationNode)->addType( typeSpec ); // typeSpec IS DELETED!!! 127 128 // Start at second variable in declaration list and clone the type specifiers for each variable.. 111 129 for ( DeclarationNode * cur = dynamic_cast<DeclarationNode *>( declList->get_next() ); cur != nullptr; cur = dynamic_cast<DeclarationNode *>( cur->get_next() ) ) { 112 cl->cloneBaseType( cur );130 cl->cloneBaseType( cur, copyattr ); // cur is modified 113 131 } // for 114 declList->addType( cl ); 115 // printf( "distAttr3 declList %p\n", declList ); declList->print( std::cout, 0 ); 132 133 // Add first variable in declaration list with hidden type information in aggInst.aggregate, which is used by 134 // extractType to recover the type for the aggregate instances. 135 declList->addType( cl, copyattr ); // cl IS DELETED!!! 116 136 return declList; 117 137 } // distAttr … … 192 212 fieldList = DeclarationNode::newName( nullptr ); 193 213 } // if 194 // return distAttr( typeSpec, fieldList ); // mark all fields in list195 214 196 215 // printf( "fieldDecl3 typeSpec %p\n", typeSpec ); typeSpec->print( std::cout, 0 ); 197 DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list216 DeclarationNode * temp = distAttr( typeSpec, fieldList ); // mark all fields in list 198 217 // printf( "fieldDecl4 temp %p\n", temp ); temp->print( std::cout, 0 ); 199 218 return temp; … … 761 780 | string_literal '`' identifier // CFA, postfix call 762 781 { $$ = new ExpressionNode( build_func( yylloc, new ExpressionNode( build_varref( yylloc, build_postfix_name( $3 ) ) ), $1 ) ); } 782 783 // SKULLDUGGERY: The typedef table used for parsing does not store fields in structures. To parse a qualified 784 // name, it is assumed all name-tokens after the first are identifiers, regardless of how the lexer identifies 785 // them. For example: 786 // 787 // struct S; 788 // forall(T) struct T; 789 // union U; 790 // enum E { S, T, E }; 791 // struct Z { int S, T, Z, E, U; }; 792 // void fred () { 793 // Z z; 794 // z.S; // lexer returns S is TYPEDEFname 795 // z.T; // lexer returns T is TYPEGENname 796 // z.Z; // lexer returns Z is TYPEDEFname 797 // z.U; // lexer returns U is TYPEDEFname 798 // z.E; // lexer returns E is TYPEDEFname 799 // } 763 800 | postfix_expression '.' identifier 764 801 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); } 802 | postfix_expression '.' TYPEDEFname // CFA, SKULLDUGGERY 803 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); } 804 | postfix_expression '.' TYPEGENname // CFA, SKULLDUGGERY 805 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_varref( yylloc, $3 ) ) ); } 806 765 807 | postfix_expression '.' INTEGERconstant // CFA, tuple index 766 808 { $$ = new ExpressionNode( build_fieldSel( yylloc, $1, build_constantInteger( yylloc, *$3 ) ) ); } … … 1039 1081 | logical_OR_expression '?' comma_expression ':' conditional_expression 1040 1082 { $$ = new ExpressionNode( build_cond( yylloc, $1, $3, $5 ) ); } 1041 // FIX ME: computes $1 twice1042 1083 | logical_OR_expression '?' /* empty */ ':' conditional_expression // GCC, omitted first operand 1043 { $$ = new ExpressionNode( build_cond( yylloc, $1, $1->clone(), $4 ) ); }1084 { $$ = new ExpressionNode( build_cond( yylloc, $1, nullptr, $4 ) ); } 1044 1085 ; 1045 1086 … … 1856 1897 declaration_list: 1857 1898 declaration 1858 | declaration_list declaration 1859 { $$ = $1->appendList( $2 ); } 1899 | declaration_list declaration { $$ = $1->appendList( $2 ); } 1860 1900 ; 1861 1901 … … 1890 1930 declaration: // old & new style declarations 1891 1931 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 // } // for1897 }1898 1932 | cfa_declaration ';' // CFA 1899 1933 | static_assert // C11 … … 2348 2382 sue_declaration_specifier: // struct, union, enum + storage class + type specifier 2349 2383 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 // } // for2355 }2356 2384 | declaration_qualifier_list sue_type_specifier 2357 2385 { $$ = $2->addQualifiers( $1 ); } … … 2364 2392 sue_type_specifier: // struct, union, enum + type specifier 2365 2393 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 // } // for2371 }2372 2394 | type_qualifier_list 2373 2395 { if ( $1->type != nullptr && $1->type->forall ) forall = true; } // remember generic type … … 2442 2464 elaborated_type: // struct, union, enum 2443 2465 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 // } // for2449 }2450 2466 | enum_type 2451 2467 ; … … 2679 2695 { $$ = DeclarationNode::newEnum( nullptr, $4, true, false )->addQualifiers( $2 ); } 2680 2696 | ENUM attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule 2681 { SemanticError( yylloc, "syntax error, hiding '!'the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }2697 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; } 2682 2698 | ENUM attribute_list_opt identifier 2683 2699 { typedefTable.makeTypedef( *$3, "enum_type 1" ); } … … 2694 2710 } 2695 2711 | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // unqualified type name 2696 { SemanticError( yylloc, "syntax error, hiding '!'the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }2712 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; } 2697 2713 | ENUM '(' ')' attribute_list_opt '{' enumerator_list comma_opt '}' 2698 2714 { … … 2700 2716 } 2701 2717 | ENUM '(' ')' attribute_list_opt '!' '{' enumerator_list comma_opt '}' // invalid syntax rule 2702 { SemanticError( yylloc, "syntax error, hiding '!'the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; }2718 { SemanticError( yylloc, "syntax error, hiding ('!') the enumerator names of an anonymous enumeration means the names are inaccessible." ); $$ = nullptr; } 2703 2719 | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt identifier attribute_list_opt 2704 2720 { … … 3177 3193 // unit, which is a dubious task, especially because C uses name rather than structural typing; hence it is 3178 3194 // disallowed at the moment. 3179 if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static && $1->type && $1->type->kind == TypeData::AggregateInst ) { 3195 if ( $1->linkage == ast::Linkage::Cforall && ! $1->storageClasses.is_static && 3196 $1->type && $1->type->kind == TypeData::AggregateInst ) { 3180 3197 if ( $1->type->aggInst.aggregate->kind == TypeData::Enum && $1->type->aggInst.aggregate->enumeration.anon ) { 3181 3198 SemanticError( yylloc, "extern anonymous enumeration is currently unimplemented." ); $$ = nullptr; -
src/ResolvExpr/CandidateFinder.cpp
r0522ebe ra4da45e 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 ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 ); 1620 1621 CandidateFinder finder1( context, tenv ); 1621 ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );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(); 1626 1629 CandidateFinder finder2( context, tenv ); 1627 1630 finder2.allowVoid = true; 1628 finder2.find( conditionalExpr->arg2, ResolveMode::withAdjustment() );1631 finder2.find( arg2, ResolveMode::withAdjustment() ); 1629 1632 if ( finder2.candidates.empty() ) return; 1630 1633 … … 1897 1900 CandidateRef newCand = 1898 1901 std::make_shared<Candidate>( 1899 newExpr, copy( tenv ), ast::OpenVarSet{}, 1902 newExpr, copy( tenv ), ast::OpenVarSet{}, 1900 1903 ast::AssertionSet{}, Cost::zero, cost 1901 1904 ); 1902 1905 1903 1906 if (newCand->expr->env) { 1904 1907 newCand->env.add(*newCand->expr->env); -
src/ResolvExpr/ResolveTypeof.cc
r0522ebe ra4da45e 35 35 36 36 struct ResolveTypeof : public ast::WithShortCircuiting { 37 const ResolveContext & context;37 const ResolveContext & context; 38 38 39 ResolveTypeof( const ResolveContext & context ) : 40 context( context ) {} 39 ResolveTypeof( const ResolveContext & context ) : context( context ) {} 41 40 42 void previsit( const ast::TypeofType * ) { visit_children = false; }41 void previsit( const ast::TypeofType * ) { visit_children = false; } 43 42 44 const ast::Type * postvisit( const ast::TypeofType * typeofType ) {45 // pass on null expression46 if ( ! typeofType->expr ) return typeofType;43 const ast::Type * postvisit( const ast::TypeofType * typeofType ) { 44 // pass on null expression 45 if ( ! typeofType->expr ) return typeofType; 47 46 48 ast::ptr< ast::Type > newType;49 if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) {50 // typeof wrapping type51 newType = tyExpr->type;52 } else {53 // typeof wrapping expression54 ast::TypeEnvironment dummy;55 ast::ptr< ast::Expr > newExpr =56 resolveInVoidContext( typeofType->expr, context, dummy );57 assert( newExpr->result && ! newExpr->result->isVoid() );58 newType = newExpr->result;59 }47 ast::ptr< ast::Type > newType; 48 if ( auto tyExpr = typeofType->expr.as< ast::TypeExpr >() ) { 49 // typeof wrapping type 50 newType = tyExpr->type; 51 } else { 52 // typeof wrapping expression 53 ast::TypeEnvironment dummy; 54 ast::ptr< ast::Expr > newExpr = 55 resolveInVoidContext( typeofType->expr, context, dummy ); 56 assert( newExpr->result && ! newExpr->result->isVoid() ); 57 newType = newExpr->result; 58 } 60 59 61 // clear qualifiers for base, combine with typeoftype quals regardless62 if ( typeofType->kind == ast::TypeofType::Basetypeof ) {63 // replace basetypeof(<enum>) by int64 if ( newType.as< ast::EnumInstType >() ) {65 newType = new ast::BasicType{66 ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) };67 }68 reset_qualifiers(69 newType,70 ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers );71 } else {72 add_qualifiers( newType, typeofType->qualifiers );73 }60 // clear qualifiers for base, combine with typeoftype quals regardless 61 if ( typeofType->kind == ast::TypeofType::Basetypeof ) { 62 // replace basetypeof(<enum>) by int 63 if ( newType.as< ast::EnumInstType >() ) { 64 newType = new ast::BasicType( 65 ast::BasicType::SignedInt, newType->qualifiers, copy(newType->attributes) ); 66 } 67 reset_qualifiers( 68 newType, 69 ( newType->qualifiers & ~ast::CV::EquivQualifiers ) | typeofType->qualifiers ); 70 } else { 71 add_qualifiers( newType, typeofType->qualifiers ); 72 } 74 73 75 return newType.release();76 }74 return newType.release(); 75 } 77 76 }; 78 77 … … 111 110 112 111 const ast::ObjectDecl * fixObjectType( const ast::ObjectDecl * decl , const ResolveContext & context ) { 113 if (decl->isTypeFixed) {114 return decl;115 }112 if ( decl->isTypeFixed ) { 113 return decl; 114 } 116 115 117 auto mutDecl = mutate(decl);118 fixObjectInit(decl, context);119 {120 auto resolvedType = resolveTypeof(decl->type, context);121 resolvedType = fixArrayType(resolvedType, context);122 mutDecl->type = resolvedType;123 }116 auto mutDecl = mutate(decl); 117 fixObjectInit(decl, context); 118 { 119 auto resolvedType = resolveTypeof(decl->type, context); 120 resolvedType = fixArrayType(resolvedType, context); 121 mutDecl->type = resolvedType; 122 } 124 123 125 // Do not mangle unnamed variables.126 if (!mutDecl->name.empty()) {127 mutDecl->mangleName = Mangle::mangle(mutDecl);128 }124 // Do not mangle unnamed variables. 125 if ( !mutDecl->name.empty() ) { 126 mutDecl->mangleName = Mangle::mangle(mutDecl); 127 } 129 128 130 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID);131 mutDecl->isTypeFixed = true;132 return mutDecl;129 mutDecl->type = renameTyVars(mutDecl->type, RenameMode::GEN_EXPR_ID); 130 mutDecl->isTypeFixed = true; 131 return mutDecl; 133 132 } 134 133 135 const ast::ObjectDecl *fixObjectInit( const ast::ObjectDecl *decl,136 const ResolveContext &context) {137 if (decl->isTypeFixed) {138 return decl;139 }134 const ast::ObjectDecl *fixObjectInit( 135 const ast::ObjectDecl *decl, const ResolveContext &context) { 136 if ( decl->isTypeFixed ) { 137 return decl; 138 } 140 139 141 auto mutDecl = mutate(decl); 140 if ( auto listInit = decl->init.as<ast::ListInit>() ) { 141 for ( size_t k = 0; k < listInit->designations.size(); k++ ) { 142 const ast::Designation *des = listInit->designations[k].get(); 143 // Desination here 144 ast::Designation * newDesignation = new ast::Designation(des->location); 145 std::deque<ast::ptr<ast::Expr>> newDesignators; 142 146 143 if ( auto mutListInit = mutDecl->init.as<ast::ListInit>() ) { 144 // std::list<ast::Designation *> newDesignations; 145 146 for ( size_t k = 0; k < mutListInit->designations.size(); k++ ) { 147 const ast::Designation *des = mutListInit->designations[k].get(); 148 // Desination here 149 ast::Designation * newDesignation = new ast::Designation(des->location); 150 std::deque<ast::ptr<ast::Expr>> newDesignators; 151 152 for ( ast::ptr<ast::Expr> designator : des->designators ) { 153 // Stupid flag variable for development, to be removed 154 // bool mutated = false; 155 if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) { 156 auto candidates = context.symtab.lookupId(designatorName->name); 157 // Does not work for the overloading case currently 158 // assert( candidates.size() == 1 ); 159 if ( candidates.size() != 1 ) return mutDecl; 160 auto candidate = candidates.at(0); 161 if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) { 162 // determine that is an enumInst, swap it with its const value 163 assert( candidates.size() == 1 ); 164 const ast::EnumDecl * baseEnum = enumInst->base; 165 // Need to iterate over all enum value to find the initializer to swap 166 for ( size_t m = 0; m < baseEnum->members.size(); ++m ) { 167 const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>(); 168 if ( baseEnum->members.at(m)->name == designatorName->name ) { 169 assert(mem); 170 newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) ); 171 // mutated = true; 172 break; 173 } 174 } 175 } else { 176 newDesignators.push_back( des->designators.at(0) ); 177 } 178 } else { 179 newDesignators.push_back( des->designators.at(0) ); 180 } 181 } 182 183 newDesignation->designators = newDesignators; 184 mutListInit = ast::mutate_field_index(mutListInit, &ast::ListInit::designations, k, newDesignation); 185 186 } 187 } 188 return mutDecl; 147 for ( ast::ptr<ast::Expr> designator : des->designators ) { 148 // Stupid flag variable for development, to be removed 149 if ( const ast::NameExpr * designatorName = designator.as<ast::NameExpr>() ) { 150 auto candidates = context.symtab.lookupId(designatorName->name); 151 // Does not work for the overloading case currently 152 // assert( candidates.size() == 1 ); 153 if ( candidates.size() != 1 ) return decl; 154 auto candidate = candidates.at(0); 155 if ( const ast::EnumInstType * enumInst = dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type())) { 156 // determine that is an enumInst, swap it with its const value 157 assert( candidates.size() == 1 ); 158 const ast::EnumDecl * baseEnum = enumInst->base; 159 // Need to iterate over all enum value to find the initializer to swap 160 for ( size_t m = 0; m < baseEnum->members.size(); ++m ) { 161 const ast::ObjectDecl * mem = baseEnum->members.at(m).as<const ast::ObjectDecl>(); 162 if ( baseEnum->members.at(m)->name == designatorName->name ) { 163 assert( mem ); 164 newDesignators.push_back( ast::ConstantExpr::from_int(designator->location, m) ); 165 break; 166 } 167 } 168 } else { 169 newDesignators.push_back( des->designators.at(0) ); 170 } 171 } else { 172 newDesignators.push_back( des->designators.at(0) ); 173 } 174 } 175 newDesignation->designators = newDesignators; 176 listInit = ast::mutate_field_index(listInit, &ast::ListInit::designations, k, newDesignation); 177 } 178 } 179 return decl; 189 180 } 190 181 191 } // namespace ResolvExpr182 } // namespace ResolvExpr 192 183 193 184 // Local Variables: // -
src/ResolvExpr/Resolver.cc
r0522ebe ra4da45e 50 50 51 51 namespace ResolvExpr { 52 namespace { 53 /// Finds deleted expressions in an expression tree 54 struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> { 55 const ast::DeletedExpr * result = nullptr; 56 57 void previsit( const ast::DeletedExpr * expr ) { 58 if ( result ) { visit_children = false; } 59 else { result = expr; } 52 53 namespace { 54 /// Finds deleted expressions in an expression tree 55 struct DeleteFinder final : public ast::WithShortCircuiting, public ast::WithVisitorRef<DeleteFinder> { 56 const ast::DeletedExpr * result = nullptr; 57 58 void previsit( const ast::DeletedExpr * expr ) { 59 if ( result ) { visit_children = false; } 60 else { result = expr; } 61 } 62 63 void previsit( const ast::Expr * expr ) { 64 if ( result ) { visit_children = false; } 65 if (expr->inferred.hasParams()) { 66 for (auto & imp : expr->inferred.inferParams() ) { 67 imp.second.expr->accept(*visitor); 68 } 60 69 } 61 62 void previsit( const ast::Expr * expr ) { 63 if ( result ) { visit_children = false; } 64 if (expr->inferred.hasParams()) { 65 for (auto & imp : expr->inferred.inferParams() ) { 66 imp.second.expr->accept(*visitor); 70 } 71 }; 72 73 struct ResolveDesignators final : public ast::WithShortCircuiting { 74 ResolveContext& context; 75 bool result = false; 76 77 ResolveDesignators( ResolveContext& _context ): context(_context) {}; 78 79 void previsit( const ast::Node * ) { 80 // short circuit if we already know there are designations 81 if ( result ) visit_children = false; 82 } 83 84 void previsit( const ast::Designation * des ) { 85 if ( result ) visit_children = false; 86 else if ( ! des->designators.empty() ) { 87 if ( (des->designators.size() == 1) ) { 88 const ast::Expr * designator = des->designators.at(0); 89 if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) { 90 auto candidates = context.symtab.lookupId(designatorName->name); 91 for ( auto candidate : candidates ) { 92 if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) { 93 result = true; 94 break; 95 } 96 } 97 } 98 } 99 visit_children = false; 100 } 101 } 102 }; 103 } // anonymous namespace 104 105 /// Check if this expression is or includes a deleted expression 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 162 winners.clear(); 163 seen_undeleted = true; 164 } 165 } 166 167 winners.emplace_back( std::move( cand ) ); 168 } 169 170 // promote candidate.cvtCost to .cost 171 // promoteCvtCost( winners ); 172 173 // produce ambiguous errors, if applicable 174 if ( winners.size() != 1 ) { 175 std::ostringstream stream; 176 stream << "Cannot choose between " << winners.size() << " alternatives for " 177 << kind << (kind != "" ? " " : "") << "expression\n"; 178 ast::print( stream, untyped ); 179 stream << " Alternatives are:\n"; 180 print( stream, winners, 1 ); 181 SemanticError( untyped->location, stream.str() ); 182 } 183 184 // single selected choice 185 CandidateRef & choice = winners.front(); 186 187 // fail on only expression deleted 188 if ( ! seen_undeleted ) { 189 SemanticError( untyped->location, choice->expr.get(), "Unique best alternative " 190 "includes deleted identifier in " ); 191 } 192 193 return std::move( choice ); 194 } 195 196 /// Strips extraneous casts out of an expression 197 struct StripCasts final { 198 const ast::Expr * postvisit( const ast::CastExpr * castExpr ) { 199 if ( 200 castExpr->isGenerated == ast::GeneratedCast 201 && typesCompatible( castExpr->arg->result, castExpr->result ) 202 ) { 203 // generated cast is the same type as its argument, remove it after keeping env 204 return ast::mutate_field( 205 castExpr->arg.get(), &ast::Expr::env, castExpr->env ); 206 } 207 return castExpr; 208 } 209 210 static void strip( ast::ptr< ast::Expr > & expr ) { 211 ast::Pass< StripCasts > stripper; 212 expr = expr->accept( stripper ); 213 } 214 }; 215 216 /// Swaps argument into expression pointer, saving original environment 217 void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) { 218 ast::ptr< ast::TypeSubstitution > env = expr->env; 219 expr.set_and_mutate( newExpr )->env = env; 220 } 221 222 /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts) 223 void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) { 224 if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) { 225 if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) { 226 // cast is to the same type as its argument, remove it 227 swap_and_save_env( expr, castExpr->arg ); 228 } 229 } 230 } 231 232 } // anonymous namespace 233 234 /// Establish post-resolver invariants for expressions 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" ) ); 468 } 469 } 470 return mutAttr; 471 } else if (attr->params.size() > 1) { 472 SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) ); 473 } else { 474 SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) ); 475 } 476 } 477 return attr; 478 } 479 } 480 481 const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) { 482 GuardValue( functionReturn ); 483 484 assert (functionDecl->unique()); 485 if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) { 486 SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations"); 487 } 488 489 if (!functionDecl->isTypeFixed) { 490 auto mutDecl = mutate(functionDecl); 491 auto mutType = mutDecl->type.get_and_mutate(); 492 493 for (auto & attr: mutDecl->attributes) { 494 attr = handleAttribute(mutDecl->location, attr, context ); 495 } 496 497 // handle assertions 498 499 symtab.enterScope(); 500 mutType->forall.clear(); 501 mutType->assertions.clear(); 502 for (auto & typeParam : mutDecl->type_params) { 503 symtab.addType(typeParam); 504 mutType->forall.emplace_back(new ast::TypeInstType(typeParam)); 505 } 506 for (auto & asst : mutDecl->assertions) { 507 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context); 508 symtab.addId(asst); 509 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst)); 510 } 511 512 // temporarily adds params to symbol table. 513 // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl) 514 515 std::vector<ast::ptr<ast::Type>> paramTypes; 516 std::vector<ast::ptr<ast::Type>> returnTypes; 517 518 for (auto & param : mutDecl->params) { 519 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context); 520 symtab.addId(param); 521 paramTypes.emplace_back(param->get_type()); 522 } 523 for (auto & ret : mutDecl->returns) { 524 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context); 525 returnTypes.emplace_back(ret->get_type()); 526 } 527 // since function type in decl is just a view of param types, need to update that as well 528 mutType->params = std::move(paramTypes); 529 mutType->returns = std::move(returnTypes); 530 531 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID)); 532 533 std::list<ast::ptr<ast::Stmt>> newStmts; 534 resolveWithExprs (mutDecl->withExprs, newStmts); 535 536 if (mutDecl->stmts) { 537 auto mutStmt = mutDecl->stmts.get_and_mutate(); 538 mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts)); 539 mutDecl->stmts = mutStmt; 540 } 541 542 symtab.leaveScope(); 543 544 mutDecl->type = renamedType; 545 mutDecl->mangleName = Mangle::mangle(mutDecl); 546 mutDecl->isTypeFixed = true; 547 functionDecl = mutDecl; 548 } 549 managedTypes.handleDWT(functionDecl); 550 551 functionReturn = extractResultType( functionDecl->type ); 552 return functionDecl; 553 } 554 555 const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) { 556 // default value expressions have an environment which shouldn't be there and trips up 557 // later passes. 558 assert( functionDecl->unique() ); 559 ast::FunctionType * mutType = mutate( functionDecl->type.get() ); 560 561 for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) { 562 if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) { 563 if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) { 564 if ( init->value->env == nullptr ) continue; 565 // clone initializer minus the initializer environment 566 auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() ); 567 auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() ); 568 auto mutValue = mutate( mutInit->value.get() ); 569 570 mutValue->env = nullptr; 571 mutInit->value = mutValue; 572 mutParam->init = mutInit; 573 mutType->params[i] = mutParam; 574 575 assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env); 576 } 577 } 578 } 579 mutate_field(functionDecl, &ast::FunctionDecl::type, mutType); 580 return functionDecl; 581 } 582 583 const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) { 584 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 585 // class-variable `initContext` is changed multiple times because the LHS is analyzed 586 // twice. The second analysis changes `initContext` because a function type can contain 587 // object declarations in the return and parameter types. Therefore each value of 588 // `initContext` is retained so the type on the first analysis is preserved and used for 589 // selecting the RHS. 590 GuardValue( currentObject ); 591 592 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) { 593 // enumerator initializers should not use the enum type to initialize, since the 594 // enum type is still incomplete at this point. Use `int` instead. 595 596 if ( auto enumBase = dynamic_cast< const ast::EnumInstType * > 597 ( objectDecl->get_type() )->base->base ) { 598 objectDecl = fixObjectType( objectDecl, context ); 599 currentObject = ast::CurrentObject{ 600 objectDecl->location, 601 enumBase 602 }; 603 } else { 604 objectDecl = fixObjectType( objectDecl, context ); 605 currentObject = ast::CurrentObject{ 606 objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } }; 607 } 608 } else { 609 if ( !objectDecl->isTypeFixed ) { 610 auto newDecl = fixObjectType(objectDecl, context); 611 auto mutDecl = mutate(newDecl); 612 613 // generate CtorInit wrapper when necessary. 614 // in certain cases, fixObjectType is called before reaching 615 // this object in visitor pass, thus disabling CtorInit codegen. 616 // this happens on aggregate members and function parameters. 617 if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) { 618 // constructed objects cannot be designated 619 if ( InitTweak::isDesignated( mutDecl->init ) ) { 620 ast::Pass<ResolveDesignators> res( context ); 621 maybe_accept( mutDecl->init.get(), res ); 622 if ( !res.core.result ) { 623 SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n" 624 "If this is really what you want, initialize with @=." ); 625 } 626 } 627 // constructed objects should not have initializers nested too deeply 628 if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " ); 629 630 mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl ); 631 } 632 633 objectDecl = mutDecl; 634 } 635 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 636 } 637 638 return objectDecl; 639 } 640 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 ); 67 1171 } 68 1172 } 69 1173 } 70 }; 71 72 struct ResolveDesignators final : public ast::WithShortCircuiting { 73 ResolveContext& context; 74 bool result = false; 75 76 ResolveDesignators( ResolveContext& _context ): context{_context} {}; 77 78 void previsit( const ast::Node * ) { 79 // short circuit if we already know there are designations 80 if ( result ) visit_children = false; 81 } 82 83 void previsit( const ast::Designation * des ) { 84 if ( result ) visit_children = false; 85 else if ( ! des->designators.empty() ) { 86 if ( (des->designators.size() == 1) ) { 87 const ast::Expr * designator = des->designators.at(0); 88 if ( const ast::NameExpr * designatorName = dynamic_cast<const ast::NameExpr *>(designator) ) { 89 auto candidates = context.symtab.lookupId(designatorName->name); 90 for ( auto candidate : candidates ) { 91 if ( dynamic_cast<const ast::EnumInstType *>(candidate.id->get_type()) ) { 92 result = true; 93 break; 94 } 95 } 96 } 97 } 98 visit_children = false; 99 } 100 } 101 }; 102 } // anonymous namespace 103 /// Check if this expression is or includes a deleted expression 104 const ast::DeletedExpr * findDeletedExpr( const ast::Expr * expr ) { 105 return ast::Pass<DeleteFinder>::read( expr ); 106 } 107 108 namespace { 109 /// always-accept candidate filter 110 bool anyCandidate( const Candidate & ) { return true; } 111 112 /// Calls the CandidateFinder and finds the single best candidate 113 CandidateRef findUnfinishedKindExpression( 114 const ast::Expr * untyped, const ResolveContext & context, const std::string & kind, 115 std::function<bool(const Candidate &)> pred = anyCandidate, ResolveMode mode = {} 116 ) { 117 if ( ! untyped ) return nullptr; 118 119 // xxx - this isn't thread-safe, but should work until we parallelize the resolver 120 static unsigned recursion_level = 0; 121 122 ++recursion_level; 123 ast::TypeEnvironment env; 124 CandidateFinder finder( context, env ); 125 finder.allowVoid = true; 126 finder.find( untyped, recursion_level == 1 ? mode.atTopLevel() : mode ); 127 --recursion_level; 128 129 // produce a filtered list of candidates 130 CandidateList candidates; 131 for ( auto & cand : finder.candidates ) { 132 if ( pred( *cand ) ) { candidates.emplace_back( cand ); } 133 } 134 135 // produce invalid error if no candidates 136 if ( candidates.empty() ) { 137 SemanticError( untyped, 138 toString( "No reasonable alternatives for ", kind, (kind != "" ? " " : ""), 139 "expression: ") ); 140 } 141 142 // search for cheapest candidate 143 CandidateList winners; 144 bool seen_undeleted = false; 145 for ( CandidateRef & cand : candidates ) { 146 int c = winners.empty() ? -1 : cand->cost.compare( winners.front()->cost ); 147 148 if ( c > 0 ) continue; // skip more expensive than winner 149 150 if ( c < 0 ) { 151 // reset on new cheapest 152 seen_undeleted = ! findDeletedExpr( cand->expr ); 153 winners.clear(); 154 } else /* if ( c == 0 ) */ { 155 if ( findDeletedExpr( cand->expr ) ) { 156 // skip deleted expression if already seen one equivalent-cost not 157 if ( seen_undeleted ) continue; 158 } else if ( ! seen_undeleted ) { 159 // replace list of equivalent-cost deleted expressions with one non-deleted 160 winners.clear(); 161 seen_undeleted = true; 162 } 163 } 164 165 winners.emplace_back( std::move( cand ) ); 166 } 167 168 // promote candidate.cvtCost to .cost 169 // promoteCvtCost( winners ); 170 171 // produce ambiguous errors, if applicable 172 if ( winners.size() != 1 ) { 173 std::ostringstream stream; 174 stream << "Cannot choose between " << winners.size() << " alternatives for " 175 << kind << (kind != "" ? " " : "") << "expression\n"; 176 ast::print( stream, untyped ); 177 stream << " Alternatives are:\n"; 178 print( stream, winners, 1 ); 179 SemanticError( untyped->location, stream.str() ); 180 } 181 182 // single selected choice 183 CandidateRef & choice = winners.front(); 184 185 // fail on only expression deleted 186 if ( ! seen_undeleted ) { 187 SemanticError( untyped->location, choice->expr.get(), "Unique best alternative " 188 "includes deleted identifier in " ); 189 } 190 191 return std::move( choice ); 192 } 193 194 /// Strips extraneous casts out of an expression 195 struct StripCasts final { 196 const ast::Expr * postvisit( const ast::CastExpr * castExpr ) { 197 if ( 198 castExpr->isGenerated == ast::GeneratedCast 199 && typesCompatible( castExpr->arg->result, castExpr->result ) 200 ) { 201 // generated cast is the same type as its argument, remove it after keeping env 202 return ast::mutate_field( 203 castExpr->arg.get(), &ast::Expr::env, castExpr->env ); 204 } 205 return castExpr; 206 } 207 208 static void strip( ast::ptr< ast::Expr > & expr ) { 209 ast::Pass< StripCasts > stripper; 210 expr = expr->accept( stripper ); 211 } 212 }; 213 214 /// Swaps argument into expression pointer, saving original environment 215 void swap_and_save_env( ast::ptr< ast::Expr > & expr, const ast::Expr * newExpr ) { 216 ast::ptr< ast::TypeSubstitution > env = expr->env; 217 expr.set_and_mutate( newExpr )->env = env; 218 } 219 220 /// Removes cast to type of argument (unlike StripCasts, also handles non-generated casts) 221 void removeExtraneousCast( ast::ptr<ast::Expr> & expr ) { 222 if ( const ast::CastExpr * castExpr = expr.as< ast::CastExpr >() ) { 223 if ( typesCompatible( castExpr->arg->result, castExpr->result ) ) { 224 // cast is to the same type as its argument, remove it 225 swap_and_save_env( expr, castExpr->arg ); 226 } 227 } 228 } 229 230 231 } // anonymous namespace 232 /// Establish post-resolver invariants for expressions 233 void finishExpr( 234 ast::ptr< ast::Expr > & expr, const ast::TypeEnvironment & env, 235 const ast::TypeSubstitution * oldenv = nullptr 236 ) { 237 // set up new type substitution for expression 238 ast::ptr< ast::TypeSubstitution > newenv = 239 oldenv ? oldenv : new ast::TypeSubstitution{}; 240 env.writeToSubstitution( *newenv.get_and_mutate() ); 241 expr.get_and_mutate()->env = std::move( newenv ); 242 // remove unncecessary casts 243 StripCasts::strip( expr ); 244 } 245 246 ast::ptr< ast::Expr > resolveInVoidContext( 247 const ast::Expr * expr, const ResolveContext & context, 248 ast::TypeEnvironment & env 249 ) { 250 assertf( expr, "expected a non-null expression" ); 251 252 // set up and resolve expression cast to void 253 ast::ptr< ast::CastExpr > untyped = new ast::CastExpr{ expr }; 254 CandidateRef choice = findUnfinishedKindExpression( 255 untyped, context, "", anyCandidate, ResolveMode::withAdjustment() ); 256 257 // a cast expression has either 0 or 1 interpretations (by language rules); 258 // if 0, an exception has already been thrown, and this code will not run 259 const ast::CastExpr * castExpr = choice->expr.strict_as< ast::CastExpr >(); 260 env = std::move( choice->env ); 261 262 return castExpr->arg; 263 } 264 265 /// Resolve `untyped` to the expression whose candidate is the best match for a `void` 266 /// context. 267 ast::ptr< ast::Expr > findVoidExpression( 268 const ast::Expr * untyped, const ResolveContext & context 269 ) { 270 ast::TypeEnvironment env; 271 ast::ptr< ast::Expr > newExpr = resolveInVoidContext( untyped, context, env ); 272 finishExpr( newExpr, env, untyped->env ); 273 return newExpr; 274 } 275 276 namespace { 277 278 279 /// resolve `untyped` to the expression whose candidate satisfies `pred` with the 280 /// lowest cost, returning the resolved version 281 ast::ptr< ast::Expr > findKindExpression( 282 const ast::Expr * untyped, const ResolveContext & context, 283 std::function<bool(const Candidate &)> pred = anyCandidate, 284 const std::string & kind = "", ResolveMode mode = {} 285 ) { 286 if ( ! untyped ) return {}; 287 CandidateRef choice = 288 findUnfinishedKindExpression( untyped, context, kind, pred, mode ); 289 ResolvExpr::finishExpr( choice->expr, choice->env, untyped->env ); 290 return std::move( choice->expr ); 291 } 292 293 /// Resolve `untyped` to the single expression whose candidate is the best match 294 ast::ptr< ast::Expr > findSingleExpression( 295 const ast::Expr * untyped, const ResolveContext & context 296 ) { 297 Stats::ResolveTime::start( untyped ); 298 auto res = findKindExpression( untyped, context ); 299 Stats::ResolveTime::stop(); 300 return res; 301 } 302 } // anonymous namespace 303 304 ast::ptr< ast::Expr > findSingleExpression( 305 const ast::Expr * untyped, const ast::Type * type, 306 const ResolveContext & context 307 ) { 308 assert( untyped && type ); 309 ast::ptr< ast::Expr > castExpr = new ast::CastExpr{ untyped, type }; 310 ast::ptr< ast::Expr > newExpr = findSingleExpression( castExpr, context ); 311 removeExtraneousCast( newExpr ); 312 return newExpr; 313 } 314 315 namespace { 316 bool structOrUnion( const Candidate & i ) { 317 const ast::Type * t = i.expr->result->stripReferences(); 318 return dynamic_cast< const ast::StructInstType * >( t ) || dynamic_cast< const ast::UnionInstType * >( t ); 319 } 320 /// Predicate for "Candidate has integral type" 321 bool hasIntegralType( const Candidate & i ) { 322 const ast::Type * type = i.expr->result; 323 324 if ( auto bt = dynamic_cast< const ast::BasicType * >( type ) ) { 325 return bt->isInteger(); 326 } else if ( 327 dynamic_cast< const ast::EnumInstType * >( type ) 328 || dynamic_cast< const ast::ZeroType * >( type ) 329 || dynamic_cast< const ast::OneType * >( type ) 330 ) { 331 return true; 332 } else return false; 333 } 334 335 /// Resolve `untyped` as an integral expression, returning the resolved version 336 ast::ptr< ast::Expr > findIntegralExpression( 337 const ast::Expr * untyped, const ResolveContext & context 338 ) { 339 return findKindExpression( untyped, context, hasIntegralType, "condition" ); 340 } 341 342 /// check if a type is a character type 343 bool isCharType( const ast::Type * t ) { 344 if ( auto bt = dynamic_cast< const ast::BasicType * >( t ) ) { 345 return bt->kind == ast::BasicType::Char 346 || bt->kind == ast::BasicType::SignedChar 347 || bt->kind == ast::BasicType::UnsignedChar; 348 } 1174 } 1175 } 1176 1177 // move cursor to next object in preparation for next initializer 1178 currentObject.increment(); 1179 1180 // set initializer expression to resolved expression 1181 return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) ); 1182 } 1183 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; 349 1237 return false; 350 1238 } 351 352 /// Advance a type itertor to the next mutex parameter 353 template<typename Iter> 354 inline bool nextMutex( Iter & it, const Iter & end ) { 355 while ( it != end && ! (*it)->is_mutex() ) { ++it; } 356 return it != end; 357 } 358 } 359 360 class Resolver final 361 : public ast::WithSymbolTable, public ast::WithGuards, 362 public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting, 363 public ast::WithStmtsToAdd<> { 364 365 ast::ptr< ast::Type > functionReturn = nullptr; 366 ast::CurrentObject currentObject; 367 // for work previously in GenInit 368 static InitTweak::ManagedTypes managedTypes; 369 ResolveContext context; 370 371 bool inEnumDecl = false; 372 373 public: 374 static size_t traceId; 375 Resolver( const ast::TranslationGlobal & global ) : 376 ast::WithSymbolTable(ast::SymbolTable::ErrorDetection::ValidateOnAdd), 377 context{ symtab, global } {} 378 Resolver( const ResolveContext & context ) : 379 ast::WithSymbolTable{ context.symtab }, 380 context{ symtab, context.global } {} 381 382 const ast::FunctionDecl * previsit( const ast::FunctionDecl * ); 383 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * ); 384 const ast::ObjectDecl * previsit( const ast::ObjectDecl * ); 385 void previsit( const ast::AggregateDecl * ); 386 void previsit( const ast::StructDecl * ); 387 void previsit( const ast::EnumDecl * ); 388 const ast::StaticAssertDecl * previsit( const ast::StaticAssertDecl * ); 389 390 const ast::ArrayType * previsit( const ast::ArrayType * ); 391 const ast::PointerType * previsit( const ast::PointerType * ); 392 393 const ast::ExprStmt * previsit( const ast::ExprStmt * ); 394 const ast::AsmExpr * previsit( const ast::AsmExpr * ); 395 const ast::AsmStmt * previsit( const ast::AsmStmt * ); 396 const ast::IfStmt * previsit( const ast::IfStmt * ); 397 const ast::WhileDoStmt * previsit( const ast::WhileDoStmt * ); 398 const ast::ForStmt * previsit( const ast::ForStmt * ); 399 const ast::SwitchStmt * previsit( const ast::SwitchStmt * ); 400 const ast::CaseClause * previsit( const ast::CaseClause * ); 401 const ast::BranchStmt * previsit( const ast::BranchStmt * ); 402 const ast::ReturnStmt * previsit( const ast::ReturnStmt * ); 403 const ast::ThrowStmt * previsit( const ast::ThrowStmt * ); 404 const ast::CatchClause * previsit( const ast::CatchClause * ); 405 const ast::CatchClause * postvisit( const ast::CatchClause * ); 406 const ast::WaitForStmt * previsit( const ast::WaitForStmt * ); 407 const ast::WithStmt * previsit( const ast::WithStmt * ); 408 409 const ast::SingleInit * previsit( const ast::SingleInit * ); 410 const ast::ListInit * previsit( const ast::ListInit * ); 411 const ast::ConstructorInit * previsit( const ast::ConstructorInit * ); 412 413 void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd); 414 415 void beginScope() { managedTypes.beginScope(); } 416 void endScope() { managedTypes.endScope(); } 417 bool on_error(ast::ptr<ast::Decl> & decl); 418 }; 419 // size_t Resolver::traceId = Stats::Heap::new_stacktrace_id("Resolver"); 420 421 InitTweak::ManagedTypes Resolver::managedTypes; 422 423 void resolve( ast::TranslationUnit& translationUnit ) { 424 ast::Pass< Resolver >::run( translationUnit, translationUnit.global ); 425 } 426 427 ast::ptr< ast::Init > resolveCtorInit( 428 const ast::ConstructorInit * ctorInit, const ResolveContext & context 429 ) { 430 assert( ctorInit ); 431 ast::Pass< Resolver > resolver( context ); 432 return ctorInit->accept( resolver ); 433 } 434 435 const ast::Expr * resolveStmtExpr( 436 const ast::StmtExpr * stmtExpr, const ResolveContext & context 437 ) { 438 assert( stmtExpr ); 439 ast::Pass< Resolver > resolver( context ); 440 auto ret = mutate(stmtExpr->accept(resolver)); 441 strict_dynamic_cast< ast::StmtExpr * >( ret )->computeResult(); 442 return ret; 443 } 444 445 namespace { 446 const ast::Attribute * handleAttribute(const CodeLocation & loc, const ast::Attribute * attr, const ResolveContext & context) { 447 std::string name = attr->normalizedName(); 448 if (name == "constructor" || name == "destructor") { 449 if (attr->params.size() == 1) { 450 auto arg = attr->params.front(); 451 auto resolved = ResolvExpr::findSingleExpression( arg, new ast::BasicType( ast::BasicType::LongLongSignedInt ), context ); 452 auto result = eval(arg); 453 454 auto mutAttr = mutate(attr); 455 mutAttr->params.front() = resolved; 456 if (! result.hasKnownValue) { 457 SemanticWarning(loc, Warning::GccAttributes, 458 toCString( name, " priorities must be integers from 0 to 65535 inclusive: ", arg ) ); 459 } 460 else { 461 auto priority = result.knownValue; 462 if (priority < 101) { 463 SemanticWarning(loc, Warning::GccAttributes, 464 toCString( name, " priorities from 0 to 100 are reserved for the implementation" ) ); 465 } else if (priority < 201 && ! buildingLibrary()) { 466 SemanticWarning(loc, Warning::GccAttributes, 467 toCString( name, " priorities from 101 to 200 are reserved for the implementation" ) ); 468 } 469 } 470 return mutAttr; 471 } else if (attr->params.size() > 1) { 472 SemanticWarning(loc, Warning::GccAttributes, toCString( "too many arguments to ", name, " attribute" ) ); 473 } else { 474 SemanticWarning(loc, Warning::GccAttributes, toCString( "too few arguments to ", name, " attribute" ) ); 475 } 476 } 477 return attr; 478 } 479 } 480 481 const ast::FunctionDecl * Resolver::previsit( const ast::FunctionDecl * functionDecl ) { 482 GuardValue( functionReturn ); 483 484 assert (functionDecl->unique()); 485 if (!functionDecl->has_body() && !functionDecl->withExprs.empty()) { 486 SemanticError(functionDecl->location, functionDecl, "Function without body has with declarations"); 487 } 488 489 if (!functionDecl->isTypeFixed) { 490 auto mutDecl = mutate(functionDecl); 491 auto mutType = mutDecl->type.get_and_mutate(); 492 493 for (auto & attr: mutDecl->attributes) { 494 attr = handleAttribute(mutDecl->location, attr, context ); 495 } 496 497 // handle assertions 498 499 symtab.enterScope(); 500 mutType->forall.clear(); 501 mutType->assertions.clear(); 502 for (auto & typeParam : mutDecl->type_params) { 503 symtab.addType(typeParam); 504 mutType->forall.emplace_back(new ast::TypeInstType(typeParam)); 505 } 506 for (auto & asst : mutDecl->assertions) { 507 asst = fixObjectType(asst.strict_as<ast::ObjectDecl>(), context); 508 symtab.addId(asst); 509 mutType->assertions.emplace_back(new ast::VariableExpr(functionDecl->location, asst)); 510 } 511 512 // temporarily adds params to symbol table. 513 // actual scoping rules for params and withexprs differ - see Pass::visit(FunctionDecl) 514 515 std::vector<ast::ptr<ast::Type>> paramTypes; 516 std::vector<ast::ptr<ast::Type>> returnTypes; 517 518 for (auto & param : mutDecl->params) { 519 param = fixObjectType(param.strict_as<ast::ObjectDecl>(), context); 520 symtab.addId(param); 521 paramTypes.emplace_back(param->get_type()); 522 } 523 for (auto & ret : mutDecl->returns) { 524 ret = fixObjectType(ret.strict_as<ast::ObjectDecl>(), context); 525 returnTypes.emplace_back(ret->get_type()); 526 } 527 // since function type in decl is just a view of param types, need to update that as well 528 mutType->params = std::move(paramTypes); 529 mutType->returns = std::move(returnTypes); 530 531 auto renamedType = strict_dynamic_cast<const ast::FunctionType *>(renameTyVars(mutType, RenameMode::GEN_EXPR_ID)); 532 533 std::list<ast::ptr<ast::Stmt>> newStmts; 534 resolveWithExprs (mutDecl->withExprs, newStmts); 535 536 if (mutDecl->stmts) { 537 auto mutStmt = mutDecl->stmts.get_and_mutate(); 538 mutStmt->kids.splice(mutStmt->kids.begin(), std::move(newStmts)); 539 mutDecl->stmts = mutStmt; 540 } 541 542 symtab.leaveScope(); 543 544 mutDecl->type = renamedType; 545 mutDecl->mangleName = Mangle::mangle(mutDecl); 546 mutDecl->isTypeFixed = true; 547 functionDecl = mutDecl; 548 } 549 managedTypes.handleDWT(functionDecl); 550 551 functionReturn = extractResultType( functionDecl->type ); 552 return functionDecl; 553 } 554 555 const ast::FunctionDecl * Resolver::postvisit( const ast::FunctionDecl * functionDecl ) { 556 // default value expressions have an environment which shouldn't be there and trips up 557 // later passes. 558 assert( functionDecl->unique() ); 559 ast::FunctionType * mutType = mutate( functionDecl->type.get() ); 560 561 for ( unsigned i = 0 ; i < mutType->params.size() ; ++i ) { 562 if ( const ast::ObjectDecl * obj = mutType->params[i].as< ast::ObjectDecl >() ) { 563 if ( const ast::SingleInit * init = obj->init.as< ast::SingleInit >() ) { 564 if ( init->value->env == nullptr ) continue; 565 // clone initializer minus the initializer environment 566 auto mutParam = mutate( mutType->params[i].strict_as< ast::ObjectDecl >() ); 567 auto mutInit = mutate( mutParam->init.strict_as< ast::SingleInit >() ); 568 auto mutValue = mutate( mutInit->value.get() ); 569 570 mutValue->env = nullptr; 571 mutInit->value = mutValue; 572 mutParam->init = mutInit; 573 mutType->params[i] = mutParam; 574 575 assert( ! mutType->params[i].strict_as< ast::ObjectDecl >()->init.strict_as< ast::SingleInit >()->value->env); 576 } 577 } 578 } 579 mutate_field(functionDecl, &ast::FunctionDecl::type, mutType); 580 return functionDecl; 581 } 582 583 const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) { 584 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], 585 // class-variable `initContext` is changed multiple times because the LHS is analyzed 586 // twice. The second analysis changes `initContext` because a function type can contain 587 // object declarations in the return and parameter types. Therefore each value of 588 // `initContext` is retained so the type on the first analysis is preserved and used for 589 // selecting the RHS. 590 GuardValue( currentObject ); 591 592 if ( inEnumDecl && dynamic_cast< const ast::EnumInstType * >( objectDecl->get_type() ) ) { 593 // enumerator initializers should not use the enum type to initialize, since the 594 // enum type is still incomplete at this point. Use `int` instead. 595 596 if ( auto enumBase = dynamic_cast< const ast::EnumInstType * > 597 ( objectDecl->get_type() )->base->base ) { 598 objectDecl = fixObjectType( objectDecl, context ); 599 currentObject = ast::CurrentObject{ 600 objectDecl->location, 601 enumBase 602 }; 603 } else { 604 objectDecl = fixObjectType( objectDecl, context ); 605 currentObject = ast::CurrentObject{ 606 objectDecl->location, new ast::BasicType{ ast::BasicType::SignedInt } }; 607 } 608 609 } 610 else { 611 if ( !objectDecl->isTypeFixed ) { 612 auto newDecl = fixObjectType(objectDecl, context); 613 auto mutDecl = mutate(newDecl); 614 615 // generate CtorInit wrapper when necessary. 616 // in certain cases, fixObjectType is called before reaching 617 // this object in visitor pass, thus disabling CtorInit codegen. 618 // this happens on aggregate members and function parameters. 619 if ( InitTweak::tryConstruct( mutDecl ) && ( managedTypes.isManaged( mutDecl ) || ((! isInFunction() || mutDecl->storage.is_static ) && ! InitTweak::isConstExpr( mutDecl->init ) ) ) ) { 620 // constructed objects cannot be designated 621 if ( InitTweak::isDesignated( mutDecl->init ) ) { 622 ast::Pass<ResolveDesignators> res( context ); 623 maybe_accept( mutDecl->init.get(), res ); 624 if ( !res.core.result ) { 625 SemanticError( mutDecl, "Cannot include designations in the initializer for a managed Object.\n" 626 "If this is really what you want, initialize with @=." ); 627 } 628 } 629 // constructed objects should not have initializers nested too deeply 630 if ( ! InitTweak::checkInitDepth( mutDecl ) ) SemanticError( mutDecl, "Managed object's initializer is too deep " ); 631 632 mutDecl->init = InitTweak::genCtorInit( mutDecl->location, mutDecl ); 633 } 634 635 objectDecl = mutDecl; 636 } 637 currentObject = ast::CurrentObject{ objectDecl->location, objectDecl->get_type() }; 638 } 639 640 return objectDecl; 641 } 642 643 void Resolver::previsit( const ast::AggregateDecl * _aggDecl ) { 644 auto aggDecl = mutate(_aggDecl); 645 assertf(aggDecl == _aggDecl, "type declarations must be unique"); 646 647 for (auto & member: aggDecl->members) { 648 // nested type decls are hoisted already. no need to do anything 649 if (auto obj = member.as<ast::ObjectDecl>()) { 650 member = fixObjectType(obj, context); 651 } 652 } 653 } 654 655 void Resolver::previsit( const ast::StructDecl * structDecl ) { 656 previsit(static_cast<const ast::AggregateDecl *>(structDecl)); 657 managedTypes.handleStruct(structDecl); 658 } 659 660 void Resolver::previsit( const ast::EnumDecl * ) { 661 // in case we decide to allow nested enums 662 GuardValue( inEnumDecl ); 663 inEnumDecl = true; 664 // don't need to fix types for enum fields 665 } 666 667 const ast::StaticAssertDecl * Resolver::previsit( 668 const ast::StaticAssertDecl * assertDecl 669 ) { 670 return ast::mutate_field( 671 assertDecl, &ast::StaticAssertDecl::cond, 672 findIntegralExpression( assertDecl->cond, context ) ); 673 } 674 675 template< typename PtrType > 676 const PtrType * handlePtrType( const PtrType * type, const ResolveContext & context ) { 677 if ( type->dimension ) { 678 const ast::Type * sizeType = context.global.sizeType.get(); 679 ast::ptr< ast::Expr > dimension = findSingleExpression( type->dimension, sizeType, context ); 680 assertf(dimension->env->empty(), "array dimension expr has nonempty env"); 681 dimension.get_and_mutate()->env = nullptr; 682 ast::mutate_field( type, &PtrType::dimension, dimension ); 683 } 684 return type; 685 } 686 687 const ast::ArrayType * Resolver::previsit( const ast::ArrayType * at ) { 688 return handlePtrType( at, context ); 689 } 690 691 const ast::PointerType * Resolver::previsit( const ast::PointerType * pt ) { 692 return handlePtrType( pt, context ); 693 } 694 695 const ast::ExprStmt * Resolver::previsit( const ast::ExprStmt * exprStmt ) { 696 visit_children = false; 697 assertf( exprStmt->expr, "ExprStmt has null expression in resolver" ); 698 699 return ast::mutate_field( 700 exprStmt, &ast::ExprStmt::expr, findVoidExpression( exprStmt->expr, context ) ); 701 } 702 703 const ast::AsmExpr * Resolver::previsit( const ast::AsmExpr * asmExpr ) { 704 visit_children = false; 705 706 asmExpr = ast::mutate_field( 707 asmExpr, &ast::AsmExpr::operand, findVoidExpression( asmExpr->operand, context ) ); 708 709 return asmExpr; 710 } 711 712 const ast::AsmStmt * Resolver::previsit( const ast::AsmStmt * asmStmt ) { 713 visitor->maybe_accept( asmStmt, &ast::AsmStmt::input ); 714 visitor->maybe_accept( asmStmt, &ast::AsmStmt::output ); 715 visit_children = false; 716 return asmStmt; 717 } 718 719 const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) { 720 return ast::mutate_field( 721 ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) ); 722 } 723 724 const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) { 725 return ast::mutate_field( 726 whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) ); 727 } 728 729 const ast::ForStmt * Resolver::previsit( const ast::ForStmt * forStmt ) { 730 if ( forStmt->cond ) { 731 forStmt = ast::mutate_field( 732 forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) ); 733 } 734 735 if ( forStmt->inc ) { 736 forStmt = ast::mutate_field( 737 forStmt, &ast::ForStmt::inc, findVoidExpression( forStmt->inc, context ) ); 738 } 739 740 return forStmt; 741 } 742 743 const ast::SwitchStmt * Resolver::previsit( const ast::SwitchStmt * switchStmt ) { 744 GuardValue( currentObject ); 745 switchStmt = ast::mutate_field( 746 switchStmt, &ast::SwitchStmt::cond, 747 findIntegralExpression( switchStmt->cond, context ) ); 748 currentObject = ast::CurrentObject{ switchStmt->location, switchStmt->cond->result }; 749 return switchStmt; 750 } 751 752 const ast::CaseClause * Resolver::previsit( const ast::CaseClause * caseStmt ) { 753 if ( caseStmt->cond ) { 754 std::deque< ast::InitAlternative > initAlts = currentObject.getOptions(); 755 assertf( initAlts.size() == 1, "SwitchStmt did not correctly resolve an integral " 756 "expression." ); 757 758 ast::ptr< ast::Expr > untyped = 759 new ast::CastExpr{ caseStmt->location, caseStmt->cond, initAlts.front().type }; 760 ast::ptr< ast::Expr > newExpr = findSingleExpression( untyped, context ); 761 762 // case condition cannot have a cast in C, so it must be removed here, regardless of 763 // whether it would perform a conversion. 764 if ( const ast::CastExpr * castExpr = newExpr.as< ast::CastExpr >() ) { 765 swap_and_save_env( newExpr, castExpr->arg ); 766 } 767 768 caseStmt = ast::mutate_field( caseStmt, &ast::CaseClause::cond, newExpr ); 769 } 770 return caseStmt; 771 } 772 773 const ast::BranchStmt * Resolver::previsit( const ast::BranchStmt * branchStmt ) { 774 visit_children = false; 775 // must resolve the argument of a computed goto 776 if ( branchStmt->kind == ast::BranchStmt::Goto && branchStmt->computedTarget ) { 777 // computed goto argument is void* 778 ast::ptr< ast::Type > target = new ast::PointerType{ new ast::VoidType{} }; 779 branchStmt = ast::mutate_field( 780 branchStmt, &ast::BranchStmt::computedTarget, 781 findSingleExpression( branchStmt->computedTarget, target, context ) ); 782 } 783 return branchStmt; 784 } 785 786 const ast::ReturnStmt * Resolver::previsit( const ast::ReturnStmt * returnStmt ) { 787 visit_children = false; 788 if ( returnStmt->expr ) { 789 returnStmt = ast::mutate_field( 790 returnStmt, &ast::ReturnStmt::expr, 791 findSingleExpression( returnStmt->expr, functionReturn, context ) ); 792 } 793 return returnStmt; 794 } 795 796 const ast::ThrowStmt * Resolver::previsit( const ast::ThrowStmt * throwStmt ) { 797 visit_children = false; 798 if ( throwStmt->expr ) { 799 const ast::StructDecl * exceptionDecl = 800 symtab.lookupStruct( "__cfaehm_base_exception_t" ); 801 assert( exceptionDecl ); 802 ast::ptr< ast::Type > exceptType = 803 new ast::PointerType{ new ast::StructInstType{ exceptionDecl } }; 804 throwStmt = ast::mutate_field( 805 throwStmt, &ast::ThrowStmt::expr, 806 findSingleExpression( throwStmt->expr, exceptType, context ) ); 807 } 808 return throwStmt; 809 } 810 811 const ast::CatchClause * Resolver::previsit( const ast::CatchClause * catchClause ) { 812 // Until we are very sure this invarent (ifs that move between passes have then) 813 // holds, check it. This allows a check for when to decode the mangling. 814 if ( auto ifStmt = catchClause->body.as<ast::IfStmt>() ) { 815 assert( ifStmt->then ); 816 } 817 // Encode the catchStmt so the condition can see the declaration. 818 if ( catchClause->cond ) { 819 ast::CatchClause * clause = mutate( catchClause ); 820 clause->body = new ast::IfStmt( clause->location, clause->cond, nullptr, clause->body ); 821 clause->cond = nullptr; 822 return clause; 823 } 824 return catchClause; 825 } 826 827 const ast::CatchClause * Resolver::postvisit( const ast::CatchClause * catchClause ) { 828 // Decode the catchStmt so everything is stored properly. 829 const ast::IfStmt * ifStmt = catchClause->body.as<ast::IfStmt>(); 830 if ( nullptr != ifStmt && nullptr == ifStmt->then ) { 831 assert( ifStmt->cond ); 832 assert( ifStmt->else_ ); 833 ast::CatchClause * clause = ast::mutate( catchClause ); 834 clause->cond = ifStmt->cond; 835 clause->body = ifStmt->else_; 836 // ifStmt should be implicately deleted here. 837 return clause; 838 } 839 return catchClause; 840 } 841 842 const ast::WaitForStmt * Resolver::previsit( const ast::WaitForStmt * stmt ) { 843 visit_children = false; 844 845 // Resolve all clauses first 846 for ( unsigned i = 0; i < stmt->clauses.size(); ++i ) { 847 const ast::WaitForClause & clause = *stmt->clauses[i]; 848 849 ast::TypeEnvironment env; 850 CandidateFinder funcFinder( context, env ); 851 852 // Find all candidates for a function in canonical form 853 funcFinder.find( clause.target, ResolveMode::withAdjustment() ); 854 855 if ( funcFinder.candidates.empty() ) { 856 stringstream ss; 857 ss << "Use of undeclared indentifier '"; 858 ss << clause.target.strict_as< ast::NameExpr >()->name; 859 ss << "' in call to waitfor"; 860 SemanticError( stmt->location, ss.str() ); 861 } 862 863 if ( clause.target_args.empty() ) { 864 SemanticError( stmt->location, 865 "Waitfor clause must have at least one mutex parameter"); 866 } 867 868 // Find all alternatives for all arguments in canonical form 869 std::vector< CandidateFinder > argFinders = 870 funcFinder.findSubExprs( clause.target_args ); 871 872 // List all combinations of arguments 873 std::vector< CandidateList > possibilities; 874 combos( argFinders.begin(), argFinders.end(), back_inserter( possibilities ) ); 875 876 // For every possible function: 877 // * try matching the arguments to the parameters, not the other way around because 878 // more arguments than parameters 879 CandidateList funcCandidates; 880 std::vector< CandidateList > argsCandidates; 881 SemanticErrorException errors; 882 for ( CandidateRef & func : funcFinder.candidates ) { 883 try { 884 auto pointerType = dynamic_cast< const ast::PointerType * >( 885 func->expr->result->stripReferences() ); 886 if ( ! pointerType ) { 887 SemanticError( stmt->location, func->expr->result.get(), 888 "candidate not viable: not a pointer type\n" ); 889 } 890 891 auto funcType = pointerType->base.as< ast::FunctionType >(); 892 if ( ! funcType ) { 893 SemanticError( stmt->location, func->expr->result.get(), 894 "candidate not viable: not a function type\n" ); 895 } 896 897 { 898 auto param = funcType->params.begin(); 899 auto paramEnd = funcType->params.end(); 900 901 if( ! nextMutex( param, paramEnd ) ) { 902 SemanticError( stmt->location, funcType, 903 "candidate function not viable: no mutex parameters\n"); 904 } 905 } 906 907 CandidateRef func2{ new Candidate{ *func } }; 908 // strip reference from function 909 func2->expr = referenceToRvalueConversion( func->expr, func2->cost ); 910 911 // Each argument must be matched with a parameter of the current candidate 912 for ( auto & argsList : possibilities ) { 913 try { 914 // Declare data structures needed for resolution 915 ast::OpenVarSet open; 916 ast::AssertionSet need, have; 917 ast::TypeEnvironment resultEnv{ func->env }; 918 // Add all type variables as open so that those not used in the 919 // parameter list are still considered open 920 resultEnv.add( funcType->forall ); 921 922 // load type variables from arguments into one shared space 923 for ( auto & arg : argsList ) { 924 resultEnv.simpleCombine( arg->env ); 925 } 926 927 // Make sure we don't widen any existing bindings 928 resultEnv.forbidWidening(); 929 930 // Find any unbound type variables 931 resultEnv.extractOpenVars( open ); 932 933 auto param = funcType->params.begin(); 934 auto paramEnd = funcType->params.end(); 935 936 unsigned n_mutex_param = 0; 937 938 // For every argument of its set, check if it matches one of the 939 // parameters. The order is important 940 for ( auto & arg : argsList ) { 941 // Ignore non-mutex arguments 942 if ( ! nextMutex( param, paramEnd ) ) { 943 // We ran out of parameters but still have arguments. 944 // This function doesn't match 945 SemanticError( stmt->location, funcType, 946 toString("candidate function not viable: too many mutex " 947 "arguments, expected ", n_mutex_param, "\n" ) ); 948 } 949 950 ++n_mutex_param; 951 952 // Check if the argument matches the parameter type in the current scope. 953 // ast::ptr< ast::Type > paramType = (*param)->get_type(); 954 955 if ( 956 ! unify( 957 arg->expr->result, *param, resultEnv, need, have, open ) 958 ) { 959 // Type doesn't match 960 stringstream ss; 961 ss << "candidate function not viable: no known conversion " 962 "from '"; 963 ast::print( ss, *param ); 964 ss << "' to '"; 965 ast::print( ss, arg->expr->result ); 966 ss << "' with env '"; 967 ast::print( ss, resultEnv ); 968 ss << "'\n"; 969 SemanticError( stmt->location, funcType, ss.str() ); 970 } 971 972 ++param; 973 } 974 975 // All arguments match! 976 977 // Check if parameters are missing 978 if ( nextMutex( param, paramEnd ) ) { 979 do { 980 ++n_mutex_param; 981 ++param; 982 } while ( nextMutex( param, paramEnd ) ); 983 984 // We ran out of arguments but still have parameters left; this 985 // function doesn't match 986 SemanticError( stmt->location, funcType, 987 toString( "candidate function not viable: too few mutex " 988 "arguments, expected ", n_mutex_param, "\n" ) ); 989 } 990 991 // All parameters match! 992 993 // Finish the expressions to tie in proper environments 994 finishExpr( func2->expr, resultEnv ); 995 for ( CandidateRef & arg : argsList ) { 996 finishExpr( arg->expr, resultEnv ); 997 } 998 999 // This is a match, store it and save it for later 1000 funcCandidates.emplace_back( std::move( func2 ) ); 1001 argsCandidates.emplace_back( std::move( argsList ) ); 1002 1003 } catch ( SemanticErrorException & e ) { 1004 errors.append( e ); 1005 } 1006 } 1007 } catch ( SemanticErrorException & e ) { 1008 errors.append( e ); 1009 } 1010 } 1011 1012 // Make sure correct number of arguments 1013 if( funcCandidates.empty() ) { 1014 SemanticErrorException top( stmt->location, 1015 "No alternatives for function in call to waitfor" ); 1016 top.append( errors ); 1017 throw top; 1018 } 1019 1020 if( argsCandidates.empty() ) { 1021 SemanticErrorException top( stmt->location, 1022 "No alternatives for arguments in call to waitfor" ); 1023 top.append( errors ); 1024 throw top; 1025 } 1026 1027 if( funcCandidates.size() > 1 ) { 1028 SemanticErrorException top( stmt->location, 1029 "Ambiguous function in call to waitfor" ); 1030 top.append( errors ); 1031 throw top; 1032 } 1033 if( argsCandidates.size() > 1 ) { 1034 SemanticErrorException top( stmt->location, 1035 "Ambiguous arguments in call to waitfor" ); 1036 top.append( errors ); 1037 throw top; 1038 } 1039 // TODO: need to use findDeletedExpr to ensure no deleted identifiers are used. 1040 1041 // build new clause 1042 auto clause2 = new ast::WaitForClause( clause.location ); 1043 1044 clause2->target = funcCandidates.front()->expr; 1045 1046 clause2->target_args.reserve( clause.target_args.size() ); 1047 const ast::StructDecl * decl_monitor = symtab.lookupStruct( "monitor$" ); 1048 for ( auto arg : argsCandidates.front() ) { 1049 const auto & loc = stmt->location; 1050 1051 ast::Expr * init = new ast::CastExpr( loc, 1052 new ast::UntypedExpr( loc, 1053 new ast::NameExpr( loc, "get_monitor" ), 1054 { arg->expr } 1055 ), 1056 new ast::PointerType( 1057 new ast::StructInstType( 1058 decl_monitor 1059 ) 1060 ) 1061 ); 1062 1063 clause2->target_args.emplace_back( findSingleExpression( init, context ) ); 1064 } 1065 1066 // Resolve the conditions as if it were an IfStmt, statements normally 1067 clause2->when_cond = findSingleExpression( clause.when_cond, context ); 1068 clause2->stmt = clause.stmt->accept( *visitor ); 1069 1070 // set results into stmt 1071 auto n = mutate( stmt ); 1072 n->clauses[i] = clause2; 1073 stmt = n; 1074 } 1075 1076 if ( stmt->timeout_stmt ) { 1077 // resolve the timeout as a size_t, the conditions like IfStmt, and stmts normally 1078 ast::ptr< ast::Type > target = 1079 new ast::BasicType{ ast::BasicType::LongLongUnsignedInt }; 1080 auto timeout_time = findSingleExpression( stmt->timeout_time, target, context ); 1081 auto timeout_cond = findSingleExpression( stmt->timeout_cond, context ); 1082 auto timeout_stmt = stmt->timeout_stmt->accept( *visitor ); 1083 1084 // set results into stmt 1085 auto n = mutate( stmt ); 1086 n->timeout_time = std::move( timeout_time ); 1087 n->timeout_cond = std::move( timeout_cond ); 1088 n->timeout_stmt = std::move( timeout_stmt ); 1089 stmt = n; 1090 } 1091 1092 if ( stmt->else_stmt ) { 1093 // resolve the condition like IfStmt, stmts normally 1094 auto else_cond = findSingleExpression( stmt->else_cond, context ); 1095 auto else_stmt = stmt->else_stmt->accept( *visitor ); 1096 1097 // set results into stmt 1098 auto n = mutate( stmt ); 1099 n->else_cond = std::move( else_cond ); 1100 n->else_stmt = std::move( else_stmt ); 1101 stmt = n; 1102 } 1103 1104 return stmt; 1105 } 1106 1107 const ast::WithStmt * Resolver::previsit( const ast::WithStmt * withStmt ) { 1108 auto mutStmt = mutate(withStmt); 1109 resolveWithExprs(mutStmt->exprs, stmtsToAddBefore); 1110 return mutStmt; 1111 } 1112 1113 void Resolver::resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd) { 1114 for (auto & expr : exprs) { 1115 // only struct- and union-typed expressions are viable candidates 1116 expr = findKindExpression( expr, context, structOrUnion, "with expression" ); 1117 1118 // if with expression might be impure, create a temporary so that it is evaluated once 1119 if ( Tuples::maybeImpure( expr ) ) { 1120 static UniqueName tmpNamer( "_with_tmp_" ); 1121 const CodeLocation loc = expr->location; 1122 auto tmp = new ast::ObjectDecl(loc, tmpNamer.newName(), expr->result, new ast::SingleInit(loc, expr ) ); 1123 expr = new ast::VariableExpr( loc, tmp ); 1124 stmtsToAdd.push_back( new ast::DeclStmt(loc, tmp ) ); 1125 if ( InitTweak::isConstructable( tmp->type ) ) { 1126 // generate ctor/dtor and resolve them 1127 tmp->init = InitTweak::genCtorInit( loc, tmp ); 1128 } 1129 // since tmp is freshly created, this should modify tmp in-place 1130 tmp->accept( *visitor ); 1131 } 1132 else if (expr->env && expr->env->empty()) { 1133 expr = ast::mutate_field(expr.get(), &ast::Expr::env, nullptr); 1134 } 1135 } 1136 } 1137 1138 1139 const ast::SingleInit * Resolver::previsit( const ast::SingleInit * singleInit ) { 1140 visit_children = false; 1141 // resolve initialization using the possibilities as determined by the `currentObject` 1142 // cursor. 1143 ast::ptr< ast::Expr > untyped = new ast::UntypedInitExpr{ 1144 singleInit->location, singleInit->value, currentObject.getOptions() }; 1145 ast::ptr<ast::Expr> newExpr = findSingleExpression( untyped, context ); 1146 const ast::InitExpr * initExpr = newExpr.strict_as< ast::InitExpr >(); 1147 1148 // move cursor to the object that is actually initialized 1149 currentObject.setNext( initExpr->designation ); 1150 1151 // discard InitExpr wrapper and retain relevant pieces. 1152 // `initExpr` may have inferred params in the case where the expression specialized a 1153 // function pointer, and newExpr may already have inferParams of its own, so a simple 1154 // swap is not sufficient 1155 ast::Expr::InferUnion inferred = initExpr->inferred; 1156 swap_and_save_env( newExpr, initExpr->expr ); 1157 newExpr.get_and_mutate()->inferred.splice( std::move(inferred) ); 1158 1159 // get the actual object's type (may not exactly match what comes back from the resolver 1160 // due to conversions) 1161 const ast::Type * initContext = currentObject.getCurrentType(); 1162 1163 removeExtraneousCast( newExpr ); 1164 1165 // check if actual object's type is char[] 1166 if ( auto at = dynamic_cast< const ast::ArrayType * >( initContext ) ) { 1167 if ( isCharType( at->base ) ) { 1168 // check if the resolved type is char* 1169 if ( auto pt = newExpr->result.as< ast::PointerType >() ) { 1170 if ( isCharType( pt->base ) ) { 1171 // strip cast if we're initializing a char[] with a char* 1172 // e.g. char x[] = "hello" 1173 if ( auto ce = newExpr.as< ast::CastExpr >() ) { 1174 swap_and_save_env( newExpr, ce->arg ); 1175 } 1176 } 1177 } 1178 } 1179 } 1180 1181 // move cursor to next object in preparation for next initializer 1182 currentObject.increment(); 1183 1184 // set initializer expression to resolved expression 1185 return ast::mutate_field( singleInit, &ast::SingleInit::value, std::move(newExpr) ); 1186 } 1187 1188 const ast::ListInit * Resolver::previsit( const ast::ListInit * listInit ) { 1189 // move cursor into brace-enclosed initializer-list 1190 currentObject.enterListInit( listInit->location ); 1191 1192 assert( listInit->designations.size() == listInit->initializers.size() ); 1193 for ( unsigned i = 0; i < listInit->designations.size(); ++i ) { 1194 // iterate designations and initializers in pairs, moving the cursor to the current 1195 // designated object and resolving the initializer against that object 1196 listInit = ast::mutate_field_index( 1197 listInit, &ast::ListInit::designations, i, 1198 currentObject.findNext( listInit->designations[i] ) ); 1199 listInit = ast::mutate_field_index( 1200 listInit, &ast::ListInit::initializers, i, 1201 listInit->initializers[i]->accept( *visitor ) ); 1202 } 1203 1204 // move cursor out of brace-enclosed initializer-list 1205 currentObject.exitListInit(); 1206 1207 visit_children = false; 1208 return listInit; 1209 } 1210 1211 const ast::ConstructorInit * Resolver::previsit( const ast::ConstructorInit * ctorInit ) { 1212 visitor->maybe_accept( ctorInit, &ast::ConstructorInit::ctor ); 1213 visitor->maybe_accept( ctorInit, &ast::ConstructorInit::dtor ); 1214 1215 // found a constructor - can get rid of C-style initializer 1216 // xxx - Rob suggests this field is dead code 1217 ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::init, nullptr ); 1218 1219 // intrinsic single-parameter constructors and destructors do nothing. Since this was 1220 // implicitly generated, there's no way for it to have side effects, so get rid of it to 1221 // clean up generated code 1222 if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->ctor ) ) { 1223 ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::ctor, nullptr ); 1224 } 1225 if ( InitTweak::isIntrinsicSingleArgCallStmt( ctorInit->dtor ) ) { 1226 ctorInit = ast::mutate_field( ctorInit, &ast::ConstructorInit::dtor, nullptr ); 1227 } 1228 1229 return ctorInit; 1230 } 1231 1232 // suppress error on autogen functions and mark invalid autogen as deleted. 1233 bool Resolver::on_error(ast::ptr<ast::Decl> & decl) { 1234 if (auto functionDecl = decl.as<ast::FunctionDecl>()) { 1235 // xxx - can intrinsic gen ever fail? 1236 if (functionDecl->linkage == ast::Linkage::AutoGen) { 1237 auto mutDecl = mutate(functionDecl); 1238 mutDecl->isDeleted = true; 1239 mutDecl->stmts = nullptr; 1240 decl = mutDecl; 1241 return false; 1242 } 1243 } 1244 return true; 1245 } 1239 } 1240 return true; 1241 } 1246 1242 1247 1243 } // namespace ResolvExpr -
src/SymTab/GenImplicitCall.cpp
r0522ebe ra4da45e 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // GenImplicitCall. hpp --7 // GenImplicitCall.cpp -- Generate code for implicit operator calls. 8 8 // 9 9 // Author : Andrew Beach … … 31 31 namespace { 32 32 33 template< typename OutIter > 33 using Inserter = std::back_insert_iterator<std::list<ast::ptr<ast::Stmt>>>; 34 34 35 ast::ptr< ast::Stmt > genCall( 35 36 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 36 const CodeLocation & loc, const std::string & fname, OutIter && out,37 const CodeLocation & loc, const std::string & fname, Inserter && out, 37 38 const ast::Type * type, const ast::Type * addCast, LoopDirection forward = LoopForward ); 38 39 … … 41 42 /// optionally returns a statement which must be inserted prior to the containing loop, if 42 43 /// 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, OutIter && out, const ast::Type * type,46 const CodeLocation & loc, std::string fname, Inserter && 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 >100 99 void genArrayCall( 101 100 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 102 const CodeLocation & loc, const std::string & fname, OutIter && out,101 const CodeLocation & loc, const std::string & fname, Inserter && out, 103 102 const ast::ArrayType * array, const ast::Type * addCast = nullptr, 104 103 LoopDirection forward = LoopForward … … 166 165 } 167 166 168 template< typename OutIter >169 167 ast::ptr< ast::Stmt > genCall( 170 168 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 171 const CodeLocation & loc, const std::string & fname, OutIter && out,169 const CodeLocation & loc, const std::string & fname, Inserter && out, 172 170 const ast::Type * type, const ast::Type * addCast, LoopDirection forward 173 171 ) { 174 172 if ( auto at = dynamic_cast< const ast::ArrayType * >( type ) ) { 175 173 genArrayCall( 176 srcParam, dstParam, loc, fname, std::forward< OutIter >(out), at, addCast,174 srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), at, addCast, 177 175 forward ); 178 176 return {}; 179 177 } else { 180 178 return genScalarCall( 181 srcParam, dstParam, loc, fname, std::forward< OutIter>( out ), type, addCast );179 srcParam, dstParam, loc, fname, std::forward< Inserter&& >( out ), type, addCast ); 182 180 } 183 181 } … … 185 183 } // namespace 186 184 187 ast::ptr< ast::Stmt >genImplicitCall(185 const ast::Stmt * genImplicitCall( 188 186 InitTweak::InitExpander & srcParam, const ast::Expr * dstParam, 189 187 const CodeLocation & loc, const std::string & fname, const ast::ObjectDecl * obj, … … 191 189 ) { 192 190 // unnamed bit fields are not copied as they cannot be accessed 193 if ( isUnnamedBitfield( obj ) ) return {};191 if ( isUnnamedBitfield( obj ) ) return nullptr; 194 192 195 193 ast::ptr< ast::Type > addCast; … … 199 197 } 200 198 201 std:: vector< ast::ptr< ast::Stmt > > stmts;199 std::list< ast::ptr< ast::Stmt > > stmts; 202 200 genCall( 203 201 srcParam, dstParam, loc, fname, back_inserter( stmts ), obj->type, addCast, forward ); 204 202 205 if ( stmts.empty() ) { 206 return {}; 207 } else if ( stmts.size() == 1 ) { 208 const ast::Stmt * callStmt = stmts.front(); 209 if ( addCast ) { 210 // implicitly generated ctor/dtor calls should be wrapped so that later passes are 211 // aware they were generated. 212 callStmt = new ast::ImplicitCtorDtorStmt( callStmt->location, callStmt ); 213 } 214 return callStmt; 215 } else { 216 assert( false ); 217 return {}; 218 } 203 if ( stmts.empty() ) return nullptr; 204 assert( stmts.size() == 1 ); 205 206 const ast::Stmt * callStmt = stmts.front().release(); 207 // Implicitly generated ctor/dtor calls should be wrapped so that 208 // later passes are aware they were generated. 209 if ( addCast ) { 210 callStmt = new ast::ImplicitCtorDtorStmt( callStmt->location, callStmt ); 211 } 212 return callStmt; 219 213 } 220 214 … … 226 220 // compile-command: "make install" // 227 221 // End: // 228 229 -
src/SymTab/GenImplicitCall.hpp
r0522ebe ra4da45e 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // GenImplicitCall.hpp -- 7 // GenImplicitCall.hpp -- Generate code for implicit operator calls. 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 ast::ptr<ast::Stmt>genImplicitCall(27 const 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
r0522ebe ra4da45e 133 133 /// Generates a single struct member operation. 134 134 /// (constructor call, destructor call, assignment call) 135 // This is managed because it uses another helper that returns a ast::ptr. 136 ast::ptr<ast::Stmt> makeMemberOp( 135 const ast::Stmt * makeMemberOp( 137 136 const CodeLocation& location, 138 137 const ast::ObjectDecl * dstParam, const ast::Expr * src, … … 525 524 } 526 525 527 ast::ptr<ast::Stmt>StructFuncGenerator::makeMemberOp(526 const ast::Stmt * StructFuncGenerator::makeMemberOp( 528 527 const CodeLocation& location, const ast::ObjectDecl * dstParam, 529 528 const ast::Expr * src, const ast::ObjectDecl * field, … … 540 539 ) 541 540 ); 542 autostmt = genImplicitCall(541 const ast::Stmt * stmt = genImplicitCall( 543 542 srcParam, dstSelect, location, func->name, 544 543 field, direction … … 598 597 location, field, new ast::VariableExpr( location, srcParam ) 599 598 ) : nullptr; 600 ast::ptr<ast::Stmt>stmt =599 const ast::Stmt * stmt = 601 600 makeMemberOp( location, dstParam, srcSelect, field, func, direction ); 602 601 603 602 if ( nullptr != stmt ) { 604 stmts->kids. push_back( stmt );603 stmts->kids.emplace_back( stmt ); 605 604 } 606 605 } … … 623 622 for ( auto param = params.begin() + 1 ; current != end ; ++current ) { 624 623 const ast::ptr<ast::Decl> & member = *current; 625 ast::ptr<ast::Stmt>stmt = nullptr;624 const ast::Stmt * stmt = nullptr; 626 625 auto field = member.as<ast::ObjectDecl>(); 627 626 // Not sure why it could be null. … … 641 640 642 641 if ( nullptr != stmt ) { 643 stmts->kids. push_back( stmt );642 stmts->kids.emplace_back( stmt ); 644 643 } 645 644 } -
src/main.cc
r0522ebe ra4da45e 181 181 182 182 static void _Signal(struct sigaction & act, int sig, int flags ) { 183 sigemptyset( &act.sa_mask ); 183 184 act.sa_flags = flags; 184 185 -
tests/.expect/attributes.arm64.txt
r0522ebe ra4da45e 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __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
r0522ebe ra4da45e 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __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
r0522ebe ra4da45e 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __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
r0522ebe ra4da45e 70 70 14 71 71 15 72 16 get this line 73 17 @# this line 1)-{} 74 18 @# this line 1)-{} 75 19 abc 76 20 abc 77 21 d d 78 79 d 80 22 ZC44% 81 23 ZC44% 72 82 1 yyyyyyyyyyyyyyyyyyyy 73 83 2 abcxxx … … 85 95 14 86 96 15 97 16 get this line 98 17 @# this line 1)-{} 99 18 @# this line 1)-{} 100 19 abc 101 20 abc 102 21 d d 103 104 d 105 22 ZC44% 106 23 ZC44% -
tests/collections/.in/string-istream-manip.txt
r0522ebe ra4da45e 38 38 aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww 39 39 uuuuu 40 get this line 41 @# this line 1)-{}% 42 @# this line 2)-{}% 43 "abc" 44 'abc ' 45 { d d 46 47 d } 48 X ZC44%Y 49 X ZC55%Y 40 50 abc 41 51 cccccb … … 45 55 aaaaaaaaxxxxxxxxaabbccbbdddwwwbbbbbbbbwwwwwwwwaaaaaaaawwwwwwwwaaaaaaaawwwwwwww 46 56 uuuuu 57 get this line 58 @# this line 1)-{}% 59 @# this line 2)-{}% 60 "abc" 61 'abc ' 62 { d d 63 64 d } 65 X ZC44%Y 66 X ZC55%Y -
tests/collections/string-istream-manip.cfa
r0522ebe ra4da45e 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;20 }21 sout | "Unable to force size" | desiredSize | "in 1,000,000 tries";16 for (1_000_000) { 17 string x = "a"; 18 (void)x; 19 if (desiredSize == DEBUG_string_bytes_avail_until_gc(DEBUG_string_heap())) return; 20 } 21 sout | "Unable to force size" | desiredSize | "in 1,000,000 tries"; 22 22 } 23 23 24 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; 25 // These "pre" cases deal with issues analogous to the "pre" cases of io/manipulatorsInput. 26 // The acceptance criterion is simpler but driving the cases is harder. 27 // The tests just read strings and echo what they read; acceptance of simple echoing assures 28 // no spurious splitting merging. 29 // The lengths of the strings are chosen to match white-box knowledge of when the string layer 30 // has tor drive the cstring layer through a second iteration: 31 // - for no-manip, lengths are near the room at end of string heap 32 // (chosen target size of 9 showed the original bug on preS2, aligned with the other cases) 33 // - for manip, lengths are near the auxiliary buffer size of 128 34 // Only first case repeats for string_res; rest run only from the passthru string layer. 35 // Similarly, the manipulator breadth isn't checked at the cstring layer either. 36 { 37 // S: string, no manipulator 38 void echoTillX(const char * casename) { 39 string s; 40 // loop assumes behaviour not tested until main-case #15: 41 // on reading nothing, the prior string value is left alone 42 do { 43 s = ""; 44 forceStringHeapFreeSpaceTo(9); 45 sin | s; 46 sout | casename | s; 47 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 48 } 49 echoTillX("preS1"); 50 echoTillX("preS2"); 51 echoTillX("preS3"); 52 echoTillX("preS4"); 53 } 54 { 55 // SMN: string, manipulator for no-op 56 void echoTillX(const char * casename) { 57 string s; 58 do { 59 s = ""; 60 sin | plainjane( s ); 61 sout | casename | s; 62 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 63 } 64 echoTillX("preSMN1"); 65 echoTillX("preSMN2"); 66 echoTillX("preSMN3"); 67 echoTillX("preSMN4"); 68 } 69 { 70 // RMN: string_res, manipulator for no-op 71 void echoTillX(const char * casename) { 72 string_res s; 73 do { 74 s = ""; 75 sin | plainjane( s ); 76 sout | casename | s; 77 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 78 } 79 echoTillX("preRMN1"); 80 echoTillX("preRMN2"); 81 echoTillX("preRMN3"); 82 echoTillX("preRMN4"); 83 } 84 { 85 // SMI: string, manipulator `incl` 86 void echoTillX(const char * casename) { 87 string s; 88 do { 89 s = ""; 90 sin | skip("-\n"); 91 sin | incl( ".:|# x", s ); 92 sout | casename | " \"" | s | "\""; 93 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 94 } 95 echoTillX("preSMI1"); 96 echoTillX("preSMI2"); 97 echoTillX("preSMI3"); 98 echoTillX("preSMI4"); 99 } 100 { 101 // SME: string, manipulator `excl` 102 void echoTillX(const char * casename) { 103 string s; 104 do { 105 s = ""; 106 sin | skip("-\n"); 107 sin | excl( "-\n", s ); 108 sout | casename | " \"" | s | "\""; 109 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 110 } 111 echoTillX("preSME1"); 112 echoTillX("preSME2"); 113 echoTillX("preSME3"); 114 echoTillX("preSME4"); 115 } 116 sin | skip("-\n"); 117 { 118 // SMG: string, manipulator `getline` 119 void echoTillX(const char * casename) { 120 string s; 121 do { 122 s = ""; 123 sin | getline( s ); 124 sout | casename | s; 125 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 126 } 127 echoTillX("preSMG1"); 128 echoTillX("preSMG2"); 129 echoTillX("preSMG3"); 130 echoTillX("preSMG4"); 131 } 132 { 133 // SMD: string, manipulator (`getline` with custom) delimiter 134 void echoTillX(const char * casename) { 135 string s; 136 do { 137 s = ""; 138 sin | getline( s, '@' ); 139 sout | casename | s; 140 } while ( size(s) > 0 && s[size(s)-1] != 'x' ); 141 sin | skip(" \n"); 142 } 143 echoTillX("preSMD1"); 144 echoTillX("preSMD2"); 145 echoTillX("preSMD3"); 146 echoTillX("preSMD4"); 147 } 148 // Keep harmonized with io/manipulatorsInput. 149 { 150 string s = "yyyyyyyyyyyyyyyyyyyy"; 151 char sk[] = "abc"; 152 sin | "abc " | skip( sk ) | skip( 5 ); sout | "1" | s; 153 sin | s; sout | "2" | s; 154 sin | ignore( s ); sout | "3" | s; 155 sin | wdi( 8, s ); sout | "4" | s; 156 sin | ignore( wdi( 8, s ) ); sout | "5" | s; 157 158 sin | incl( "abc", s ); sout | "6" | s; 159 sin | excl( "abc", s ); sout | "7" | s; 160 sin | ignore( incl( "abc", s ) ); sout | "8" | s; 161 sin | ignore( excl( "abc", s ) ); sout | "9" | s; 162 sin | incl( "abc", wdi( 8, s ) ); sout | "10" | s; 163 sin | excl( "abc", wdi( 8, s ) ); sout | "11" | s; 164 sin | ignore( incl( "abc", wdi( 8, s ) ) ); sout | "12" | s; 165 sin | ignore( excl( "abc", wdi( 8, s ) ) ); sout | "13" | s; 166 sin | nl; 167 168 s = "q"; 169 sin | incl( "abc", s ); sout | "14" | s; 170 s = "q"; 171 sin | excl( "u", s ); sout | "15" | s; 172 sin | skip( "u" ) | nl; 173 174 sin | getline( s ); sout | "16" | s; 175 sin | getline( s, '%' ) | nl; sout | "17" | s; 176 sin | ignore( getline( s, '%' ) ) | nl; sout | "18" | s; 177 178 sin | quoted( s ); sout | "19" | s; 179 sin | quoted( s, '\'' ); sout | "20" | s; 180 sin | quoted( s, '{', '}' ); sout | "21" | s; 181 sin | quoted( s, 'X', 'Y' ); sout | "22" | s; 182 sin | ignore( quoted( s, 'X', 'Y' ) ); sout | "23" | s; 183 sin | nl; 184 } 185 // Full repeat on string_res layer assures the full manipulator vocabulary is supported there. 186 { 187 string_res s = "yyyyyyyyyyyyyyyyyyyy"; 188 char sk[] = "abc"; 189 sin | "abc " | skip( sk ) | skip( 5 ); sout | "1" | s; 190 sin | s; sout | "2" | s; 191 sin | ignore( s ); sout | "3" | s; 192 sin | wdi( 8, s ); sout | "4" | s; 193 sin | ignore( wdi( 8, s ) ); sout | "5" | s; 194 195 sin | incl( "abc", s ); sout | "6" | s; 196 sin | excl( "abc", s ); sout | "7" | s; 197 sin | ignore( incl( "abc", s ) ); sout | "8" | s; 198 sin | ignore( excl( "abc", s ) ); sout | "9" | s; 199 sin | incl( "abc", wdi( 8, s ) ); sout | "10" | s; 200 sin | excl( "abc", wdi( 8, s ) ); sout | "11" | s; 201 sin | ignore( incl( "abc", wdi( 8, s ) ) ); sout | "12" | s; 202 sin | ignore( excl( "abc", wdi( 8, s ) ) ); sout | "13" | s; 167 203 sin | "\n"; 168 204 169 205 s = "q"; 170 sin | incl( "abc", s ); sout | "14" | s;171 s = "q"; 172 sin | excl( "u", s ); sout | "15" | s;206 sin | incl( "abc", s ); sout | "14" | s; 207 s = "q"; 208 sin | excl( "u", s ); sout | "15" | s; 173 209 sin | skip( "u" ); 174 210 sin | "\n"; 175 } 176 // Full repeat on string_res layer assures the full manipulator vocabulary is supported there. 177 { 178 string_res s = "yyyyyyyyyyyyyyyyyyyy"; 179 char sk[] = "abc"; 180 sin | "abc " | skip( sk ) | skip( 5 ); sout | "1" | s; 181 sin | s; sout | "2" | s; 182 sin | ignore( s ); sout | "3" | s; 183 sin | wdi( 8, s ); sout | "4" | s; 184 sin | ignore( wdi( 8, s ) ); sout | "5" | s; 185 186 sin | incl( "abc", s ); sout | "6" | s; 187 sin | excl( "abc", s ); sout | "7" | s; 188 sin | ignore( incl( "abc", s ) ); sout | "8" | s; 189 sin | ignore( excl( "abc", s ) ); sout | "9" | s; 190 sin | incl( "abc", wdi( 8, s ) ); sout | "10" | s; 191 sin | excl( "abc", wdi( 8, s ) ); sout | "11" | s; 192 sin | ignore( incl( "abc", wdi( 8, s ) ) ); sout | "12" | s; 193 sin | ignore( excl( "abc", wdi( 8, s ) ) ); sout | "13" | s; 194 sin | "\n"; 195 196 s = "q"; 197 sin | incl( "abc", s ); sout | "14" | s; 198 s = "q"; 199 sin | excl( "u", s ); sout | "15" | s; 200 sin | skip( "u" ); 201 sin | "\n"; 202 } 211 sin | getline( s ); sout | "16" | s; 212 sin | getline( s, '%' ) | nl; sout | "17" | s; 213 sin | ignore( getline( s, '%' ) ) | nl; sout | "18" | s; 214 215 sin | quoted( s ); sout | "19" | s; 216 sin | quoted( s, '\'' ); sout | "20" | s; 217 sin | quoted( s, '{', '}' ); sout | "21" | s; 218 sin | quoted( s, 'X', 'Y' ); sout | "22" | s; 219 sin | ignore( quoted( s, 'X', 'Y' ) ); sout | "23" | s; 220 } 203 221 } -
tests/configs/parsebools.cfa
r0522ebe ra4da45e 10 10 // Author : Thierry Delisle 11 11 // Created On : Wed Oct 12 15:28:01 2022 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Sun Feb 11 09:26:13 2024 14 // Update Count : 2 15 15 // 16 16 17 17 #include <fstream.hfa> 18 19 18 #include "../meta/fork+exec.hfa" 20 19 … … 22 21 #include <parseargs.hfa> 23 22 24 int main( int argc, char * argv[]) {23 int main( int argc, char * argv[] ) { 25 24 check_main(argv[0]); 26 25 … … 41 40 42 41 char **left; 43 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left );42 parse_args( options, "[OPTIONS]...\ntesting bool parameters", left ); 44 43 45 44 sout | "yes/no :" | YN; … … 51 50 } 52 51 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);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 ); 60 59 } 61 } 62 else { 63 int status = do_wait(child); 64 print_status(status); 65 } 60 } else { 61 int status = do_wait( child ); 62 print_status( status ); 63 } // if 66 64 67 printf("all true/set arg:\n"); 68 if(pid_t child = strict_fork(); child == 0) { 69 int ret = execle(path, "parsebools", "-e=yes", "-y=Y", "-n=y", "-t=true", "-s", "-u", (const char*)0p, env); 70 if(ret < 0) { 71 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 72 exit(1); 73 } 74 } 75 else { 76 int status = do_wait(child); 77 print_status(status); 78 } 65 printf( "all true/set arg:\n" ); 66 if ( pid_t child = strict_fork(); child == 0 ) { 67 int ret = execle( path, "parsebools", "-e=yes", "-y=Y", "-n=y", "-t=true", "-s", "-u", (const char*)0p, env ); 68 if ( ret < 0 ) { 69 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 70 exit( 1 ); 71 } // if 72 } else { 73 int status = do_wait( child ); 74 print_status( status ); 75 } // if 79 76 80 printf("all false/unset arg:\n"); 81 if(pid_t child = strict_fork(); child == 0) { 82 int ret = execle(path, "parsebools", "-e=no", "-y=N", "-n=n", "-t=false", (const char*)0p, env); 83 if(ret < 0) { 84 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 85 exit(1); 86 } 87 } 88 else { 89 int status = do_wait(child); 90 print_status(status); 91 } 77 printf( "all false/unset arg:\n" ); 78 if ( pid_t child = strict_fork(); child == 0 ) { 79 int ret = execle( path, "parsebools", "-e=no", "-y=N", "-n=n", "-t=false", (const char*)0p, env ); 80 if ( ret < 0 ) { 81 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 82 exit( 1 ); 83 } // if 84 } else { 85 int status = do_wait( child ); 86 print_status( status ); 87 } // if 92 88 93 printf("gibberish arg 1:\n"); 94 if(pid_t child = strict_fork(); child == 0) { 95 int ret = execle(path, "parsebools", "-y=true", (const char*)0p, env); 96 if(ret < 0) { 97 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 98 exit(1); 99 } 100 } 101 else { 102 int status = do_wait(child); 103 print_status(status); 104 } 89 printf( "gibberish arg 1:\n" ); 90 if ( pid_t child = strict_fork(); child == 0 ) { 91 int ret = execle( path, "parsebools", "-y=true", (const char*)0p, env ); 92 if ( ret < 0 ) { 93 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 94 exit( 1 ); 95 } // if 96 } else { 97 int status = do_wait( child ); 98 print_status( status ); 99 } // if 105 100 106 printf("gibberish arg 2:\n"); 107 if(pid_t child = strict_fork(); child == 0) { 108 int ret = execle(path, "parsebools", "-t=yes", (const char*)0p, env); 109 if(ret < 0) { 110 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 111 exit(1); 112 } 113 } 114 else { 115 int status = do_wait(child); 116 print_status(status); 117 } 101 printf( "gibberish arg 2:\n" ); 102 if ( pid_t child = strict_fork(); child == 0 ) { 103 int ret = execle( path, "parsebools", "-t=yes", (const char*)0p, env ); 104 if ( ret < 0 ) { 105 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 106 exit( 1 ); 107 } // if 108 } else { 109 int status = do_wait( child ); 110 print_status( status ); 111 } // if 118 112 119 printf("gibberish arg 3:\n"); 120 if(pid_t child = strict_fork(); child == 0) { 121 int ret = execle(path, "parsebools", "-s=yes", (const char*)0p, env); 122 if(ret < 0) { 123 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 124 exit(1); 125 } 126 } 127 else { 128 int status = do_wait(child); 129 print_status(status); 130 } 113 printf( "gibberish arg 3:\n" ); 114 if ( pid_t child = strict_fork(); child == 0 ) { 115 int ret = execle( path, "parsebools", "-s=yes", (const char*)0p, env ); 116 if ( ret < 0 ) { 117 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 118 exit( 1 ); 119 } // if 120 } else { 121 int status = do_wait( child ); 122 print_status( status ); 123 } // if 131 124 132 printf("gibberish arg 4:\n"); 133 if(pid_t child = strict_fork(); child == 0) { 134 int ret = execle(path, "parsebools", "-u=yes", (const char*)0p, env); 135 if(ret < 0) { 136 fprintf(stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror(errno)); 137 exit(1); 138 } 139 } 140 else { 141 int status = do_wait(child); 142 print_status(status); 143 } 125 printf( "gibberish arg 4:\n" ); 126 if ( pid_t child = strict_fork(); child == 0 ) { 127 int ret = execle( path, "parsebools", "-u=yes", ( const char*)0p, env ); 128 if ( ret < 0 ) { 129 fprintf( stderr, "Execl 2 returned with error: %d '%s'\n", errno, strerror( errno ) ); 130 exit( 1 ); 131 } // if 132 } else { 133 int status = do_wait( child ); 134 print_status( status ); 135 } // if 144 136 145 printf( "All Done!\n");137 printf( "All Done!\n" ); 146 138 147 139 return 0; -
tests/configs/parsenums.cfa
r0522ebe ra4da45e 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 } 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"); 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" ); 216 204 217 205 return 0; -
tests/configs/usage.cfa
r0522ebe ra4da45e 10 10 // Author : Thierry Delisle 11 11 // Created On : Wed Oct 12 15:28:01 2022 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 12 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Sun Feb 11 09:29:51 2024 14 // Update Count : 1 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 } 30 else { 31 int status = do_wait(child); 32 print_status(status); 33 } 28 print_args_usage(1, fake_argv, opts, "Test usage", false ); 29 } else { 30 int status = do_wait( child ); 31 print_status( status ); 32 } // if 34 33 35 34 sout | "No args, with errors"; 36 if (pid_t child = strict_fork(); child == 0) {35 if ( pid_t child = strict_fork(); child == 0 ) { 37 36 array( cfa_option, 0 ) opts; 38 print_args_usage(1, fake_argv, opts, "Test usage", true); 39 } 40 else { 41 int status = do_wait(child); 42 print_status(status); 43 } 37 print_args_usage(1, fake_argv, opts, "Test usage", true ); 38 } else { 39 int status = do_wait( child ); 40 print_status( status ); 41 } // if 44 42 45 43 sout | "Args with short names only:"; 46 if (pid_t child = strict_fork(); child == 0) {44 if ( pid_t child = strict_fork(); child == 0 ) { 47 45 int a, b, c; 48 46 array( cfa_option, 3 ) opts; … … 50 48 opts[1] = (cfa_option){'b', "", "Second arg", b }; 51 49 opts[2] = (cfa_option){'c', "", "Third arg", c }; 52 print_args_usage(1, fake_argv, opts, "Test usage", false); 53 } 54 else { 55 int status = do_wait(child); 56 print_status(status); 57 } 50 print_args_usage(1, fake_argv, opts, "Test usage", false ); 51 } else { 52 int status = do_wait( child ); 53 print_status( status ); 54 } // if 58 55 59 56 sout | "Args with long names only:"; 60 if (pid_t child = strict_fork(); child == 0) {57 if ( pid_t child = strict_fork(); child == 0 ) { 61 58 int a, b, c; 62 59 array( cfa_option, 3 ) opts; … … 64 61 opts[1] = (cfa_option){'\0', "BB", "Second arg", b }; 65 62 opts[2] = (cfa_option){'\0', "CC", "Third arg", c }; 66 print_args_usage(1, fake_argv, opts, "Test usage", false); 67 } 68 else { 69 int status = do_wait(child); 70 print_status(status); 71 } 63 print_args_usage(1, fake_argv, opts, "Test usage", false ); 64 } else { 65 int status = do_wait( child ); 66 print_status( status ); 67 } // if 72 68 73 69 sout | "Mix of short and long args:"; 74 if (pid_t child = strict_fork(); child == 0) {70 if ( pid_t child = strict_fork(); child == 0 ) { 75 71 int a, b, c; 76 72 array( cfa_option, 3 ) opts; … … 78 74 opts[1] = (cfa_option){'b', "BBBB", "Second arg", b }; 79 75 opts[2] = (cfa_option){'\0', "CC", "Third arg", c }; 80 print_args_usage(1, fake_argv, opts, "Test usage", false); 81 } 82 else { 83 int status = do_wait(child); 84 print_status(status); 85 } 76 print_args_usage(1, fake_argv, opts, "Test usage", false ); 77 } else { 78 int status = do_wait( child ); 79 print_status( status ); 80 } // if 86 81 87 82 sout | "Mix of short and long and some missing description:"; 88 if (pid_t child = strict_fork(); child == 0) {83 if ( pid_t child = strict_fork(); child == 0 ) { 89 84 int a, b, c; 90 85 array( cfa_option, 3 ) opts; … … 92 87 opts[1] = (cfa_option){'b', "BBBB", "", b }; 93 88 opts[2] = (cfa_option){'\0', "CC", "Third arg", c }; 94 print_args_usage(1, fake_argv, opts, "Test usage", false); 95 } 96 else { 97 int status = do_wait(child); 98 print_status(status); 99 } 89 print_args_usage(1, fake_argv, opts, "Test usage", false ); 90 } else { 91 int status = do_wait( child ); 92 print_status( status ); 93 } // if 100 94 101 95 sout | "Mix of short and long and some long description:"; 102 if (pid_t child = strict_fork(); child == 0) {96 if ( pid_t child = strict_fork(); child == 0 ) { 103 97 int a, b, c; 104 98 array( cfa_option, 3 ) opts; … … 106 100 opts[1] = (cfa_option){'b', "BBBB", "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", b }; 107 101 opts[2] = (cfa_option){'\0', "CC", "Third arg", c }; 108 print_args_usage(1, fake_argv, opts, "Test usage", false); 109 } 110 else { 111 int status = do_wait(child); 112 print_status(status); 113 } 102 print_args_usage(1, fake_argv, opts, "Test usage", false ); 103 } else { 104 int status = do_wait( child ); 105 print_status( status ); 106 } // if 114 107 } 115 108 116 109 // no used 117 static int true_main( const char * path, char * env[]) { return 0; }110 static int true_main( const char * path, char * env[]) { return 0; } -
tests/errors/.expect/declaration.txt
r0522ebe ra4da45e 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
r0522ebe ra4da45e 2 2 #include <thread.hfa> 3 3 #include <mutex_stmt.hfa> 4 #include <Exception.hfa> 4 5 5 exception num_ping_pongs { int num; }; 6 vtable(num_ping_pongs) num_ping_pongs_vt; 6 ExceptionDecl( num_ping_pongs, int num; ); 7 7 8 8 thread Ping_Pong { … … 16 16 this.name = name; 17 17 cnt = 0; 18 ?{}( except, &num_ping_pongs_vt, 0);18 ?{}( except, ExceptionArgs( num_ping_pongs, 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 num_ping_pongs except{ &num_ping_pongs_vt, 0 }; 52 resumeAt( pong, except ); 51 resumeAt( pong, ExceptionInst( num_ping_pongs, 0 ) ); 53 52 } 54 53 sout | "main end"; -
tests/io/.in/manipulatorsInput.txt
r0522ebe ra4da45e 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
r0522ebe ra4da45e 7 7 // Created On : Sat Jun 8 17:58:54 2019 8 8 // Last Modified By : Peter A. Buhr 9 // Last Modified On : Sun Jan 28 11:59:55202410 // Update Count : 13 39 // Last Modified On : Mon Feb 5 21:54:49 2024 10 // Update Count : 134 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" ) | "\n";133 sin | skip( "u" ) | nl; 134 134 sin | getline( wdi( sizeof(s), s ) ); sout | "16" | s; 135 sin | getline( wdi( sizeof(s), s ), '%' ) | "\n"; sout | "17" | s;136 sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | "\n"; sout | "18" | s;135 sin | getline( wdi( sizeof(s), s ), '%' ) | nl; sout | "17" | s; 136 sin | ignore( getline( wdi( sizeof(s), s ), '%' ) ) | nl; sout | "18" | s; 137 137 138 138 sin | quoted( wdi( sizeof(s), s ) ); sout | "19" | s; -
tools/cfa.nanorc
r0522ebe ra4da45e 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)\>"13 12 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)\>" 14 15 15 16 # Declarations
Note:
See TracChangeset
for help on using the changeset viewer.