[7d9a805b] | 1 | \chapter{\CFA Enumeration} |
---|
[956299b] | 2 | |
---|
[e561551] | 3 | % \CFA supports C enumeration using the same syntax and semantics for backwards compatibility. |
---|
| 4 | % \CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages. |
---|
| 5 | % Any enumeration extensions must be intuitive to C programmers both in syntax and semantics. |
---|
| 6 | % The following sections detail all of my new contributions to enumerations in \CFA. |
---|
| 7 | \CFA extends the enumeration declaration by parameterizing with a type (like a generic type). |
---|
| 8 | \begin{clang}[identifierstyle=\linespread{0.9}\it] |
---|
| 9 | $\it enum$-specifier: |
---|
[a03ed29] | 10 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list } |
---|
| 11 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list , } |
---|
[e561551] | 12 | enum @(type-specifier$\(_{opt}\)$)@ identifier |
---|
[a03ed29] | 13 | cfa-enumerator-list: |
---|
| 14 | cfa-enumerator |
---|
| 15 | cfa-enumerator, cfa-enumerator-list |
---|
| 16 | cfa-enumerator: |
---|
[e561551] | 17 | enumeration-constant |
---|
[a03ed29] | 18 | $\it inline$ identifier |
---|
| 19 | enumeration-constant = expression |
---|
[e561551] | 20 | \end{clang} |
---|
[a03ed29] | 21 | A \newterm{\CFA enumeration}, or \newterm{\CFA enum}, has an optional type declaration in the bracket next to the @enum@ keyword. |
---|
[e561551] | 22 | Without optional type declarations, the syntax defines "opaque enums". |
---|
| 23 | Otherwise, \CFA enum with type declaration are "typed enums". |
---|
| 24 | |
---|
| 25 | \section{Opaque Enum} |
---|
| 26 | \label{s:OpaqueEnum} |
---|
| 27 | Opaque enum is a special CFA enumeration type, where the internal representation is chosen by the compiler and hidden from users. |
---|
| 28 | Compared C enum, opaque enums are more restrictive in terms of typing, and cannot be implicitly converted to integers. |
---|
| 29 | Enumerators of opaque enum cannot have initializer. Declaring initializer in the body of opaque enum results in a syntax error. |
---|
| 30 | \begin{cfa} |
---|
| 31 | enum@()@ Planets { MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE }; |
---|
| 32 | |
---|
| 33 | Planet p = URANUS; |
---|
| 34 | @int i = VENUS; // Error, VENUS cannot be converted into an integral type@ |
---|
| 35 | \end{cfa} |
---|
| 36 | Each opage enum has two @attributes@: @position@ and @label@. \CFA auto-generates @attribute functions@ @posn()@ and @label()@ for every \CFA enum to returns the respective attributes. |
---|
| 37 | \begin{cfa} |
---|
| 38 | // Auto-generated |
---|
| 39 | int posn(Planet p); |
---|
| 40 | char * s label(Planet p); |
---|
| 41 | \end{cfa} |
---|
| 42 | |
---|
| 43 | \begin{cfa} |
---|
| 44 | unsigned i = posn(VENUS); // 1 |
---|
| 45 | char * s = label(MARS); // "MARS" |
---|
| 46 | \end{cfa} |
---|
| 47 | |
---|
| 48 | % \subsection{Representation} |
---|
| 49 | \CFA uses chooses signed int as the underlying representation of an opaque enum variable, holding the value of enumeration position. Therefore, @posn()@ is in fact a cast that bypassing type system, converting an |
---|
| 50 | cfa enum to its integral representation. |
---|
| 51 | |
---|
| 52 | Labels information are stored in a global array. @label()@ is a function that maps enum position to an element of the array. |
---|
| 53 | |
---|
| 54 | \section{Typed Enum} |
---|
| 55 | \label{s:EnumeratorTyping} |
---|
| 56 | |
---|
| 57 | \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. |
---|
| 58 | 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. |
---|
| 59 | Note, the synonyms @Liz@ and @Beth@ in the last declaration. |
---|
| 60 | Because enumerators are constants, the enumeration type is implicitly @const@, so all the enumerator types in Figure~\ref{f:EumeratorTyping} are logically rewritten with @const@. |
---|
| 61 | |
---|
| 62 | \begin{figure} |
---|
| 63 | \begin{cfa} |
---|
| 64 | // integral |
---|
| 65 | enum( @char@ ) Currency { Dollar = '$\textdollar$', Cent = '$\textcent$', Yen = '$\textyen$', Pound = '$\textsterling$', Euro = 'E' }; |
---|
| 66 | enum( @signed char@ ) srgb { Red = -1, Green = 0, Blue = 1 }; |
---|
| 67 | enum( @long long int@ ) BigNum { X = 123_456_789_012_345, Y = 345_012_789_456_123 }; |
---|
| 68 | // non-integral |
---|
| 69 | enum( @double@ ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 }; |
---|
| 70 | enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i }; |
---|
| 71 | // pointer |
---|
| 72 | enum( @const char *@ ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" }; |
---|
| 73 | int i, j, k; |
---|
| 74 | enum( @int *@ ) ptr { I = &i, J = &j, K = &k }; |
---|
| 75 | enum( @int &@ ) ref { I = i, J = j, K = k }; |
---|
| 76 | // tuple |
---|
| 77 | enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$ |
---|
| 78 | // function |
---|
| 79 | void f() {...} void g() {...} |
---|
| 80 | enum( @void (*)()@ ) funs { F = f, G = g }; |
---|
| 81 | // aggregate |
---|
| 82 | struct Person { char * name; int age, height; }; |
---|
| 83 | @***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz, |
---|
| 84 | Jon = { "JONATHAN", 35, 190 } }; |
---|
| 85 | \end{cfa} |
---|
| 86 | \caption{Enumerator Typing} |
---|
| 87 | \label{f:EumeratorTyping} |
---|
| 88 | \end{figure} |
---|
| 89 | |
---|
| 90 | An advantage of the typed enumerations is eliminating the \emph{harmonizing} problem between an enumeration and companion data \see{\VRef{s:Usage}}: |
---|
| 91 | \begin{cfa} |
---|
| 92 | enum( char * ) integral_types { |
---|
| 93 | chr = "char", schar = "signed char", uschar = "unsigned char", |
---|
| 94 | sshort = "signed short int", ushort = "unsigned short int", |
---|
| 95 | sint = "signed int", usint = "unsigned int", |
---|
| 96 | ... |
---|
| 97 | }; |
---|
| 98 | \end{cfa} |
---|
| 99 | 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. |
---|
| 100 | |
---|
| 101 | While the enumeration type can be any C aggregate, the aggregate's \CFA constructors are not used to evaluate an enumerator's value. |
---|
| 102 | \CFA enumeration constants are compile-time values (static); |
---|
| 103 | calling constructors happens at runtime (dynamic). |
---|
| 104 | |
---|
| 105 | @value@ is an @attribute@ that defined for typed enum along with position and label. @values@ of a typed enum are stored in a global array of declared typed, initialized with |
---|
| 106 | value of enumerator initializers. @value()@ functions maps an enum to an elements of the array. |
---|
| 107 | |
---|
| 108 | |
---|
| 109 | \subsection{Implicit Conversion} |
---|
| 110 | C has an implicit type conversion from an enumerator to its base type @int@. |
---|
| 111 | Correspondingly, \CFA has an implicit (safe) conversion from a typed enumerator to its base type. |
---|
| 112 | \begin{cfa} |
---|
| 113 | char currency = Dollar; |
---|
| 114 | string fred = Fred; $\C{// implicit conversion from char * to \CFA string type}$ |
---|
| 115 | Person student = Beth; |
---|
| 116 | \end{cfa} |
---|
| 117 | |
---|
| 118 | % The implicit conversion is accomplished by the compiler adding @value()@ function calls as a candidate with safe cost. Therefore, the expression |
---|
| 119 | % \begin{cfa} |
---|
| 120 | % char currency = Dollar; |
---|
| 121 | % \end{cfa} |
---|
| 122 | % is equivalent to |
---|
[dc1c430] | 123 | % \begin{cfa} |
---|
[e561551] | 124 | % char currency = value(Dollar); |
---|
[dc1c430] | 125 | % \end{cfa} |
---|
[e561551] | 126 | % Such conversion an @additional@ safe |
---|
[956299b] | 127 | |
---|
[e561551] | 128 | The implicit conversion is accomplished by the resolver adding call to @value()@ functions as a resolution candidate with a @implicit@ cost. |
---|
| 129 | Implicit cost is an additional category to Aaron's cost model. It is more signicant than @unsafe@ to have |
---|
| 130 | the compiler choosing implicit conversion over the narrowing conversion; It is less signicant to @poly@ |
---|
| 131 | so that function overloaded with enum traits will be selected over the implicit. @Enum trait@ will be discussed in the chapter. |
---|
[f632117] | 132 | |
---|
[e561551] | 133 | Therefore, \CFA conversion cost is 8-tuple |
---|
| 134 | @@(unsafe, implicit, poly, safe, sign, vars, specialization, reference)@@ |
---|
[f632117] | 135 | |
---|
[e561551] | 136 | \section{Auto Initialization} |
---|
[956299b] | 137 | |
---|
[e561551] | 138 | C auto-initialization works for the integral type @int@ with constant expressions. |
---|
[956299b] | 139 | \begin{cfa} |
---|
[e561551] | 140 | enum Alphabet ! { |
---|
| 141 | A = 'A', B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, |
---|
| 142 | a = 'a', b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z |
---|
| 143 | }; |
---|
[956299b] | 144 | \end{cfa} |
---|
[e561551] | 145 | The complexity of the constant expression depends on the level of runtime computation the compiler implements, \eg \CC \lstinline[language={[GNU]C++}]{constexpr} provides complex compile-time computation across multiple types, which blurs the compilation/runtime boundary. |
---|
[956299b] | 146 | |
---|
[e561551] | 147 | % The notion of auto-initialization can be generalized in \CFA through the trait @AutoInitializable@. |
---|
| 148 | % \begin{cfa} |
---|
| 149 | % forall(T) @trait@ AutoInitializable { |
---|
| 150 | % void ?{}( T & o, T v ); $\C{// initialization}$ |
---|
| 151 | % void ?{}( T & t, zero_t ); $\C{// 0}$ |
---|
| 152 | % T ?++( T & t); $\C{// increment}$ |
---|
| 153 | % }; |
---|
| 154 | % \end{cfa} |
---|
| 155 | % In addition, there is an implicit enumeration counter, @ecnt@ of type @T@, managed by the compiler. |
---|
| 156 | % For example, the type @Odd@ satisfies @AutoInitializable@: |
---|
| 157 | % \begin{cfa} |
---|
| 158 | % struct Odd { int i; }; |
---|
| 159 | % void ?{}( Odd & o, int v ) { if ( v & 1 ) o.i = v; else /* error not odd */ ; }; |
---|
| 160 | % void ?{}( Odd & o, zero_t ) { o.i = 1; }; |
---|
| 161 | % Odd ?++( Odd o ) { return (Odd){ o.i + 2 }; }; |
---|
| 162 | % \end{cfa} |
---|
| 163 | % and implicit initialization is available. |
---|
| 164 | % \begin{cfa} |
---|
| 165 | % enum( Odd ) { A, B, C = 7, D }; $\C{// 1, 3, 7, 9}$ |
---|
| 166 | % \end{cfa} |
---|
| 167 | % where the compiler performs the following transformation and runs the code. |
---|
| 168 | % \begin{cfa} |
---|
| 169 | % enum( Odd ) { |
---|
| 170 | % ?{}( ecnt, @0@ } ?{}( A, ecnt }, ?++( ecnt ) ?{}( B, ecnt ), |
---|
| 171 | % ?{}( ecnt, 7 ) ?{}( C, ecnt ), ?++( ecnt ) ?{}( D, ecnt ) |
---|
| 172 | % }; |
---|
| 173 | % \end{cfa} |
---|
| 174 | |
---|
| 175 | The notion of auto-initialization is generalized in \CFA enum in the following way: |
---|
| 176 | Enumerator e is the first enumerator of \CFA enumeration E with base type T. If e declares no no initializer, e is auto-initialized by the $zero\_t$ constructor of T. |
---|
| 177 | \CFA reports a compile time error if T has no $zero\_t$ constructor. |
---|
| 178 | Enumerator e is an enumerator of base-type T enumeration E that position i, where $i \neq 0$. And d is the enumerator with position @i-1@, e is auto-initialized with |
---|
| 179 | the result of @value(d)++@. If operator @?++@ is not defined for type T, \CFA reports a compile time error. |
---|
[956299b] | 180 | |
---|
[e561551] | 181 | Unfortunately, auto-initialization is not implemented because \CFA is only a transpiler, relying on generated C code to perform the detail work. |
---|
| 182 | C does not have the equivalent of \CC \lstinline[language={[GNU]C++}]{constexpr}, and it is currently beyond the scope of the \CFA project to implement a complex runtime interpreter in the transpiler. |
---|
| 183 | Nevertheless, the necessary language concepts exist to support this feature. |
---|
| 184 | |
---|
| 185 | |
---|
| 186 | |
---|
| 187 | \section{Enumeration Inheritance} |
---|
| 188 | |
---|
| 189 | \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). |
---|
[956299b] | 190 | |
---|
| 191 | \begin{cfa} |
---|
[e561551] | 192 | enum( char * ) Names { /* as above */ }; |
---|
| 193 | enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" }; |
---|
| 194 | enum( char * ) Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" }; |
---|
[956299b] | 195 | \end{cfa} |
---|
[e561551] | 196 | |
---|
| 197 | Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Names@ by containment, and a @Names@ enumeration is a @subtype@ of enumeration @Name2@. |
---|
| 198 | Note, that enumerators must be unique in inheritance but enumerator values may be repeated. |
---|
| 199 | |
---|
| 200 | % The enumeration type for the inheriting type must be the same as the inherited type; |
---|
| 201 | % hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@. |
---|
| 202 | % When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important. |
---|
| 203 | Specifically, the inheritance relationship for @Names@ is: |
---|
[956299b] | 204 | \begin{cfa} |
---|
[e561551] | 205 | Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\C{// enum type of Names}$ |
---|
[956299b] | 206 | \end{cfa} |
---|
[e561551] | 207 | |
---|
| 208 | Inlined from \CFA enumeration @O@, new enumeration @N@ copies all enumerators from @O@, including those @O@ obtains through inheritance. Enumerators inherited from @O@ |
---|
| 209 | keeps same @label@ and @value@, but @position@ may shift to the right if other enumerators or inline enumeration declared in prior of @inline A@. |
---|
[956299b] | 210 | \begin{cfa} |
---|
[e561551] | 211 | enum() Phynchocephalia { Tuatara }; |
---|
| 212 | enum() Squamata { Snake, Lizard }; |
---|
| 213 | enum() Lepidosauromorpha { inline Phynchocephalia, inline Squamata, Kuehneosauridae }; |
---|
| 214 | \end{cfa} |
---|
| 215 | Snake, for example, has the position 0 in Squamata, but 1 in Lepidosauromorpha as Tuatara inherited from Phynchocephalia is position 0 in Lepidosauromorpha. |
---|
| 216 | |
---|
| 217 | A subtype enumeration can be casted, or implicitly converted into its supertype, with a safe cost. |
---|
| 218 | \begin{cfa} |
---|
| 219 | enum Squamata squamata_lizard = Lizard; |
---|
| 220 | posn(quamata_lizard); // 1 |
---|
| 221 | enum Lepidosauromorpha lepidosauromorpha_lizard = squamata_lizard; |
---|
| 222 | posn(lepidosauromorpha_lizard); // 2 |
---|
| 223 | void foo( Lepidosauromorpha l ); |
---|
| 224 | foo( squamata_lizard ); |
---|
| 225 | posn( (Lepidosauromorpha) squamata_lizard ); // 2 |
---|
| 226 | |
---|
| 227 | Lepidosauromorpha s = Snake; |
---|
| 228 | \end{cfa} |
---|
| 229 | The last expression in the preceding example is umabigious. While both @Squamata.Snake@ and @Lepidosauromorpha.Snake@ are valid candidate, @Squamata.Snake@ has |
---|
| 230 | an associated safe cost and \CFA select the zero cost candidate @Lepidosauromorpha.Snake@. |
---|
| 231 | |
---|
| 232 | As discussed in \VRef{s:OpaqueEnum}, \CFA chooses position as a representation of \CFA enum. Conversion involves both change of typing |
---|
| 233 | and possibly @position@. |
---|
| 234 | |
---|
| 235 | When converting a subtype to a supertype, the position can only be a larger value. The difference between the position in subtype and in supertype is an "offset". |
---|
| 236 | \CFA runs a the following algorithm to determine the offset for an enumerator to a super type. |
---|
| 237 | % In a summary, \CFA loops over members (include enumerators and inline enums) of the supertype. |
---|
| 238 | % If the member is the matching enumerator, the algorithm returns its position. |
---|
| 239 | % If the member is a inline enumeration, the algorithm trys to find the enumerator in the inline enumeration. If success, it returns the position of enumerator in the inline enumeration, plus |
---|
| 240 | % the position in the current enumeration. Otherwises, it increase the offset by the size of inline enumeration. |
---|
| 241 | |
---|
| 242 | \begin{cfa} |
---|
| 243 | struct Enumerator; |
---|
| 244 | struct CFAEnum { |
---|
| 245 | vector<variant<CFAEnum, Enumerator>> members; |
---|
| 246 | }; |
---|
| 247 | pair<bool, int> calculateEnumOffset( CFAEnum dst, Enumerator e ) { |
---|
| 248 | int offset = 0; |
---|
| 249 | for( auto v: dst.members ) { |
---|
| 250 | if ( v.holds_alternative<Enumerator>() ) { |
---|
| 251 | auto m = v.get<Enumerator>(); |
---|
| 252 | if ( m == e ) return make_pair( true, 0 ); |
---|
| 253 | offset++; |
---|
| 254 | } else { |
---|
| 255 | auto p = calculateEnumOffset( v, e ); |
---|
| 256 | if ( p.first ) return make_pair( true, offset + p.second ); |
---|
| 257 | offset += p.second; |
---|
| 258 | } |
---|
| 259 | } |
---|
| 260 | return make_pair( false, offset ); |
---|
[956299b] | 261 | } |
---|
| 262 | \end{cfa} |
---|
[e561551] | 263 | |
---|
| 264 | % \begin{cfa} |
---|
| 265 | % Names fred = Name.Fred; |
---|
| 266 | % (Names2) fred; (Names3) fred; (Name3) Names.Jack; $\C{// cast to super type}$ |
---|
| 267 | % Names2 fred2 = fred; Names3 fred3 = fred2; $\C{// assign to super type}$ |
---|
| 268 | % \end{cfa} |
---|
| 269 | For the given function prototypes, the following calls are valid. |
---|
| 270 | \begin{cquote} |
---|
| 271 | \begin{tabular}{ll} |
---|
| 272 | \begin{cfa} |
---|
| 273 | void f( Names ); |
---|
| 274 | void g( Names2 ); |
---|
| 275 | void h( Names3 ); |
---|
| 276 | void j( const char * ); |
---|
| 277 | \end{cfa} |
---|
| 278 | & |
---|
| 279 | \begin{cfa} |
---|
| 280 | f( Fred ); |
---|
| 281 | g( Fred ); g( Jill ); |
---|
| 282 | h( Fred ); h( Jill ); h( Sue ); |
---|
| 283 | j( Fred ); j( Jill ); j( Sue ); j( "WILL" ); |
---|
| 284 | \end{cfa} |
---|
| 285 | \end{tabular} |
---|
| 286 | \end{cquote} |
---|
| 287 | 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. |
---|
| 288 | |
---|
[f632117] | 289 | \section{Enumerator Control Structures} |
---|
[956299b] | 290 | |
---|
| 291 | Enumerators can be used in multiple contexts. |
---|
| 292 | In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution). |
---|
| 293 | However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts. |
---|
[09dd830] | 294 | In these contexts, a programmer's initition assumes an implicit conversion to position. |
---|
[956299b] | 295 | |
---|
[09dd830] | 296 | For example, an intuitive use of enumerations is with the \CFA @switch@/@choose@ statement, where @choose@ performs an implicit @break@ rather than a fall-through at the end of a @case@ clause. |
---|
[10a99d87] | 297 | (For this discussion, ignore the fact that @case@ requires a compile-time constant.) |
---|
| 298 | \begin{cfa}[belowskip=0pt] |
---|
[956299b] | 299 | enum Count { First, Second, Third, Fourth }; |
---|
| 300 | Count e; |
---|
| 301 | \end{cfa} |
---|
[10a99d87] | 302 | \begin{cquote} |
---|
| 303 | \setlength{\tabcolsep}{15pt} |
---|
| 304 | \noindent |
---|
| 305 | \begin{tabular}{@{}ll@{}} |
---|
| 306 | \begin{cfa}[aboveskip=0pt] |
---|
[956299b] | 307 | |
---|
| 308 | choose( e ) { |
---|
| 309 | case @First@: ...; |
---|
| 310 | case @Second@: ...; |
---|
| 311 | case @Third@: ...; |
---|
| 312 | case @Fourth@: ...; |
---|
| 313 | } |
---|
| 314 | \end{cfa} |
---|
| 315 | & |
---|
[10a99d87] | 316 | \begin{cfa}[aboveskip=0pt] |
---|
[956299b] | 317 | // rewrite |
---|
| 318 | choose( @value@( e ) ) { |
---|
| 319 | case @value@( First ): ...; |
---|
| 320 | case @value@( Second ): ...; |
---|
| 321 | case @value@( Third ): ...; |
---|
| 322 | case @value@( Fourth ): ...; |
---|
| 323 | } |
---|
| 324 | \end{cfa} |
---|
| 325 | \end{tabular} |
---|
| 326 | \end{cquote} |
---|
[de3a579] | 327 | Here, the intuitive code on the left is implicitly transformed into the standard implementation on the right, using the value of the enumeration variable and enumerators. |
---|
[f9da761] | 328 | However, this implementation is fragile, \eg if the enumeration is changed to: |
---|
[956299b] | 329 | \begin{cfa} |
---|
| 330 | enum Count { First, Second, Third @= First@, Fourth }; |
---|
| 331 | \end{cfa} |
---|
[10a99d87] | 332 | making @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicate @case@ clauses. |
---|
[ec20ab9] | 333 | To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context. |
---|
[09dd830] | 334 | For conditional clauses and switch statements, \CFA uses the robust position implementation. |
---|
[956299b] | 335 | \begin{cfa} |
---|
[10a99d87] | 336 | if ( @posn@( e ) < posn( Third ) ) ... |
---|
| 337 | choose( @posn@( e ) ) { |
---|
| 338 | case @posn@( First ): ...; |
---|
| 339 | case @posn@( Second ): ...; |
---|
| 340 | case @posn@( Third ): ...; |
---|
| 341 | case @posn@( Fourth ): ...; |
---|
[956299b] | 342 | } |
---|
| 343 | \end{cfa} |
---|
| 344 | |
---|
[10a99d87] | 345 | \CFA provides a special form of for-control for enumerating through an enumeration, where the range is a type. |
---|
[956299b] | 346 | \begin{cfa} |
---|
[10a99d87] | 347 | for ( cx; @Count@ ) { sout | cx | nonl; } sout | nl; |
---|
| 348 | for ( cx; +~= Count ) { sout | cx | nonl; } sout | nl; |
---|
| 349 | for ( cx; -~= Count ) { sout | cx | nonl; } sout | nl; |
---|
| 350 | First Second Third Fourth |
---|
| 351 | First Second Third Fourth |
---|
| 352 | Fourth Third Second First |
---|
[956299b] | 353 | \end{cfa} |
---|
[10a99d87] | 354 | The enumeration type is syntax sugar for looping over all enumerators and assigning each enumerator to the loop index, whose type is inferred from the range type. |
---|
| 355 | The prefix @+~=@ or @-~=@ iterate forward or backwards through the inclusive enumeration range, where no prefix defaults to @+~=@. |
---|
[7d9a805b] | 356 | |
---|
[10a99d87] | 357 | C has an idiom for @if@ and loop predicates of comparing the predicate result ``not equal to 0''. |
---|
[dc1c430] | 358 | \begin{cfa} |
---|
[10a99d87] | 359 | if ( x + y /* != 0 */ ) ... |
---|
| 360 | while ( p /* != 0 */ ) ... |
---|
[dc1c430] | 361 | \end{cfa} |
---|
[10a99d87] | 362 | This idiom extends to enumerations because there is a boolean conversion in terms of the enumeration value, if and only if such a conversion is available. |
---|
| 363 | For example, such a conversion exists for all numerical types (integral and floating-point). |
---|
| 364 | It is possible to explicitly extend this idiom to any typed enumeration by overloading the @!=@ operator. |
---|
[dc1c430] | 365 | \begin{cfa} |
---|
[10a99d87] | 366 | bool ?!=?( Name n, zero_t ) { return n != Fred; } |
---|
| 367 | Name n = Mary; |
---|
| 368 | if ( n ) ... // result is true |
---|
[dc1c430] | 369 | \end{cfa} |
---|
[10a99d87] | 370 | Specialize meanings are also possible. |
---|
[dc1c430] | 371 | \begin{cfa} |
---|
| 372 | enum(int) ErrorCode { Normal = 0, Slow = 1, Overheat = 1000, OutOfResource = 1001 }; |
---|
[10a99d87] | 373 | bool ?!=?( ErrorCode ec, zero_t ) { return ec >= Overheat; } |
---|
| 374 | ErrorCode code = ...; |
---|
| 375 | if ( code ) { problem(); } |
---|
[dc1c430] | 376 | \end{cfa} |
---|
[7d9a805b] | 377 | |
---|
[f632117] | 378 | |
---|
[10a99d87] | 379 | \section{Enumeration Dimension} |
---|
[f632117] | 380 | |
---|
[10a99d87] | 381 | \VRef{s:EnumeratorTyping} introduced the harmonizing problem between an enumeration and secondary information. |
---|
| 382 | When possible, using a typed enumeration for the secondary information is the best approach. |
---|
| 383 | However, there are times when combining these two types is not possible. |
---|
| 384 | For example, the secondary information might precede the enumeration and/or its type is needed directly to declare parameters of functions. |
---|
| 385 | In these cases, having secondary arrays of the enumeration size are necessary. |
---|
[f632117] | 386 | |
---|
[10a99d87] | 387 | To support some level of harmonizing in these cases, an array dimension can be defined using an enumerator type, and the enumerators used as subscripts. |
---|
[dc1c430] | 388 | \begin{cfa} |
---|
[10a99d87] | 389 | enum E { A, B, C, N }; // possibly predefined |
---|
| 390 | float H1[N] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // C |
---|
| 391 | float H2[@E@] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // CFA |
---|
[d69f7114] | 392 | \end{cfa} |
---|
[10a99d87] | 393 | (Note, C uses the symbol, @'='@ for designator initialization, but \CFA had to change to @':'@ because of problems with tuple syntax.) |
---|
| 394 | This approach is also necessary for a predefined typed enumeration (unchangeable), when additional secondary-information need to be added. |
---|
[d69f7114] | 395 | |
---|
[bc17be98] | 396 | |
---|
[6f47834] | 397 | \section{Enumeration I/O} |
---|
| 398 | |
---|
| 399 | As seen in multiple examples, enumerations can be printed and the default property printed is the enumerator's label, which is similar in other programming languages. |
---|
| 400 | However, very few programming languages provide a mechanism to read in enumerator values. |
---|
| 401 | Even the @boolean@ type in many languages does not have a mechanism for input using the enumerators @true@ or @false@. |
---|
| 402 | \VRef[Figure]{f:EnumerationI/O} show \CFA enumeration input based on the enumerator labels. |
---|
| 403 | When the enumerator labels are packed together in the input stream, the input algorithm scans for the longest matching string. |
---|
| 404 | For basic types in \CFA, the constants use to initialize a variable in a program are available to initialize a variable using input, where strings constants can be quoted or unquoted. |
---|
| 405 | |
---|
| 406 | \begin{figure} |
---|
| 407 | \begin{cquote} |
---|
| 408 | \setlength{\tabcolsep}{15pt} |
---|
| 409 | \begin{tabular}{@{}ll@{}} |
---|
| 410 | \begin{cfa} |
---|
| 411 | int main() { |
---|
| 412 | enum(int ) E { BBB = 3, AAA, AA, AB, B }; |
---|
| 413 | E e; |
---|
| 414 | |
---|
| 415 | for () { |
---|
| 416 | try { |
---|
| 417 | @sin | e@; |
---|
| 418 | } catch( missing_data * ) { |
---|
| 419 | sout | "missing data"; |
---|
| 420 | continue; // try again |
---|
| 421 | } |
---|
| 422 | if ( eof( sin ) ) break; |
---|
| 423 | sout | e | "= " | value( e ); |
---|
| 424 | } |
---|
| 425 | } |
---|
| 426 | \end{cfa} |
---|
| 427 | & |
---|
| 428 | \begin{cfa} |
---|
| 429 | $\rm input$ |
---|
| 430 | BBBABAAAAB |
---|
| 431 | BBB AAA AA AB B |
---|
| 432 | |
---|
| 433 | $\rm output$ |
---|
| 434 | BBB = 3 |
---|
| 435 | AB = 6 |
---|
| 436 | AAA = 4 |
---|
| 437 | AB = 6 |
---|
| 438 | BBB = 3 |
---|
| 439 | AAA = 4 |
---|
| 440 | AA = 5 |
---|
| 441 | AB = 6 |
---|
| 442 | B = 7 |
---|
| 443 | |
---|
| 444 | \end{cfa} |
---|
| 445 | \end{tabular} |
---|
| 446 | \end{cquote} |
---|
| 447 | \caption{Enumeration I/O} |
---|
| 448 | \label{f:EnumerationI/O} |
---|
| 449 | \end{figure} |
---|
| 450 | |
---|
| 451 | |
---|
| 452 | |
---|
[7d9a805b] | 453 | \section{Planet Example} |
---|
| 454 | |
---|
[f632117] | 455 | \VRef[Figure]{f:PlanetExample} shows an archetypal enumeration example illustrating most of the \CFA enumeration features. |
---|
[caaf424] | 456 | @Planet@ is an enumeration of type @MR@. |
---|
[09dd830] | 457 | Each planet enumerator is initialized to a specific mass/radius, @MR@, value. |
---|
[caaf424] | 458 | The unnamed enumeration provides the gravitational-constant enumerator @G@. |
---|
| 459 | Function @surfaceGravity@ uses the @with@ clause to remove @p@ qualification from fields @mass@ and @radius@. |
---|
[29092213] | 460 | The program main uses the pseudo function @countof@ to obtain the number of enumerators in @Planet@, and safely converts the random value into a @Planet@ enumerator using @fromInt@. |
---|
| 461 | The resulting random orbital-body is used in a @choose@ statement. |
---|
[09dd830] | 462 | The enumerators in the @case@ clause use the enumerator position for testing. |
---|
[29092213] | 463 | The prints use @label@ to print an enumerator's name. |
---|
| 464 | Finally, a loop enumerates through the planets computing the weight on each planet for a given earth mass. |
---|
| 465 | The print statement does an equality comparison with an enumeration variable and enumerator (@p == MOON@). |
---|
[7d9a805b] | 466 | |
---|
| 467 | \begin{figure} |
---|
[caaf424] | 468 | \small |
---|
[7d9a805b] | 469 | \begin{cfa} |
---|
[10a99d87] | 470 | struct MR { double mass, radius; }; $\C{// planet definition}$ |
---|
[29092213] | 471 | enum( @MR@ ) Planet { $\C{// typed enumeration}$ |
---|
[f632117] | 472 | // mass (kg) radius (km) |
---|
| 473 | MERCURY = { 0.330_E24, 2.4397_E6 }, |
---|
| 474 | VENUS = { 4.869_E24, 6.0518_E6 }, |
---|
[7d9a805b] | 475 | EARTH = { 5.976_E24, 6.3781_E6 }, |
---|
[29092213] | 476 | MOON = { 7.346_E22, 1.7380_E6 }, $\C{// not a planet}$ |
---|
[f632117] | 477 | MARS = { 0.642_E24, 3.3972_E6 }, |
---|
| 478 | JUPITER = { 1898._E24, 71.492_E6 }, |
---|
| 479 | SATURN = { 568.8_E24, 60.268_E6 }, |
---|
| 480 | URANUS = { 86.86_E24, 25.559_E6 }, |
---|
| 481 | NEPTUNE = { 102.4_E24, 24.746_E6 }, |
---|
[29092213] | 482 | PLUTO = { 1.303_E22, 1.1880_E6 }, $\C{// not a planet}$ |
---|
[7d9a805b] | 483 | }; |
---|
[29092213] | 484 | enum( double ) { G = 6.6743_E-11 }; $\C{// universal gravitational constant (m3 kg-1 s-2)}$ |
---|
[caaf424] | 485 | static double surfaceGravity( Planet p ) @with( p )@ { |
---|
[29092213] | 486 | return G * mass / ( radius @\@ 2 ); $\C{// no qualification, exponentiation}$ |
---|
[7d9a805b] | 487 | } |
---|
| 488 | static double surfaceWeight( Planet p, double otherMass ) { |
---|
| 489 | return otherMass * surfaceGravity( p ); |
---|
| 490 | } |
---|
| 491 | int main( int argc, char * argv[] ) { |
---|
[29092213] | 492 | if ( argc != 2 ) @exit@ | "Usage: " | argv[0] | "earth-weight"; // terminate program |
---|
[7d9a805b] | 493 | double earthWeight = convert( argv[1] ); |
---|
[d69f7114] | 494 | double earthMass = earthWeight / surfaceGravity( EARTH ); |
---|
[29092213] | 495 | Planet rp = @fromInt@( prng( @countof@( Planet ) ) ); $\C{// select random orbiting body}$ |
---|
| 496 | @choose( rp )@ { $\C{// implicit breaks}$ |
---|
[caaf424] | 497 | case MERCURY, VENUS, EARTH, MARS: |
---|
[62a38e7] | 498 | sout | @rp@ | "is a rocky planet"; |
---|
[29092213] | 499 | case JUPITER, SATURN, URANUS, NEPTUNE: |
---|
[62a38e7] | 500 | sout | rp | "is a gas-giant planet"; |
---|
[caaf424] | 501 | default: |
---|
[62a38e7] | 502 | sout | rp | "is not a planet"; |
---|
[caaf424] | 503 | } |
---|
[29092213] | 504 | for ( @p; Planet@ ) { $\C{// enumerate}$ |
---|
[62a38e7] | 505 | sout | "Your weight on" | ( @p == MOON@ ? "the" : " " ) | p |
---|
[29092213] | 506 | | "is" | wd( 1,1, surfaceWeight( p, earthMass ) ) | "kg"; |
---|
[7d9a805b] | 507 | } |
---|
| 508 | } |
---|
[f632117] | 509 | $\$$ planet 100 |
---|
[caaf424] | 510 | JUPITER is a gas-giant planet |
---|
[f632117] | 511 | Your weight on MERCURY is 37.7 kg |
---|
| 512 | Your weight on VENUS is 90.5 kg |
---|
| 513 | Your weight on EARTH is 100.0 kg |
---|
[caaf424] | 514 | Your weight on the MOON is 16.6 kg |
---|
[f632117] | 515 | Your weight on MARS is 37.9 kg |
---|
| 516 | Your weight on JUPITER is 252.8 kg |
---|
| 517 | Your weight on SATURN is 106.6 kg |
---|
| 518 | Your weight on URANUS is 90.5 kg |
---|
| 519 | Your weight on NEPTUNE is 113.8 kg |
---|
[29092213] | 520 | Your weight on PLUTO is 6.3 kg |
---|
[7d9a805b] | 521 | \end{cfa} |
---|
| 522 | \caption{Planet Example} |
---|
| 523 | \label{f:PlanetExample} |
---|
| 524 | \end{figure} |
---|