Changeset f6e8c67
- Timestamp:
- Mar 6, 2024, 6:06:43 AM (13 months ago)
- Branches:
- master
- Children:
- 647d633
- Parents:
- bbf2cb1 (diff), af60383 (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:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified doc/theses/jiada_liang_MMath/relatedwork.tex ¶
rbbf2cb1 rf6e8c67 2 2 \label{s:RelatedWork} 3 3 4 An enumeration type exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}, and the algebraic data-type in functional programming. 4 In general, an \Newterm{algebraic data type} (ADT) is a composite type, \ie, a type formed by combining other types. 5 Three common classes of algebraic types are \Newterm{array type}, \ie homogeneous types, \Newterm{product type}, \ie heterogeneous tuples and records (structures), and \Newterm{sum type}, \ie tagged product-types (unions). 6 Enumerated types are a special case of product/sum types with non-mutable fields, \ie initialized (constructed) once at the type's declaration, possible restricted to compile-time initialization. 7 Values of algebraic types are access by subscripting, field qualification, or type (pattern) matching. 8 9 Enumeration types exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, Haskell~\cite{Haskell} \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}. 5 10 Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions. 6 11 7 8 12 \section{Pascal} 9 10 \lstnewenvironment{pascal}[1][]{% necessary 11 \lstset{ 12 language=pascal, 13 escapechar=\$, % LaTeX escape in code 14 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 15 }% lstset 16 \lstset{#1}% necessary 17 }{} 13 \lstnewenvironment{pascal}[1][]{\lstset{language=pascal,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 18 14 19 15 Classic Pascal has the \lstinline[language=pascal]{const} declaration binding a name to a constant literal/expression. … … 22 18 PI = 3.14159; Plus = '+'; Fred = 'Fred'; 23 19 \end{pascal} 24 The enumerator type is inferred from the constant-expression type. 25 There is no notion of an ordered set, modulo the \lstinline[language=pascal]{set of} type. 20 Here, there is no enumeration because there is no specific type (pseudo enumeration). 21 Hence, there is no notion of a (possibly ordered) set, modulo the \lstinline[language=pascal]{set of} type. 22 The type of each constant name (enumerator) is inferred from the constant-expression type. 26 23 27 24 Free Pascal~\cite[\S~3.1.1]{FreePascal} is a modern, object-oriented version of classic Pascal, with a C-style enumeration type. … … 42 39 43 40 \section{Ada} 44 45 \lstnewenvironment{ada}[1][]{% necessary 46 \lstset{ 47 language=[2005]Ada, 48 escapechar=\$, % LaTeX escape in code 49 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 50 literate={'}{\ttfamily'\!}1 % remove '-' literate as comment 51 }% lstset 52 \lstset{#1}% necessary 53 }{} 41 \lstnewenvironment{ada}[1][]{\lstset{language=[2005]Ada,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},literate={'}{\ttfamily'\!}1}\lstset{#1}}{} 54 42 55 43 An Ada enumeration type is an ordered list of constants, called \Newterm{literals} (enumerators). 56 44 \begin{ada} 57 type RGB is ( @Red@, @Green@, Blue ); -- 3 literals (enumerators)45 type RGB is ( Red, Green, Blue ); -- 3 literals (enumerators) 58 46 \end{ada} 59 No other enumerators are assignable to objects of this type.47 Object initialization and assignment are restricted to the enumerators of this type. 60 48 Enumerators without an explicitly designated constant value are auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by 1. 61 49 To explicitly set enumerator values, \emph{all} enumerators must be set in \emph{ascending} order, \ie there is no auto-initialization. … … 68 56 (0, 10, RED) (1, 20, GREEN) (2, 30, BLUE) 69 57 \end{ada} 58 Note, Ada is case-\emph{insensitive} so names may appear in multiple forms and still be the same, \eg @Red@ and @RED@ (a questionable design decision). 70 59 71 60 Like C, Ada enumerators are unscoped, \ie enumerators declared inside of an enum are visible (projected) into the enclosing scope. 72 Note, Ada is case-\emph{insensitive} so names may appear in multiple forms and still be the same name (a questionable design decision). 73 The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relation is given implicitly by the sequence of enumerators, which is always ascending. 61 The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of enumerators, which is always ascending. 74 62 75 63 Ada enumerators are overloadable. … … 136 124 \begin{ada} 137 125 type Operator is ( '+', '-', '*', '/' ); 138 Op : Operator;139 126 \end{ada} 140 127 which is syntactic sugar for the label and not character literals from the predefined type @Character@. 141 The purpose is readability using character literals rather than names.128 The purpose is strictly readability using character literals rather than names. 142 129 \begin{ada} 143 Op := '+'; 144 case Op is -- all enumerators must appear 145 when '+' => ... ; 146 when '-' => ... ; 147 when '*' => ... ; 148 when '/' => ... ; 149 end case; 130 Op : Operator := '+'; 131 if Op = '+' or else Op = '-' then ... ; 132 elsif Op = '*' or else Op = '/' then ... ; end if; 150 133 \end{ada} 151 Arrays of character enumerators can be treated as strings.134 Interestingly, arrays of character enumerators can be treated as strings. 152 135 \begin{ada} 153 136 Ops : array( 0..3 ) of Operator; 154 137 Ops := @"+-*/"@; -- string assignment to array elements 155 138 Ops := @"+-" & "*/"@; -- string concatenation and assignment 156 for Op of Ops loop157 Put_Line( Operator'Image( Op ) );158 end loop;159 139 \end{ada} 160 140 Ada's @Character@ type is defined as a character enumeration across all Latin-1 characters. 161 141 162 Ada's boolean type is defined asa special enumeration, which can be used in conditions.142 Ada's boolean type is also a special enumeration, which can be used in conditions. 163 143 \begin{ada} 164 144 type Boolean is (False, True); -- False / True not keywords … … 177 157 Hence, the ordering of the enumerators is crucial to provide the necessary ranges. 178 158 179 An enumeration type can be used in the Ada \lstinline[language=ada]{case} ( switch) anditerating constructs.159 An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a default) or iterating constructs. 180 160 \begin{cquote} 181 161 \lstDeleteShortInline@ … … 233 213 \section{\CC} 234 214 \label{s:C++RelatedWork} 235 236 \lstnewenvironment{c++}[1][]{% necessary 237 \lstset{ 238 language=[GNU]C++, 239 escapechar=\$, % LaTeX escape in code 240 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 241 }% lstset 242 \lstset{#1}% necessary 243 }{} 215 \lstnewenvironment{c++}[1][]{\lstset{language=[GNU]C++,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 244 216 245 217 \CC is largely backwards compatible with C, so it inherited C's enumerations. … … 288 260 enum class E { A, B, C }; 289 261 E e = @E::@A; $\C{// qualified enumerator}$ 290 e = B; $\C{// B not in scope}$262 e = B; $\C{// error: B not in scope}$ 291 263 \end{c++} 292 264 \CC{20} supports explicit unscoping with a \lstinline[language=c++]{using enum} declaration. … … 303 275 enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 }; 304 276 \end{c++} 305 There is no implicit conversion from the \lstinline[language=c++]{enum class} type and to itstype.277 There is no implicit conversion from the \lstinline[language=c++]{enum class} type to its declared type. 306 278 \begin{c++} 307 279 rgb crgb = rgb::Red; 308 char ch = rgb::Red; ch = crgb; $\C{// disallowed}$280 char ch = rgb::Red; ch = crgb; $\C{// error}$ 309 281 \end{c++} 310 Finally, there is no mechanism to iterate through an enumeration nor use the enumeration type to declare an array dimension. 282 Finally, enumerations can be used in the @switch@ statement but there is no mechanism to iterate through an enumeration. 283 An enumeration type cannot declare an array dimension but can be used as a subscript. 284 There is no mechanism to subtype or inherit from enumerations. 311 285 312 286 313 287 \section{C\raisebox{-0.7ex}{\LARGE$^\sharp$}\xspace} % latex bug: cannot use \relsize{2} so use \LARGE 314 315 \lstnewenvironment{csharp}[1][]{% necessary 316 \lstset{ 317 language=[Sharp]C, 318 escapechar=\$, % LaTeX escape in code 319 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 320 }% lstset 321 \lstset{#1}% necessary 322 }{} 288 \label{s:Csharp} 289 \lstnewenvironment{csharp}[1][]{\lstset{language=[Sharp]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 323 290 324 291 % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx 325 292 326 \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to C/\CC enumeration.293 \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to the C/\CC enumeration. 327 294 \begin{csharp} 328 enum Weekday : byte { Mon day, Tuesday, Wednesday, Thursday@ = 10@, Friday, Saturday, Sunday@,@ };295 enum Weekday : byte { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ }; 329 296 \end{csharp} 330 The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@) 297 The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@). 331 298 A method cannot be defined in an enumeration type. 332 299 As well, there is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts. 333 300 \begin{csharp} 334 int day = (int)Weekday.Fri day;$\C{// day == 10}$335 Weekday weekday = (Weekdays)42; $\C{// weekday == 42}$336 Console.WriteLine( Weekday.Fri day ); $\C{// print Friday}$337 string mon = Weekday.Mon day.ToString();301 int day = (int)Weekday.Fri; $\C{// day == 10}$ 302 Weekday weekday = (Weekdays)42; $\C{// weekday == 42, logically invalid}$ 303 Console.WriteLine( Weekday.Fri ); $\C{// print Fri}$ 304 string mon = Weekday.Mon.ToString(); $\C{// mon == "Mon"}$ 338 305 \end{csharp} 339 306 340 The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable .307 The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation). 341 308 \begin{csharp} 342 309 foreach ( Weekday constant in @Enum.GetValues@( typeof(Weekday) ) ) { … … 348 315 \begin{csharp} 349 316 @[Flags]@ public enum Weekday { 350 None = 0x0, Mon day = 0x1, Tuesday = 0x2, Wednesday= 0x4,351 Thu rsday = 0x8, Friday = 0x10, Saturday = 0x20, Sunday= 0x40,352 Weekend = @Sat urday | Sunday@,353 Weekdays = @Mon day | Tuesday | Wednesday | Thursday | Friday@354 } 355 Weekday meetings = @Weekday.Mon day | Weekday.Wednesday@; // 0x5317 None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4, 318 Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40, 319 Weekend = @Sat | Sun@, 320 Weekdays = @Mon | Tue | Wed | Thu | Fri@ 321 } 322 Weekday meetings = @Weekday.Mon | Weekday.Wed@; // 0x5 356 323 \end{csharp} 357 324 358 \Csharp supports an enumeration class to embed enumeration operations, where the enumerators are objects. 325 \VRef[Figure]{CsharpFreeVersusClass} shows an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class. 326 The key observation is that an enumeration class is just a structuring mechanism without any additional semantics. 327 328 % https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0 329 330 \begin{figure} 331 \centering 332 \lstDeleteShortInline@ 333 \begin{tabular}{@{}l|l@{}} 334 \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ 335 \hline 359 336 \begin{csharp} 360 public class PaymentType : Enumeration { 361 public static readonly PaymentType DebitCard = new PaymentType(0); 362 public static readonly PaymentType CreditCard = new PaymentType(1); 363 private PaymentType(int value, [CallerMemberName] string name = null) : base(value, name) { } 337 public class Program { 338 339 enum Weekday { 340 Mon, Tue, Wed, Thu, Fri, Sat, Sun }; 341 342 static bool isWeekday( Weekday wd ) { 343 return wd <= Weekday.Fri; 344 } 345 static bool isWeekend( Weekday wd ) { 346 return Weekday.Sat <= wd; 347 } 348 349 350 public static void Main() { 351 Weekday day = Weekday.Sat; 352 353 Console.WriteLine( isWeekday( day ) ); 354 Console.WriteLine( isWeekend( day ) ); 355 } 364 356 } 365 357 \end{csharp} 366 Find a meaningful example and test it. 358 & 359 \begin{csharp} 360 public class Program { 361 public @class@ WeekDay : Enumeration { 362 public enum Day { 363 Mon, Tue, Wed, Thu, Fri, Sat, Sun }; 364 public enum Day2 : Day { 365 XXX, YYY }; 366 Day day; 367 public bool isWeekday() { 368 return day <= Day.Fri; 369 } 370 public bool isWeekend() { 371 return day > Day.Fri; 372 } 373 public WeekDay( Day d ) { day = d; } 374 } 375 public static void Main() { 376 WeekDay cday = new 377 WeekDay( WeekDay.Day.Sat ); 378 Console.WriteLine( cday.isWeekday() ); 379 Console.WriteLine( cday.isWeekend() ); 380 } 381 } 382 \end{csharp} 383 \end{tabular} 384 \lstMakeShortInline@ 385 \caption{\Csharp: Free Routine Versus Class Enumeration} 386 \label{CsharpFreeVersusClass} 387 \end{figure} 367 388 368 389 369 390 \section{Golang} 370 371 \lstnewenvironment{Go}[1][]{% necessary 372 \lstset{ 373 language=Go, 374 escapechar=\$, % LaTeX escape in code 375 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 376 }% lstset 377 \lstset{#1}% necessary 378 }{} 379 380 The Golang enumeration is similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression. 391 \lstnewenvironment{Go}[1][]{\lstset{language=Go,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 392 393 Golang provides pseudo-enumeration similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression. 381 394 \begin{Go} 382 395 const ( R = 0; G; B ) $\C{// implicit: 0 0 0}$ … … 401 414 Auto-incrementing stops after an explicit initialization. 402 415 \begin{Go} 403 const ( Mon day = iota; Tuesday; Wednesday; // 0, 1, 2404 @Thu rsday = 10@; Friday; Saturday; Sunday) // 10, 10, 10, 10416 const ( Mon = iota; Tue; Wed; // 0, 1, 2 417 @Thu = 10@; Fri; Sat; Sun ) // 10, 10, 10, 10 405 418 \end{Go} 406 419 Auto-incrementing can be restarted with an expression containing \emph{one} \lstinline[language=Go]{iota}. 407 420 \begin{Go} 408 421 const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@; V5 ) // 0 1 7 3 4 409 const ( Mon day = iota; Tuesday; Wednesday; // 0, 1, 2410 @Thu rsday = 10;@ Friday = @iota - Wednesday + Thursday - 1@; Saturday; Sunday) // 10, 11, 12, 13422 const ( Mon = iota; Tue; Wed; // 0, 1, 2 423 @Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13 411 424 \end{Go} 412 425 Note, \lstinline[language=Go]{iota} is advanced for an explicitly initialized enumerator, like the underscore @_@ identifier. 413 426 427 Basic switch and looping are possible. 428 \begin{cquote} 429 \lstDeleteShortInline@ 430 \setlength{\tabcolsep}{15pt} 431 \begin{tabular}{@{}ll@{}} 432 \begin{Go} 433 day := Mon; 434 switch day { 435 case Mon, Tue, Wed, Thu, Fri: 436 fmt.Println( "weekday" ); 437 case Sat, Sun: 438 fmt.Println( "weekend" ); 439 } 440 \end{Go} 441 & 442 \begin{Go} 443 444 for i := Mon; i <= Sun; i += 1 { 445 fmt.Println( i ) 446 } 447 448 449 450 \end{Go} 451 \end{tabular} 452 \lstMakeShortInline@ 453 \end{cquote} 454 However, the loop prints the values from 0 to 13 because there is no actual enumeration. 455 414 456 415 457 \section{Java} 416 417 \lstnewenvironment{Java}[1][]{% necessary 418 \lstset{ 419 language=Java, 420 escapechar=\$, % LaTeX escape in code 421 moredelim=**[is][\color{red}]{`}{`}, % red highlighting @...@ 422 }% lstset 423 \lstset{#1}% necessary 424 }{} 425 426 Here's a quick and simple example of an enum that defines the status of a pizza order; the order status can be ORDERED, READY or DELIVERED: 458 \lstnewenvironment{Java}[1][]{\lstset{language=Java,morekeywords={enum,assert,strictfp}, 459 escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{} 460 461 Every enumeration in Java is an enumeration class. 462 For a basic enumeration 427 463 \begin{Java} 428 public enum PizzaStatus { 429 ORDERED, 430 READY, 431 DELIVERED; 432 } 464 enum Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; 465 Weekday day = Weekday.Sat; 433 466 \end{Java} 434 Additionally, enums come with many useful methods that we would otherwise need to write if we were using traditional public static final constants. 435 436 \paragraph{Custom Enum Methods} 437 438 Now that we have a basic understanding of what enums are and how we can use them, we'll take our previous example to the next level by defining some extra API methods on the enum: 467 the scoped enumerators are an ordered list of @final@ methods of type integer, ordered left to right starting at 0, increasing by 1. 468 The value of an enumeration instance is restricted to the enumeration's enumerators. 469 There is an implicit @int@ variable in the enumeration used to store the value of an enumeration instance. 470 The position (ordinal) and label are accessible, where the value is the same as the position. 439 471 \begin{Java} 440 public class Pizza { 441 private PizzaStatus status; 442 public enum PizzaStatus { 443 ORDERED, 444 READY, 445 DELIVERED; 446 } 447 public boolean isDeliverable() { 448 if (getStatus() == PizzaStatus.READY) { 449 return true; 450 } 451 return false; 452 } 453 // Methods that set and get the status variable. 454 } 472 System.out.println( day.!ordinal()! + " " + day.!name()! ); // 5 Sat 455 473 \end{Java} 456 457 \paragraph{Comparing Enum Types Using "==" Operator} 458 459 Since enum types ensure that only one instance of the constants exist in the JVM, we can safely use the "==" operator to compare two variables, like we did in the above example. 460 Furthermore, the "==" operator provides compile-time and run-time safety. 461 462 First, we'll look at run-time safety in the following snippet, where we'll use the "==" operator to compare statuses. 463 Either value can be null and we won't get a NullPointerException. Conversely, if we use the equals method, we will get a NullPointerException: 474 There is an inverse function @valueOf@ from string to enumerator. 464 475 \begin{Java} 465 if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED)); 466 if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED); 476 day = Weekday.valueOf( "Wed" ); 467 477 \end{Java} 468 As for compile-time safety, let's look at an example where we'll determine that an enum of a different type is equal by comparing it using the equals method. 469 This is because the values of the enum and the getStatus method coincidentally are the same; 470 however, logically the comparison should be false. We avoid this issue by using the "==" operator. 471 472 The compiler will flag the comparison as an incompatibility error: 478 There are no implicit conversions to/from an enumerator and its underlying type. 479 Like \Csharp, \VRef[Figure]{f:JavaFreeVersusClass} shows the same example for an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class. 480 481 \begin{figure} 482 \centering 483 \lstDeleteShortInline@ 484 \begin{tabular}{@{}l|l@{}} 485 \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ 486 \hline 473 487 \begin{Java} 474 if(testPz.getStatus().equals(TestColor.GREEN)); 475 if(testPz.getStatus() == TestColor.GREEN); 488 enum Weekday !{! 489 Mon, Tue, Wed, Thu, Fri, Sat, Sun !}!; 490 491 static boolean isWeekday( Weekday wd ) { 492 return wd.ordinal() <= Weekday.Fri.ordinal(); 493 } 494 static boolean isWeekend( Weekday wd ) { 495 return Weekday.Fri.ordinal() < wd.ordinal(); 496 } 497 498 public static void main( String[] args ) { 499 Weekday day = Weekday.Sat; 500 System.out.println( isWeekday( day ) ); 501 System.out.println( isWeekend( day ) ); 502 } 476 503 \end{Java} 477 478 \paragraph{Using Enum Types in Switch Statements} 479 480 We can use enum types in switch statements also: 504 & 481 505 \begin{Java} 482 public int getDeliveryTimeInDays() { 483 switch (status) { 484 case ORDERED: return 5; 485 case READY: return 2; 486 case DELIVERED: return 0; 487 } 488 return 0; 506 enum Weekday !{! 507 Mon, Tue, Wed, Thu, Fri, Sat, Sun; 508 509 public boolean isWeekday() { 510 return ordinal() <= Weekday.Fri.ordinal(); 511 } 512 public boolean isWeekend() { 513 return Weekday.Fri.ordinal() < ordinal(); 514 } 515 !}! 516 public static void main( String[] args ) { 517 WeekDay day = WeekDay.Sat; 518 System.out.println( day.isWeekday() ); 519 System.out.println( day.isWeekend() ); 489 520 } 490 521 \end{Java} 491 492 \ paragraph{Fields, Methods and Constructors in Enums}493 494 We can define constructors, methods, and fields inside enum types, which makes them very powerful. 495 496 Next, let's extend the example above by implementing the transition from one stage of a pizza order to another. 497 We'll see how we can get rid of the if and switch statements used before: 522 \end{tabular} 523 \lstMakeShortInline@ 524 \caption{Java: Free Routine Versus Class Enumeration} 525 \label{f:JavaFreeVersusClass} 526 \end{figure} 527 528 To explicitly assign enumerator values and/or use a non-@int@ enumeration type (any Java type may be used), the enumeration must specify an explicit type in the enumeration class and a constructor. 498 529 \begin{Java} 499 public class Pizza { 500 private PizzaStatus status; 501 public enum PizzaStatus { 502 ORDERED (5){ 503 @Override 504 public boolean isOrdered() { 505 return true; 506 } 507 }, 508 READY (2){ 509 @Override 510 public boolean isReady() { 511 return true; 512 } 513 }, 514 DELIVERED (0){ 515 @Override 516 public boolean isDelivered() { 517 return true; 518 } 519 }; 520 521 private int timeToDelivery; 522 public boolean isOrdered() {return false;} 523 public boolean isReady() {return false;} 524 public boolean isDelivered(){return false;} 525 public int getTimeToDelivery() { 526 return timeToDelivery; 527 } 528 PizzaStatus (int timeToDelivery) { 529 this.timeToDelivery = timeToDelivery; 530 } 531 } 532 public boolean isDeliverable() { 533 return this.status.isReady(); 534 } 535 public void printTimeToDeliver() { 536 System.out.println("Time to delivery is " + 537 this.getStatus().getTimeToDelivery()); 538 } 539 // Methods that set and get the status variable. 540 } 530 enum Weekday { 531 Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first 532 private !long! day; $\C{// underlying enumeration type}$ 533 private Weekday( !long! d ) { day = d; } $\C{// used to initialize enumerators}$ 534 }; 535 Weekday day = Weekday.Sat; 541 536 \end{Java} 542 The test snippet below demonstrates how this works: 537 If an enumerator initialization is a runtime expression, the expression is executed once at the point the enumeration is declaraed. 538 539 The position, value, and label are accessible. 543 540 \begin{Java} 544 @Test 545 public void givenPizaOrder_whenReady_thenDeliverable() { 546 Pizza testPz = new Pizza(); 547 testPz.setStatus(Pizza.PizzaStatus.READY); 548 assertTrue(testPz.isDeliverable()); 549 } 541 System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! ); // 5 6 Sat 550 542 \end{Java} 551 552 \paragraph{EnumSet and EnumMap} 553 554 \paragraph{EnumSet} 555 556 The EnumSet is a specialized Set implementation that's meant to be used with Enum types. 557 558 Compared to a HashSet, it's a very efficient and compact representation of a particular Set of Enum constants, owing to the internal Bit Vector Representation that's used. 559 It also provides a type-safe alternative to traditional int-based "bit flags," allowing us to write concise code that's more readable and maintainable. 560 561 The EnumSet is an abstract class that has two implementations, RegularEnumSet and JumboEnumSet, one of which is chosen depending on the number of constants in the enum at the time of instantiation. 562 563 Therefore, it's a good idea to use this set whenever we want to work with a collection of enum constants in most scenarios (like subsetting, adding, removing, and bulk operations like containsAll and removeAll), and use Enum.values() if we just want to iterate over all possible constants. 564 565 In the code snippet below, we can see how to use EnumSet to create a subset of constants: 543 The constructor is private so only initialization or assignment can be used to set an enumeration, which ensures only corresponding enumerator values are allowed. 544 545 An enumeration can appear in a @switch@ statement, but no ranges. 566 546 \begin{Java} 567 public class Pizza { 568 private static EnumSet<PizzaStatus> undeliveredPizzaStatuses = 569 EnumSet.of(PizzaStatus.ORDERED, PizzaStatus.READY); 570 private PizzaStatus status; 571 public enum PizzaStatus { 572 ... 573 } 574 public boolean isDeliverable() { 575 return this.status.isReady(); 576 } 577 public void printTimeToDeliver() { 578 System.out.println("Time to delivery is " + 579 this.getStatus().getTimeToDelivery() + " days"); 580 } 581 public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) { 582 return input.stream().filter( 583 (s) -> undeliveredPizzaStatuses.contains(s.getStatus())) 584 .collect(Collectors.toList()); 585 } 586 public void deliver() { 587 if (isDeliverable()) { 588 PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy() 589 .deliver(this); 590 this.setStatus(PizzaStatus.DELIVERED); 591 } 592 } 593 // Methods that set and get the status variable. 547 switch ( day ) { 548 case Mon: case Tue: case Wed: case Thu: case Fri: 549 System.out.println( "weekday" ); 550 break; 551 case Sat: case Sun: 552 System.out.println( "weekend" ); 553 break; 594 554 } 595 555 \end{Java} 596 597 Executing the following test demonstrates the power of the EnumSet implementation of the Set interface: 556 Like \Csharp, looping over an enumeration is done using method @values@, which returns the array of enumerator values (expensive operation). 598 557 \begin{Java} 599 @Test 600 public void givenPizaOrders_whenRetrievingUnDeliveredPzs_thenCorrectlyRetrieved() { 601 List<Pizza> pzList = new ArrayList<>(); 602 Pizza pz1 = new Pizza(); 603 pz1.setStatus(Pizza.PizzaStatus.DELIVERED); 604 605 Pizza pz2 = new Pizza(); 606 pz2.setStatus(Pizza.PizzaStatus.ORDERED); 607 608 Pizza pz3 = new Pizza(); 609 pz3.setStatus(Pizza.PizzaStatus.ORDERED); 610 611 Pizza pz4 = new Pizza(); 612 pz4.setStatus(Pizza.PizzaStatus.READY); 613 614 pzList.add(pz1); 615 pzList.add(pz2); 616 pzList.add(pz3); 617 pzList.add(pz4); 618 619 List<Pizza> undeliveredPzs = Pizza.getAllUndeliveredPizzas(pzList); 620 assertTrue(undeliveredPzs.size() == 3); 621 } 558 for ( Weekday iday : Weekday.values() ) { 559 System.out.print( iday.ordinal() + iday.day + " " + iday.name() + ", " ); 560 } 561 0 1 Mon, 1 2 Tue, 2 3 Wed, 3 4 Thu, 4 5 Fri, 5 6 Sat, 6 7 Sun, 622 562 \end{Java} 623 563 624 \paragraph{EnumMap} 625 626 EnumMap is a specialized Map implementation meant to be used with enum constants as keys. 627 Compared to its counterpart HashMap, it's an efficient and compact implementation that's internally represented as an array: 628 \begin{Java} 629 EnumMap<Pizza.PizzaStatus, Pizza> map; 630 \end{Java} 631 Let's look at an example of how we can use it in practice: 632 \begin{Java} 633 public static EnumMap<PizzaStatus, List<Pizza>> 634 groupPizzaByStatus(List<Pizza> pizzaList) { 635 EnumMap<PizzaStatus, List<Pizza>> pzByStatus = 636 new EnumMap<PizzaStatus, List<Pizza>>(PizzaStatus.class); 637 638 for (Pizza pz : pizzaList) { 639 PizzaStatus status = pz.getStatus(); 640 if (pzByStatus.containsKey(status)) { 641 pzByStatus.get(status).add(pz); 642 } else { 643 List<Pizza> newPzList = new ArrayList<Pizza>(); 644 newPzList.add(pz); 645 pzByStatus.put(status, newPzList); 646 } 647 } 648 return pzByStatus; 649 } 650 \end{Java} 651 Executing the following test demonstrates the power of the EnumMap implementation of the Map interface: 652 \begin{Java} 653 @Test 654 public void givenPizaOrders_whenGroupByStatusCalled_thenCorrectlyGrouped() { 655 List<Pizza> pzList = new ArrayList<>(); 656 Pizza pz1 = new Pizza(); 657 pz1.setStatus(Pizza.PizzaStatus.DELIVERED); 658 659 Pizza pz2 = new Pizza(); 660 pz2.setStatus(Pizza.PizzaStatus.ORDERED); 661 662 Pizza pz3 = new Pizza(); 663 pz3.setStatus(Pizza.PizzaStatus.ORDERED); 664 665 Pizza pz4 = new Pizza(); 666 pz4.setStatus(Pizza.PizzaStatus.READY); 667 668 pzList.add(pz1); 669 pzList.add(pz2); 670 pzList.add(pz3); 671 pzList.add(pz4); 672 673 EnumMap<Pizza.PizzaStatus,List<Pizza>> map = Pizza.groupPizzaByStatus(pzList); 674 assertTrue(map.get(Pizza.PizzaStatus.DELIVERED).size() == 1); 675 assertTrue(map.get(Pizza.PizzaStatus.ORDERED).size() == 2); 676 assertTrue(map.get(Pizza.PizzaStatus.READY).size() == 1); 677 } 678 \end{Java} 679 680 \paragraph{Singleton Pattern} 681 682 Normally, implementing a class using the Singleton pattern is quite non-trivial. 683 Enums provide a quick and easy way of implementing singletons. 684 685 In addition, since the enum class implements the Serializable interface under the hood, the class is guaranteed to be a singleton by the JVM. 686 This is unlike the conventional implementation, where we have to ensure that no new instances are created during deserialization. 687 688 In the code snippet below, we see how we can implement a singleton pattern: 689 \begin{Java} 690 public enum PizzaDeliverySystemConfiguration { 691 INSTANCE; 692 PizzaDeliverySystemConfiguration() { 693 // Initialization configuration which involves 694 // overriding defaults like delivery strategy 695 } 696 697 private PizzaDeliveryStrategy deliveryStrategy = PizzaDeliveryStrategy.NORMAL; 698 699 public static PizzaDeliverySystemConfiguration getInstance() { 700 return INSTANCE; 701 } 702 703 public PizzaDeliveryStrategy getDeliveryStrategy() { 704 return deliveryStrategy; 705 } 706 } 707 \end{Java} 708 709 \paragraph{Strategy Pattern} 710 711 Conventionally, the Strategy pattern is written by having an interface that is implemented by different classes. 712 713 Adding a new strategy means adding a new implementation class. 714 With enums, we can achieve this with less effort, and adding a new implementation means simply defining another instance with some implementation. 715 716 The code snippet below shows how to implement the Strategy pattern: 717 \begin{Java} 718 public enum PizzaDeliveryStrategy { 719 EXPRESS { 720 @Override 721 public void deliver(Pizza pz) { 722 System.out.println("Pizza will be delivered in express mode"); 723 } 724 }, 725 NORMAL { 726 @Override 727 public void deliver(Pizza pz) { 728 System.out.println("Pizza will be delivered in normal mode"); 729 } 730 }; 731 732 public abstract void deliver(Pizza pz); 733 } 734 \end{Java} 735 Then we add the following method to the Pizza class: 736 \begin{Java} 737 public void deliver() { 738 if (isDeliverable()) { 739 PizzaDeliverySystemConfiguration.getInstance().getDeliveryStrategy() 740 .deliver(this); 741 this.setStatus(PizzaStatus.DELIVERED); 742 } 743 } 744 745 @Test 746 public void givenPizaOrder_whenDelivered_thenPizzaGetsDeliveredAndStatusChanges() { 747 Pizza pz = new Pizza(); 748 pz.setStatus(Pizza.PizzaStatus.READY); 749 pz.deliver(); 750 assertTrue(pz.getStatus() == Pizza.PizzaStatus.DELIVERED); 751 } 752 \end{Java} 753 754 8. Java 8 and Enums 755 756 We can rewrite the Pizza class in Java 8, and see how the methods getAllUndeliveredPizzas() and groupPizzaByStatus() become so concise with the use of lambdas and the Stream APIs: 757 \begin{Java} 758 public static List<Pizza> getAllUndeliveredPizzas(List<Pizza> input) { 759 return input.stream().filter( 760 (s) -> !deliveredPizzaStatuses.contains(s.getStatus())) 761 .collect(Collectors.toList()); 762 } 763 764 public static EnumMap<PizzaStatus, List<Pizza>> 765 groupPizzaByStatus(List<Pizza> pzList) { 766 EnumMap<PizzaStatus, List<Pizza>> map = pzList.stream().collect( 767 Collectors.groupingBy(Pizza::getStatus, 768 () -> new EnumMap<>(PizzaStatus.class), Collectors.toList())); 769 return map; 770 } 771 \end{Java} 564 As well, Java provides an @EnumSet@ where the underlying type is an efficient set of bits, one per enumeration \see{\Csharp \lstinline{Flags}, \VRef{s:Csharp}}, providing (logical) operations on groups of enumerators. 565 There is also a specialized version of @HashMap@ with enumerator keys, which has performance benefits. 566 567 Enumeration inheritence is disallowed because an enumeration is @final@. 568 772 569 773 570 774 571 \section{Modula-3} 775 572 573 574 776 575 \section{Rust} 576 \lstnewenvironment{rust}[1][]{\lstset{language=Rust,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 577 578 Enumerations 579 \begin{rust} 580 Syntax 581 Enumeration : 582 enum IDENTIFIER GenericParams? WhereClause? { EnumItems? } 583 584 EnumItems : 585 EnumItem ( , EnumItem )* ,? 586 587 EnumItem : 588 OuterAttribute* Visibility? 589 IDENTIFIER ( EnumItemTuple | EnumItemStruct )? EnumItemDiscriminant? 590 591 EnumItemTuple : 592 ( TupleFields? ) 593 594 EnumItemStruct : 595 { StructFields? } 596 597 EnumItemDiscriminant : 598 = Expression 599 \end{rust} 600 An enumeration, also referred to as an enum, is a simultaneous definition of a nominal enumerated type as well as a set of constructors, that can be used to create or pattern-match values of the corresponding enumerated type. 601 602 Enumerations are declared with the keyword enum. 603 604 An example of an enum item and its use: 605 \begin{rust} 606 enum Animal { 607 Dog, 608 Cat, 609 } 610 611 let mut a: Animal = Animal::Dog; 612 a = Animal::Cat; 613 \end{rust} 614 Enum constructors can have either named or unnamed fields: 615 \begin{rust} 616 enum Animal { 617 Dog(String, f64), 618 Cat { name: String, weight: f64 }, 619 } 620 621 let mut a: Animal = Animal::Dog("Cocoa".to_string(), 37.2); 622 a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; 623 \end{rust} 624 In this example, Cat is a struct-like enum variant, whereas Dog is simply called an enum variant. 625 626 An enum where no constructors contain fields are called a field-less enum. For example, this is a fieldless enum: 627 \begin{rust} 628 enum Fieldless { 629 Tuple(), 630 Struct{}, 631 Unit, 632 } 633 \end{rust} 634 If a field-less enum only contains unit variants, the enum is called an unit-only enum. For example: 635 \begin{rust} 636 enum Enum { 637 Foo = 3, 638 Bar = 2, 639 Baz = 1, 640 } 641 \end{rust} 642 643 \subsection{Discriminants} 644 645 Each enum instance has a discriminant: an integer logically associated to it that is used to determine which variant it holds. 646 647 Under the default representation, the discriminant is interpreted as an isize value. However, the compiler is allowed to use a smaller type (or another means of distinguishing variants) in its actual memory layout. 648 649 \subsection{Assigning discriminant values} 650 651 \subsection{Explicit discriminants} 652 653 In two circumstances, the discriminant of a variant may be explicitly set by following the variant name with = and a constant expression: 654 655 if the enumeration is "unit-only". 656 657 if a primitive representation is used. For example: 658 \begin{rust} 659 #[repr(u8)] 660 enum Enum { 661 Unit = 3, 662 Tuple(u16), 663 Struct { 664 a: u8, 665 b: u16, 666 } = 1, 667 } 668 \end{rust} 669 670 \subsection{Implicit discriminants} 671 672 If a discriminant for a variant is not specified, then it is set to one higher than the discriminant of the previous variant in the declaration. If the discriminant of the first variant in the declaration is unspecified, then it is set to zero. 673 \begin{rust} 674 enum Foo { 675 Bar, // 0 676 Baz = 123, // 123 677 Quux, // 124 678 } 679 680 let baz_discriminant = Foo::Baz as u32; 681 assert_eq!(baz_discriminant, 123); 682 \end{rust} 683 684 \subsection{Restrictions} 685 686 It is an error when two variants share the same discriminant. 687 \begin{rust} 688 enum SharedDiscriminantError { 689 SharedA = 1, 690 SharedB = 1 691 } 692 693 enum SharedDiscriminantError2 { 694 Zero, // 0 695 One, // 1 696 OneToo = 1 // 1 (collision with previous!) 697 } 698 \end{rust} 699 It is also an error to have an unspecified discriminant where the previous discriminant is the maximum value for the size of the discriminant. 700 \begin{rust} 701 #[repr(u8)] 702 enum OverflowingDiscriminantError { 703 Max = 255, 704 MaxPlusOne // Would be 256, but that overflows the enum. 705 } 706 707 #[repr(u8)] 708 enum OverflowingDiscriminantError2 { 709 MaxMinusOne = 254, // 254 710 Max, // 255 711 MaxPlusOne // Would be 256, but that overflows the enum. 712 } 713 \end{rust} 714 715 \subsection{Accessing discriminant} 716 717 \begin{rust} 718 Via mem::discriminant 719 \end{rust} 720 @mem::discriminant@ returns an opaque reference to the discriminant of an enum value which can be compared. This cannot be used to get the value of the discriminant. 721 722 \subsection{Casting} 723 724 If an enumeration is unit-only (with no tuple and struct variants), then its discriminant can be directly accessed with a numeric cast; e.g.: 725 \begin{rust} 726 enum Enum { 727 Foo, 728 Bar, 729 Baz, 730 } 731 732 assert_eq!(0, Enum::Foo as isize); 733 assert_eq!(1, Enum::Bar as isize); 734 assert_eq!(2, Enum::Baz as isize); 735 \end{rust} 736 Field-less enums can be casted if they do not have explicit discriminants, or where only unit variants are explicit. 737 \begin{rust} 738 enum Fieldless { 739 Tuple(), 740 Struct{}, 741 Unit, 742 } 743 744 assert_eq!(0, Fieldless::Tuple() as isize); 745 assert_eq!(1, Fieldless::Struct{} as isize); 746 assert_eq!(2, Fieldless::Unit as isize); 747 \end{rust} 748 \begin{rust} 749 #[repr(u8)] 750 enum FieldlessWithDiscrimants { 751 First = 10, 752 Tuple(), 753 Second = 20, 754 Struct{}, 755 Unit, 756 } 757 758 assert_eq!(10, FieldlessWithDiscrimants::First as u8); 759 assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8); 760 assert_eq!(20, FieldlessWithDiscrimants::Second as u8); 761 assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8); 762 assert_eq!(22, FieldlessWithDiscrimants::Unit as u8); 763 \end{rust} 764 765 \subsection{Pointer casting} 766 767 If the enumeration specifies a primitive representation, then the discriminant may be reliably accessed via unsafe pointer casting: 768 \begin{rust} 769 #[repr(u8)] 770 enum Enum { 771 Unit, 772 Tuple(bool), 773 Struct{a: bool}, 774 } 775 776 impl Enum { 777 fn discriminant(&self) -> u8 { 778 unsafe { *(self as *const Self as *const u8) } 779 } 780 } 781 782 let unit_like = Enum::Unit; 783 let tuple_like = Enum::Tuple(true); 784 let struct_like = Enum::Struct{a: false}; 785 786 assert_eq!(0, unit_like.discriminant()); 787 assert_eq!(1, tuple_like.discriminant()); 788 assert_eq!(2, struct_like.discriminant()); 789 \end{rust} 790 791 \subsection{Zero-variant enums} 792 793 Enums with zero variants are known as zero-variant enums. As they have no valid values, they cannot be instantiated. 794 \begin{rust} 795 enum ZeroVariants {} 796 \end{rust} 797 Zero-variant enums are equivalent to the never type, but they cannot be coerced into other types. 798 \begin{rust} 799 let x: ZeroVariants = panic!(); 800 let y: u32 = x; // mismatched type error 801 \end{rust} 802 803 \subsection{Variant visibility} 804 805 Enum variants syntactically allow a Visibility annotation, but this is rejected when the enum is validated. This allows items to be parsed with a unified syntax across different contexts where they are used. 806 \begin{rust} 807 macro_rules! mac_variant { 808 ($vis:vis $name:ident) => { 809 enum $name { 810 $vis Unit, 811 812 $vis Tuple(u8, u16), 813 814 $vis Struct { f: u8 }, 815 } 816 } 817 } 818 819 // Empty `vis` is allowed. 820 mac_variant! { E } 821 822 // This is allowed, since it is removed before being validated. 823 #[cfg(FALSE)] 824 enum E { 825 pub U, 826 pub(crate) T(u8), 827 pub(super) T { f: String } 828 } 829 \end{rust} 777 830 778 831 779 832 \section{Swift} 780 781 \lstnewenvironment{swift}[1][]{% necessary 782 \lstset{ 783 language=Swift, 784 escapechar=\$, % LaTeX escape in code 785 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 786 }% lstset 787 \lstset{#1}% necessary 788 }{} 789 790 Model custom types that define a list of possible values. 833 \lstnewenvironment{swift}[1][]{\lstset{language=Swift,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 834 835 % https://www.programiz.com/swift/online-compiler 836 837 A Swift enumeration provides a heterogenous set of enumerators, like a tagged @union@, where the field name is the enumerator and its list of type parameters form its type. 838 \begin{swift} 839 enum Many { 840 case Mon, Tue, Wed, Thu, Fri, Sat, Sun // basic enumerator 841 case code( String ) // string enumerator 842 case tuple( Int, Int, Int ) // tuple enumerator 843 }; 844 var day = Many.Sat; // qualification to resolve type 845 print( day ); 846 day = .Wed // no qualification after type resolved 847 print( day ); 848 day = .code( "ABC" ); 849 print( day ); 850 day = .tuple( 1, 2, 3 ); 851 print( day ); 852 853 Sat 854 Wed 855 code("ABC") 856 tuple(1, 2, 3) 857 \end{swift} 858 791 859 792 860 An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. … … 808 876 \paragraph{Enumeration Syntax} 809 877 810 You introduce enumerations with the @enum@ keyword and place their entire definition within a pair of braces:811 \begin{swift}812 enum SomeEnumeration {813 // enumeration definition goes here814 }815 \end{swift}816 Here's an example for the four main points of a compass:817 \begin{swift}818 enum CompassPoint {819 case north820 case south821 case east822 case west823 }824 \end{swift}825 The values defined in an enumeration (such as @north@, @south@, @east@, and @west@) are its enumeration cases.826 You use the @case@ keyword to introduce new enumeration cases.827 878 828 879 Note: … … 1139 1190 1140 1191 \section{Python} 1192 \lstnewenvironment{python}[1][]{\lstset{language=Python,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 1193 1194 An @Enum@ is a set of symbolic names bound to unique values. 1195 They are similar to global variables, but they offer a more useful @repr()@, grouping, type-safety, and a few other features. 1196 1197 They are most useful when you have a variable that can take one of a limited selection of values. For example, the days of the week: 1198 \begin{python} 1199 >>> from enum import Enum 1200 >>> class Weekday(Enum): 1201 ... MONDAY = 1 1202 ... TUESDAY = 2 1203 ... WEDNESDAY = 3 1204 ... THURSDAY = 4 1205 ... FRIDAY = 5 1206 ... SATURDAY = 6 1207 ... SUNDAY = 7 1208 \end{python} 1209 Or perhaps the RGB primary colors: 1210 \begin{python} 1211 >>> from enum import Enum 1212 >>> class Color(Enum): 1213 ... RED = 1 1214 ... GREEN = 2 1215 ... BLUE = 3 1216 \end{python} 1217 As you can see, creating an @Enum@ is as simple as writing a class that inherits from @Enum@ itself. 1218 1219 Note: Case of Enum Members 1220 1221 Because Enums are used to represent constants, and to help avoid issues with name clashes between mixin-class methods/attributes and enum names, we strongly recommend using @UPPER_CASE@ names for members, and will be using that style in our examples. 1222 1223 Depending on the nature of the enum a member's value may or may not be important, but either way that value can be used to get the corresponding member: 1224 \begin{python} 1225 >>> Weekday(3) 1226 <Weekday.WEDNESDAY: 3> 1227 \end{python} 1228 As you can see, the @repr()@ of a member shows the enum name, the member name, and the value. 1229 The @str()@ of a member shows only the enum name and member name: 1230 \begin{python} 1231 print(Weekday.THURSDAY) 1232 Weekday.THURSDAY 1233 \end{python} 1234 The type of an enumeration member is the enum it belongs to: 1235 \begin{python} 1236 >>> type(Weekday.MONDAY) 1237 <enum 'Weekday'> 1238 isinstance(Weekday.FRIDAY, Weekday) 1239 True 1240 \end{python} 1241 Enum members have an attribute that contains just their name: 1242 \begin{python} 1243 >>> print(Weekday.TUESDAY.name) 1244 TUESDAY 1245 \end{python} 1246 Likewise, they have an attribute for their value: 1247 \begin{python} 1248 >>> Weekday.WEDNESDAY.value 1249 3 1250 \end{python} 1251 Unlike many languages that treat enumerations solely as name/value pairs, Python @Enum@s can have behavior added. 1252 For example, @datetime.date@ has two methods for returning the weekday: @weekday()@ and @isoweekday()@. 1253 The difference is that one of them counts from 0-6 and the other from 1-7. 1254 Rather than keep track of that ourselves we can add a method to the @Weekday@ enum to extract the day from the date instance and return the matching enum member: 1255 \begin{python} 1256 $@$classmethod 1257 def from_date(cls, date): 1258 return cls(date.isoweekday()) 1259 \end{python} 1260 The complete Weekday enum now looks like this: 1261 \begin{python} 1262 >>> class Weekday(Enum): 1263 ... MONDAY = 1 1264 ... TUESDAY = 2 1265 ... WEDNESDAY = 3 1266 ... THURSDAY = 4 1267 ... FRIDAY = 5 1268 ... SATURDAY = 6 1269 ... SUNDAY = 7 1270 ... # 1271 ... $@$classmethod 1272 ... def from_date(cls, date): 1273 ... return cls(date.isoweekday()) 1274 \end{python} 1275 Now we can find out what today is! Observe: 1276 \begin{python} 1277 >>> from datetime import date 1278 >>> Weekday.from_date(date.today()) 1279 <Weekday.TUESDAY: 2> 1280 \end{python} 1281 Of course, if you're reading this on some other day, you'll see that day instead. 1282 1283 This Weekday enum is great if our variable only needs one day, but what if we need several? Maybe we're writing a function to plot chores during a week, and don't want to use a @list@ -- we could use a different type of @Enum@: 1284 \begin{python} 1285 >>> from enum import Flag 1286 >>> class Weekday(Flag): 1287 ... MONDAY = 1 1288 ... TUESDAY = 2 1289 ... WEDNESDAY = 4 1290 ... THURSDAY = 8 1291 ... FRIDAY = 16 1292 ... SATURDAY = 32 1293 ... SUNDAY = 64 1294 \end{python} 1295 We've changed two things: we're inherited from @Flag@, and the values are all powers of 2. 1296 1297 Just like the original @Weekday@ enum above, we can have a single selection: 1298 \begin{python} 1299 >>> first_week_day = Weekday.MONDAY 1300 >>> first_week_day 1301 <Weekday.MONDAY: 1> 1302 \end{python} 1303 But @Flag@ also allows us to combine several members into a single variable: 1304 \begin{python} 1305 >>> weekend = Weekday.SATURDAY | Weekday.SUNDAY 1306 >>> weekend 1307 <Weekday.SATURDAY|SUNDAY: 96> 1308 \end{python} 1309 You can even iterate over a @Flag@ variable: 1310 \begin{python} 1311 >>> for day in weekend: 1312 ... print(day) 1313 Weekday.SATURDAY 1314 Weekday.SUNDAY 1315 \end{python} 1316 Okay, let's get some chores set up: 1317 \begin{python} 1318 >>> chores_for_ethan = { 1319 ... 'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY, 1320 ... 'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY, 1321 ... 'answer SO questions': Weekday.SATURDAY, 1322 ... } 1323 \end{python} 1324 And a function to display the chores for a given day: 1325 \begin{python} 1326 >>> def show_chores(chores, day): 1327 ... for chore, days in chores.items(): 1328 ... if day in days: 1329 ... print(chore) 1330 >>> show_chores(chores_for_ethan, Weekday.SATURDAY) 1331 answer SO questions 1332 \end{python} 1333 In cases where the actual values of the members do not matter, you can save yourself some work and use @auto()@ for the values: 1334 \begin{python} 1335 >>> from enum import auto 1336 >>> class Weekday(Flag): 1337 ... MONDAY = auto() 1338 ... TUESDAY = auto() 1339 ... WEDNESDAY = auto() 1340 ... THURSDAY = auto() 1341 ... FRIDAY = auto() 1342 ... SATURDAY = auto() 1343 ... SUNDAY = auto() 1344 ... WEEKEND = SATURDAY | SUNDAY 1345 \end{python} 1346 1347 \subsection{Programmatic access to enumeration members and their attributes} 1348 1349 Sometimes it's useful to access members in enumerations programmatically (i.e. situations where @Color.RED@ won't do because the exact color is not known at program-writing time). 1350 @Enum@ allows such access: 1351 \begin{python} 1352 >>> Color(1) 1353 <Color.RED: 1> 1354 >>> Color(3) 1355 <Color.BLUE: 3> 1356 \end{python} 1357 If you want to access enum members by name, use item access: 1358 \begin{python} 1359 Color['RED'] 1360 <Color.RED: 1> 1361 1362 Color['GREEN'] 1363 <Color.GREEN: 2> 1364 \end{python} 1365 If you have an enum member and need its name or value: 1366 \begin{python} 1367 >>> member = Color.RED 1368 >>> member.name 1369 'RED' 1370 >>> member.value 1371 1 1372 \end{python} 1373 1374 \subsection{Duplicating enum members and values} 1375 1376 Having two enum members with the same name is invalid: 1377 \begin{python} 1378 >>> class Shape(Enum): 1379 ... SQUARE = 2 1380 ... SQUARE = 3 1381 ... 1382 Traceback (most recent call last): 1383 ... 1384 TypeError: 'SQUARE' already defined as 2 1385 \end{python} 1386 However, an enum member can have other names associated with it. 1387 Given two entries @A@ and @B@ with the same value (and @A@ defined first), @B@ is an alias for the member @A@. 1388 By-value lookup of the value of @A@ will return the member @A@. 1389 By-name lookup of @A@ will return the member @A@. 1390 By-name lookup of @B@ will also return the member @A@: 1391 \begin{python} 1392 >>> class Shape(Enum): 1393 ... SQUARE = 2 1394 ... DIAMOND = 1 1395 ... CIRCLE = 3 1396 ... ALIAS_FOR_SQUARE = 2 1397 ... 1398 >>> Shape.SQUARE 1399 <Shape.SQUARE: 2> 1400 >>> Shape.ALIAS_FOR_SQUARE 1401 <Shape.SQUARE: 2> 1402 >>> Shape(2) 1403 <Shape.SQUARE: 2> 1404 \end{python} 1405 1406 Note: Attempting to create a member with the same name as an already defined attribute (another member, a method, etc.) or attempting to create an attribute with the same name as a member is not allowed. 1407 1408 \subsection{Ensuring unique enumeration values} 1409 1410 By default, enumerations allow multiple names as aliases for the same value. 1411 When this behavior isn't desired, you can use the @unique()@ decorator: 1412 \begin{python} 1413 >>> from enum import Enum, unique 1414 >>> $@$unique 1415 ... class Mistake(Enum): 1416 ... ONE = 1 1417 ... TWO = 2 1418 ... THREE = 3 1419 ... FOUR = 3 1420 ... 1421 Traceback (most recent call last): 1422 ... 1423 ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE 1424 \end{python} 1425 1426 \subsection{Using automatic values} 1427 1428 If the exact value is unimportant you can use @auto@: 1429 \begin{python} 1430 >>> from enum import Enum, auto 1431 >>> class Color(Enum): 1432 ... RED = auto() 1433 ... BLUE = auto() 1434 ... GREEN = auto() 1435 ... 1436 >>> [member.value for member in Color] 1437 [1, 2, 3] 1438 \end{python} 1439 The values are chosen by \_generate\_next\_value\_(), which can be overridden: 1440 \begin{python} 1441 >>> class AutoName(Enum): 1442 ... $@$staticmethod 1443 ... def _generate_next_value_(name, start, count, last_values): 1444 ... return name 1445 ... 1446 >>> class Ordinal(AutoName): 1447 ... NORTH = auto() 1448 ... SOUTH = auto() 1449 ... EAST = auto() 1450 ... WEST = auto() 1451 ... 1452 >>> [member.value for member in Ordinal] 1453 ['NORTH', 'SOUTH', 'EAST', 'WEST'] 1454 \end{python} 1455 Note The @_generate_next_value_()@ method must be defined before any members. 1456 1457 \subsection{Iteration} 1458 1459 Iterating over the members of an enum does not provide the aliases: 1460 \begin{python} 1461 >>> list(Shape) 1462 [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>] 1463 >>> list(Weekday) 1464 [<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, 1465 <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>] 1466 \end{python} 1467 Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Weekday.WEEKEND@ aren't shown. 1468 1469 The special attribute @__members__@ is a read-only ordered mapping of names to members. 1470 It includes all names defined in the enumeration, including the aliases: 1471 \begin{python} 1472 >>> for name, member in Shape.__members__.items(): 1473 ... name, member 1474 ... 1475 ('SQUARE', <Shape.SQUARE: 2>) 1476 ('DIAMOND', <Shape.DIAMOND: 1>) 1477 ('CIRCLE', <Shape.CIRCLE: 3>) 1478 ('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>) 1479 \end{python} 1480 The @__members__@ attribute can be used for detailed programmatic access to the enumeration members. 1481 For example, finding all the aliases: 1482 \begin{python} 1483 >>> [name for name, member in Shape.__members__.items() if member.name != name] 1484 ['ALIAS_FOR_SQUARE'] 1485 \end{python} 1486 Note: Aliases for flags include values with multiple flags set, such as 3, and no flags set, i.e. 0. 1487 1488 \subsection{Comparisons} 1489 1490 Enumeration members are compared by identity: 1491 \begin{python} 1492 >>> Color.RED is Color.RED 1493 True 1494 >>> Color.RED is Color.BLUE 1495 False 1496 >>> Color.RED is not Color.BLUE 1497 True 1498 \end{python} 1499 Ordered comparisons between enumeration values are not supported. 1500 Enum members are not integers (but see @IntEnum@ below): 1501 \begin{python} 1502 >>> Color.RED < Color.BLUE 1503 Traceback (most recent call last): 1504 File "<stdin>", line 1, in <module> 1505 TypeError: '<' not supported between instances of 'Color' and 'Color' 1506 \end{python} 1507 Equality comparisons are defined though: 1508 \begin{python} 1509 >>> Color.BLUE == Color.RED 1510 False 1511 >>> Color.BLUE != Color.RED 1512 True 1513 >>> Color.BLUE == Color.BLUE 1514 True 1515 \end{python} 1516 Comparisons against non-enumeration values will always compare not equal (again, @IntEnum@ was explicitly designed to behave differently, see below): 1517 \begin{python} 1518 >>> Color.BLUE == 2 1519 False 1520 \end{python} 1521 1522 Warning: It is possible to reload modules -- if a reloaded module contains enums, they will be recreated, and the new members may not compare identical/equal to the original members. 1523 1524 \subsection{Allowed members and attributes of enumerations} 1525 1526 Most of the examples above use integers for enumeration values. 1527 Using integers is short and handy (and provided by default by the Functional API), but not strictly enforced. 1528 In the vast majority of use-cases, one doesn't care what the actual value of an enumeration is. 1529 But if the value is important, enumerations can have arbitrary values. 1530 1531 Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration: 1532 \begin{python} 1533 >>> class Mood(Enum): 1534 ... FUNKY = 1 1535 ... HAPPY = 3 1536 ... 1537 ... def describe(self): 1538 ... # self is the member here 1539 ... return self.name, self.value 1540 ... 1541 ... def __str__(self): 1542 ... return 'my custom str! {0}'.format(self.value) 1543 ... 1544 ... $@$classmethod 1545 ... 1546 ... def favorite_mood(cls): 1547 ... # cls here is the enumeration 1548 ... return cls.HAPPY 1549 ... 1550 \end{python} 1551 Then: 1552 \begin{python} 1553 >>> Mood.favorite_mood() 1554 <Mood.HAPPY: 3> 1555 >>> Mood.HAPPY.describe() 1556 ('HAPPY', 3) 1557 >>> str(Mood.FUNKY) 1558 'my custom str! 1' 1559 \end{python} 1560 The rules for what is allowed are as follows: names that start and end with a single underscore are reserved by enum and cannot be used; 1561 all other attributes defined within an enumeration will become members of this enumeration, with the exception of special methods (@__str__()@, @__add__()@, etc.), descriptors (methods are also descriptors), and variable names listed in @_ignore_@. 1562 1563 Note: if your enumeration defines @__new__()@ and/or @__init__()@, any value(s) given to the enum member will be passed into those methods. 1564 See Planet for an example. 1565 1566 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 1567 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 1568 See When to use @__new__()@ vs. @__init__()@ for more details. 1569 1570 \subsection{Restricted Enum subclassing} 1571 1572 A new @Enum@ class must have one base enum class, up to one concrete data type, and as many object-based mixin classes as needed. 1573 The order of these base classes is: 1574 \begin{python} 1575 class EnumName([mix-in, ...,] [data-type,] base-enum): 1576 pass 1577 \end{python} 1578 Also, subclassing an enumeration is allowed only if the enumeration does not define any members. 1579 So this is forbidden: 1580 \begin{python} 1581 >>> class MoreColor(Color): 1582 ... PINK = 17 1583 ... 1584 Traceback (most recent call last): 1585 ... 1586 TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'> 1587 \end{python} 1588 But this is allowed: 1589 \begin{python} 1590 >>> class Foo(Enum): 1591 ... def some_behavior(self): 1592 ... pass 1593 ... 1594 >>> class Bar(Foo): 1595 ... HAPPY = 1 1596 ... SAD = 2 1597 ... 1598 \end{python} 1599 Allowing subclassing of enums that define members would lead to a violation of some important invariants of types and instances. 1600 On the other hand, it makes sense to allow sharing some common behavior between a group of enumerations. (See OrderedEnum for an example.) 1601 1602 \subsection{Dataclass support} 1603 1604 When inheriting from a @dataclass@, the @__repr__()@ omits the inherited class' name. 1605 For example: 1606 \begin{python} 1607 >>> from dataclasses import dataclass, field 1608 >>> $@$dataclass 1609 ... class CreatureDataMixin: 1610 ... size: str 1611 ... legs: int 1612 ... tail: bool = field(repr=False, default=True) 1613 ... 1614 >>> class Creature(CreatureDataMixin, Enum): 1615 ... BEETLE = 'small', 6 1616 ... DOG = 'medium', 4 1617 ... 1618 >>> Creature.DOG 1619 <Creature.DOG: size='medium', legs=4> 1620 \end{python} 1621 Use the @dataclass()@ argument repr=False to use the standard @repr()@. 1622 1623 Changed in version 3.12: Only the dataclass fields are shown in the value area, not the dataclass' name. 1624 1625 \subsection{Pickling} 1626 1627 Enumerations can be pickled and unpickled: 1628 \begin{python} 1629 >>> from test.test_enum import Fruit 1630 >>> from pickle import dumps, loads 1631 >>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO)) 1632 True 1633 \end{python} 1634 The usual restrictions for pickling apply: picklable enums must be defined in the top level of a module, since unpickling requires them to be importable from that module. 1635 1636 Note: With pickle protocol version 4 it is possible to easily pickle enums nested in other classes. 1637 1638 It is possible to modify how enum members are pickled/unpickled by defining @__reduce_ex__()@ in the enumeration class. 1639 The default method is by-value, but enums with complicated values may want to use by-name: 1640 \begin{python} 1641 >>> import enum 1642 >>> class MyEnum(enum.Enum): 1643 ... __reduce_ex__ = enum.pickle_by_enum_name 1644 \end{python} 1645 Note: Using by-name for flags is not recommended, as unnamed aliases will not unpickle. 1646 1647 \subsection{Functional API} 1648 1649 The @Enum@ class is callable, providing the following functional API: 1650 \begin{python} 1651 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG') 1652 >>> Animal 1653 <enum 'Animal'> 1654 >>> Animal.ANT 1655 <Animal.ANT: 1> 1656 >>> list(Animal) 1657 [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>] 1658 \end{python} 1659 The semantics of this API resemble @namedtuple@. 1660 The first argument of the call to @Enum@ is the name of the enumeration. 1661 1662 The second argument is the source of enumeration member names. 1663 It can be a whitespace-separated string of names, a sequence of names, a sequence of 2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to values. 1664 The last two options enable assigning arbitrary values to enumerations; 1665 the others auto-assign increasing integers starting with 1 (use the @start@ parameter to specify a different starting value). 1666 A new class derived from @Enum@ is returned. 1667 In other words, the above assignment to Animal is equivalent to: 1668 \begin{python} 1669 >>> class Animal(Enum): 1670 ... ANT = 1 1671 ... BEE = 2 1672 ... CAT = 3 1673 ... DOG = 4 1674 ... 1675 \end{python} 1676 The reason for defaulting to 1 as the starting number and not 0 is that 0 is @False@ in a boolean sense, but by default enum members all evaluate to @True@. 1677 1678 Pickling enums created with the functional API can be tricky as frame stack implementation details are used to try and figure out which module the enumeration is being created in (e.g. it will fail if you use a utility function in a separate module, and also may not work on IronPython or Jython). 1679 The solution is to specify the module name explicitly as follows: 1680 \begin{python} 1681 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__) 1682 \end{python} 1683 Warning: If module is not supplied, and @Enum@ cannot determine what it is, the new @Enum@ members will not be unpicklable; to keep errors closer to the source, pickling will be disabled. 1684 1685 The new pickle protocol 4 also, in some circumstances, relies on @__qualname__@ being set to the location where pickle will be able to find the class. 1686 For example, if the class was made available in class SomeData in the global scope: 1687 \begin{python} 1688 >>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal') 1689 \end{python} 1690 The complete signature is: 1691 \begin{python} 1692 Enum( 1693 value='NewEnumName', 1694 names=<...>, 1695 *, 1696 module='...', 1697 qualname='...', 1698 type=<mixed-in class>, 1699 start=1, 1700 ) 1701 \end{python} 1702 \begin{itemize} 1703 \item 1704 @value@: What the new enum class will record as its name. 1705 \item 1706 @names@: The enum members. 1707 This can be a whitespace- or comma-separated string (values will start at 1 unless otherwise specified): 1708 \begin{python} 1709 'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE' 1710 \end{python} 1711 or an iterator of names: 1712 \begin{python} 1713 ['RED', 'GREEN', 'BLUE'] 1714 \end{python} 1715 or an iterator of (name, value) pairs: 1716 \begin{python} 1717 [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)] 1718 \end{python} 1719 or a mapping: 1720 \begin{python} 1721 {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42} 1722 \end{python} 1723 \item 1724 module: name of module where new enum class can be found. 1725 \item 1726 @qualname@: where in module new enum class can be found. 1727 \item 1728 @type@: type to mix in to new enum class. 1729 \item 1730 @start@: number to start counting at if only names are passed in. 1731 \end{itemize} 1732 Changed in version 3.5: The start parameter was added. 1733 1734 \subsection{Derived Enumerations} 1735 1736 \subsection{IntEnum} 1737 1738 The first variation of @Enum@ that is provided is also a subclass of @int@. 1739 Members of an @IntEnum@ can be compared to integers; 1740 by extension, integer enumerations of different types can also be compared to each other: 1741 \begin{python} 1742 >>> from enum import IntEnum 1743 >>> class Shape(IntEnum): 1744 ... CIRCLE = 1 1745 ... SQUARE = 2 1746 ... 1747 >>> class Request(IntEnum): 1748 ... POST = 1 1749 ... GET = 2 1750 ... 1751 >>> Shape == 1 1752 False 1753 >>> Shape.CIRCLE == 1 1754 True 1755 >>> Shape.CIRCLE == Request.POST 1756 True 1757 \end{python} 1758 However, they still can't be compared to standard @Enum@ enumerations: 1759 \begin{python} 1760 >>> class Shape(IntEnum): 1761 ... CIRCLE = 1 1762 ... SQUARE = 2 1763 ... 1764 >>> class Color(Enum): 1765 ... RED = 1 1766 ... GREEN = 2 1767 ... 1768 >>> Shape.CIRCLE == Color.RED 1769 False 1770 \end{python} 1771 @IntEnum@ values behave like integers in other ways you'd expect: 1772 \begin{python} 1773 >>> int(Shape.CIRCLE) 1774 1 1775 >>> ['a', 'b', 'c'][Shape.CIRCLE] 1776 'b' 1777 >>> [i for i in range(Shape.SQUARE)] 1778 [0, 1] 1779 \end{python} 1780 1781 \subsection{StrEnum} 1782 1783 The second variation of @Enum@ that is provided is also a subclass of @str@. 1784 Members of a @StrEnum@ can be compared to strings; 1785 by extension, string enumerations of different types can also be compared to each other. 1786 1787 New in version 3.11. 1788 1789 \subsection{IntFlag} 1790 1791 The next variation of @Enum@ provided, @IntFlag@, is also based on @int@. 1792 The difference being @IntFlag@ members can be combined using the bitwise operators (@&, |, ^, ~@) and the result is still an @IntFlag@ member, if possible. 1793 Like @IntEnum@, @IntFlag@ members are also integers and can be used wherever an int is used. 1794 1795 Note: Any operation on an IntFlag member besides the bit-wise operations will lose the @IntFlag@ membership. 1796 1797 Bit-wise operations that result in invalid @IntFlag@ values will lose the @IntFlag@ membership. 1798 See @FlagBoundary@ for details. 1799 1800 New in version 3.6. 1801 1802 Changed in version 3.11. 1803 1804 Sample @IntFlag@ class: 1805 \begin{python} 1806 >>> from enum import IntFlag 1807 >>> class Perm(IntFlag): 1808 ... R = 4 1809 ... W = 2 1810 ... X = 1 1811 ... 1812 >>> Perm.R | Perm.W 1813 <Perm.R|W: 6> 1814 >>> Perm.R + Perm.W 1815 6 1816 >>> RW = Perm.R | Perm.W 1817 >>> Perm.R in RW 1818 True 1819 \end{python} 1820 It is also possible to name the combinations: 1821 \begin{python} 1822 >>> class Perm(IntFlag): 1823 ... R = 4 1824 ... W = 2 1825 ... X = 1 1826 ... RWX = 7 1827 ... 1828 >>> Perm.RWX 1829 <Perm.RWX: 7> 1830 >>> ~Perm.RWX 1831 <Perm: 0> 1832 >>> Perm(7) 1833 <Perm.RWX: 7> 1834 \end{python} 1835 Note: Named combinations are considered aliases. Aliases do not show up during iteration, but can be returned from by-value lookups. 1836 1837 Changed in version 3.11. 1838 1839 Another important difference between @IntFlag@ and @Enum@ is that if no flags are set (the value is 0), its boolean evaluation is @False@: 1840 \begin{python} 1841 >>> Perm.R & Perm.X 1842 <Perm: 0> 1843 >>> bool(Perm.R & Perm.X) 1844 False 1845 \end{python} 1846 Because @IntFlag@ members are also subclasses of int they can be combined with them (but may lose @IntFlag@ membership: 1847 \begin{python} 1848 >>> Perm.X | 4 1849 <Perm.R|X: 5> 1850 1851 >>> Perm.X + 8 1852 9 1853 \end{python} 1854 Note: The negation operator, @~@, always returns an @IntFlag@ member with a positive value: 1855 \begin{python} 1856 >>> (~Perm.X).value == (Perm.R|Perm.W).value == 6 1857 True 1858 \end{python} 1859 @IntFlag@ members can also be iterated over: 1860 \begin{python} 1861 >>> list(RW) 1862 [<Perm.R: 4>, <Perm.W: 2>] 1863 \end{python} 1864 New in version 3.11. 1865 1866 \subsection{Flag} 1867 1868 The last variation is @Flag@. 1869 Like @IntFlag@, @Flag@ members can be combined using the bitwise operators (@&, |, ^, ~@). 1870 Unlike @IntFlag@, they cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@. 1871 While it is possible to specify the values directly it is recommended to use @auto@ as the value and let @Flag@ select an appropriate value. 1872 1873 New in version 3.6. 1874 1875 Like @IntFlag@, if a combination of @Flag@ members results in no flags being set, the boolean evaluation is @False@: 1876 \begin{python} 1877 >>> from enum import Flag, auto 1878 >>> class Color(Flag): 1879 ... RED = auto() 1880 ... BLUE = auto() 1881 ... GREEN = auto() 1882 ... 1883 >>> Color.RED & Color.GREEN 1884 <Color: 0> 1885 >>> bool(Color.RED & Color.GREEN) 1886 False 1887 \end{python} 1888 Individual flags should have values that are powers of two (1, 2, 4, 8, ...), while combinations of flags will not: 1889 \begin{python} 1890 >>> class Color(Flag): 1891 ... RED = auto() 1892 ... BLUE = auto() 1893 ... GREEN = auto() 1894 ... WHITE = RED | BLUE | GREEN 1895 ... 1896 >>> Color.WHITE 1897 <Color.WHITE: 7> 1898 \end{python} 1899 Giving a name to the ``no flags set'' condition does not change its boolean value: 1900 \begin{python} 1901 >>> class Color(Flag): 1902 ... BLACK = 0 1903 ... RED = auto() 1904 ... BLUE = auto() 1905 ... GREEN = auto() 1906 ... 1907 >>> Color.BLACK 1908 <Color.BLACK: 0> 1909 >>> bool(Color.BLACK) 1910 False 1911 \end{python} 1912 @Flag@ members can also be iterated over: 1913 \begin{python} 1914 >>> purple = Color.RED | Color.BLUE 1915 >>> list(purple) 1916 [<Color.RED: 1>, <Color.BLUE: 2>] 1917 \end{python} 1918 New in version 3.11. 1919 1920 Note: For the majority of new code, @Enum@ and @Flag@ are strongly recommended, since @IntEnum@ and @IntFlag@ break some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations). 1921 @IntEnum@ and @IntFlag@ should be used only in cases where @Enum@ and @Flag@ will not do; 1922 for example, when integer constants are replaced with enumerations, or for interoperability with other systems. 1923 1924 \subsection{Others} 1925 1926 While @IntEnum@ is part of the enum module, it would be very simple to implement independently: 1927 \begin{python} 1928 class IntEnum(int, Enum): 1929 pass 1930 \end{python} 1931 This demonstrates how similar derived enumerations can be defined; 1932 for example a @FloatEnum@ that mixes in float instead of @int@. 1933 1934 Some rules: 1935 \begin{itemize} 1936 \item 1937 When subclassing @Enum@, mix-in types must appear before @Enum@ itself in the sequence of bases, as in the @IntEnum@ example above. 1938 \item 1939 Mix-in types must be subclassable. 1940 For example, @bool@ and @range@ are not subclassable and will throw an error during Enum creation if used as the mix-in type. 1941 \item 1942 While @Enum@ can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. @int@ above. 1943 This restriction does not apply to mix-ins which only add methods and don't specify another type. 1944 \item 1945 When another data type is mixed in, the value attribute is not the same as the enum member itself, although it is equivalent and will compare equal. 1946 \item 1947 A data type is a mixin that defines @__new__()@, or a @dataclass@ 1948 \item 1949 \%-style formatting: @%s@ and @%r@ call the @Enum@ class's @__str__()@ and @__repr__()@ respectively; other codes (such as @%i@ or @%h@ for @IntEnum@) treat the enum member as its mixed-in type. 1950 \item 1951 Formatted string literals, @str.format()@, and format() will use the enum's @__str__()@ method. 1952 \end{itemize} 1953 Note: Because @IntEnum@, @IntFlag@, and @StrEnum@ are designed to be drop-in replacements for existing constants, their @__str__()@ method has been reset to their data types' @__str__()@ method. 1954 1955 \subsection{When to use \lstinline{__new__()} vs. \lstinline{__init__()}} 1956 1957 @__new__()@ must be used whenever you want to customize the actual value of the @Enum@ member. 1958 Any other modifications may go in either @__new__()@ or @__init__()@, with @__init__()@ being preferred. 1959 1960 For example, if you want to pass several items to the constructor, but only want one of them to be the value: 1961 \begin{python} 1962 >>> class Coordinate(bytes, Enum): 1963 ... """ 1964 ... Coordinate with binary codes that can be indexed by the int code. 1965 ... """ 1966 ... def __new__(cls, value, label, unit): 1967 ... obj = bytes.__new__(cls, [value]) 1968 ... obj._value_ = value 1969 ... obj.label = label 1970 ... obj.unit = unit 1971 ... return obj 1972 ... PX = (0, 'P.X', 'km') 1973 ... PY = (1, 'P.Y', 'km') 1974 ... VX = (2, 'V.X', 'km/s') 1975 ... VY = (3, 'V.Y', 'km/s') 1976 1977 >>> print(Coordinate['PY']) 1978 Coordinate.PY 1979 1980 >>> print(Coordinate(3)) 1981 Coordinate.VY 1982 \end{python} 1983 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; instead, use the data type directly. 1984 1985 \subsection{Finer Points} 1986 1987 Supported @__dunder__@ names 1988 1989 @__members__@ is a read-only ordered mapping of member\_name:member items. It is only available on the class. 1990 1991 @__new__()@, if specified, must create and return the enum members; it is also a very good idea to set the member's @_value_@ appropriately. Once all the members are created it is no longer used. 1992 Supported @_sunder_@ names 1993 \begin{itemize} 1994 \item 1995 @_name_@ -- name of the member 1996 \item 1997 @_value_@ -- value of the member; can be set / modified in @__new__@ 1998 \item 1999 @_missing_@ -- a lookup function used when a value is not found; may be overridden 2000 \item 2001 @_ignore_@ -- a list of names, either as a @list@ or a @str@, that will not be transformed into members, and will be removed from the final class 2002 \item 2003 @_order_@ -- used in Python 2/3 code to ensure member order is consistent (class attribute, removed during class creation) 2004 \item 2005 @_generate_@next@_value_@ -- used by the Functional API and by @auto@ to get an appropriate value for an enum member; may be overridden 2006 \end{itemize} 2007 Note: For standard @Enum@ classes the next value chosen is the last value seen incremented by one. 2008 2009 For @Flag@ classes the next value chosen will be the next highest power-of-two, regardless of the last value seen. 2010 2011 New in version 3.6: @_missing_@, @_order_@, @_generate_@next@_value_@ 2012 2013 New in version 3.7: @_ignore_@ 2014 2015 To help keep Python 2 / Python 3 code in sync an @_order_@ attribute can be provided. 2016 It will be checked against the actual order of the enumeration and raise an error if the two do not match: 2017 \begin{python} 2018 >>> class Color(Enum): 2019 ... _order_ = 'RED GREEN BLUE' 2020 ... RED = 1 2021 ... BLUE = 3 2022 ... GREEN = 2 2023 ... 2024 Traceback (most recent call last): 2025 ... 2026 TypeError: member order does not match _order_: 2027 ['RED', 'BLUE', 'GREEN'] 2028 ['RED', 'GREEN', 'BLUE'] 2029 \end{python} 2030 Note: In Python 2 code the @_order_@ attribute is necessary as definition order is lost before it can be recorded. 2031 2032 \subsection{\lstinline{_Private__names}} 2033 2034 Private names are not converted to enum members, but remain normal attributes. 2035 2036 Changed in version 3.11. 2037 2038 \subsection{\lstinline{Enum} member type} 2039 2040 @Enum@ members are instances of their enum class, and are normally accessed as @EnumClass.member@. 2041 In certain situations, such as writing custom enum behavior, being able to access one member directly from another is useful, and is supported; 2042 however, in order to avoid name clashes between member names and attributes/methods from mixed-in classes, upper-case names are strongly recommended. 2043 2044 Changed in version 3.5. 2045 2046 \subsection{Creating members that are mixed with other data types} 2047 2048 When subclassing other data types, such as @int@ or @str@, with an @Enum@, all values after the = @are@ passed to that data type's constructor. For example: 2049 \begin{python} 2050 >>> class MyEnum(IntEnum): # help(int) -> int(x, base=10) -> integer 2051 ... example = '11', 16 # so x='11' and base=16 2052 ... 2053 MyEnum.example.value # and hex(11) is... 2054 17 2055 \end{python} 2056 2057 \subsection{\lstinline{Boolean} value of \lstinline{Enum} classes and members} 2058 2059 Enum classes that are mixed with non-@Enum@ types (such as @int@, @str@, etc.) are evaluated according to the mixed-in type's rules; 2060 otherwise, all members evaluate as @True@. 2061 To make your own enum's boolean evaluation depend on the member's value add the following to your class: 2062 \begin{python} 2063 def __bool__(self): 2064 return bool(self.value) 2065 \end{python} 2066 Plain @Enum@ classes always evaluate as @True@. 2067 2068 \subsection{\lstinline{Enum} classes with methods} 2069 2070 If you give your enum subclass extra methods, like the Planet class below, those methods will show up in a dir() of the member, but not of the class: 2071 \begin{python} 2072 >>> dir(Planet) 2073 ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', 2074 '__class__', '__doc__', '__members__', '__module__'] 2075 >>> dir(Planet.EARTH) 2076 ['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value'] 2077 \end{python} 2078 2079 \subsection{Combining members of \lstinline{Flag}} 2080 2081 Iterating over a combination of @Flag@ members will only return the members that are comprised of a single bit: 2082 \begin{python} 2083 >>> class Color(Flag): 2084 ... RED = auto() 2085 ... GREEN = auto() 2086 ... BLUE = auto() 2087 ... MAGENTA = RED | BLUE 2088 ... YELLOW = RED | GREEN 2089 ... CYAN = GREEN | BLUE 2090 ... 2091 >>> Color(3) # named combination 2092 <Color.YELLOW: 3> 2093 >>> Color(7) # not named combination 2094 <Color.RED|GREEN|BLUE: 7> 2095 \end{python} 2096 2097 \subsection{\lstinline{Flag} and \lstinline{IntFlag} minutia} 2098 2099 Using the following snippet for our examples: 2100 \begin{python} 2101 >>> class Color(IntFlag): 2102 ... BLACK = 0 2103 ... RED = 1 2104 ... GREEN = 2 2105 ... BLUE = 4 2106 ... PURPLE = RED | BLUE 2107 ... WHITE = RED | GREEN | BLUE 2108 ... 2109 \end{python} 2110 the following are true: 2111 \begin{itemize} 2112 \item 2113 single-bit flags are canonical 2114 \item 2115 multi-bit and zero-bit flags are aliases 2116 \item 2117 only canonical flags are returned during iteration: 2118 \begin{python} 2119 >>> list(Color.WHITE) 2120 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 2121 \end{python} 2122 negating a flag or flag set returns a new flag/flag set with the corresponding positive integer value: 2123 \begin{python} 2124 >>> Color.BLUE 2125 <Color.BLUE: 4> 2126 2127 >>> ~Color.BLUE 2128 <Color.RED|GREEN: 3> 2129 \end{python} 2130 \item 2131 names of pseudo-flags are constructed from their members' names: 2132 \begin{python} 2133 >>> (Color.RED | Color.GREEN).name 2134 'RED|GREEN' 2135 \end{python} 2136 \item 2137 multi-bit flags, aka aliases, can be returned from operations: 2138 \begin{python} 2139 >>> Color.RED | Color.BLUE 2140 <Color.PURPLE: 5> 2141 2142 >>> Color(7) # or Color(-1) 2143 <Color.WHITE: 7> 2144 2145 >>> Color(0) 2146 <Color.BLACK: 0> 2147 \end{python} 2148 \item 2149 membership / containment checking: zero-valued flags are always considered to be contained: 2150 \begin{python} 2151 >>> Color.BLACK in Color.WHITE 2152 True 2153 \end{python} 2154 otherwise, only if all bits of one flag are in the other flag will True be returned: 2155 \begin{python} 2156 >>> Color.PURPLE in Color.WHITE 2157 True 2158 2159 >>> Color.GREEN in Color.PURPLE 2160 False 2161 \end{python} 2162 \end{itemize} 2163 There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: @STRICT@, @CONFORM@, @EJECT@, and @KEEP@: 2164 \begin{itemize} 2165 \item 2166 @STRICT@ --> raises an exception when presented with invalid values 2167 \item 2168 @CONFORM@ --> discards any invalid bits 2169 \item 2170 @EJECT@ --> lose Flag status and become a normal int with the given value 2171 \item 2172 @KEEP@ --> keep the extra bits 2173 \begin{itemize} 2174 \item 2175 keeps Flag status and extra bits 2176 \item 2177 extra bits do not show up in iteration 2178 \item 2179 extra bits do show up in repr() and str() 2180 \end{itemize} 2181 \end{itemize} 2182 The default for @Flag@ is @STRICT@, the default for @IntFlag@ is @EJECT@, and the default for @_convert_@ is @KEEP@ (see @ssl.Options@ for an example of when @KEEP@ is needed). 2183 2184 \section{How are Enums and Flags different?} 2185 2186 Enums have a custom metaclass that affects many aspects of both derived @Enum@ classes and their instances (members). 2187 2188 \subsection{Enum Classes} 2189 2190 The @EnumType@ metaclass is responsible for providing the @__contains__()@, @__dir__()@, @__iter__()@ and other methods that allow one to do things with an @Enum@ class that fail on a typical class, such as @list(Color)@ or @some_enum_var@ in @Color@. 2191 @EnumType@ is responsible for ensuring that various other methods on the final @Enum@ class are correct (such as @__new__()@, @__getnewargs__()@, @__str__()@ and @__repr__()@). 2192 2193 \subsection{Flag Classes} 2194 2195 Flags have an expanded view of aliasing: to be canonical, the value of a flag needs to be a power-of-two value, and not a duplicate name. 2196 So, in addition to the @Enum@ definition of alias, a flag with no value (a.k.a. 0) or with more than one power-of-two value (e.g. 3) is considered an alias. 2197 2198 \subsection{Enum Members (aka instances)} 2199 2200 The most interesting thing about enum members is that they are singletons. 2201 @EnumType@ creates them all while it is creating the enum class itself, and then puts a custom @__new__()@ in place to ensure that no new ones are ever instantiated by returning only the existing member instances. 2202 2203 \subsection{Flag Members} 2204 2205 Flag members can be iterated over just like the @Flag@ class, and only the canonical members will be returned. 2206 For example: 2207 \begin{python} 2208 >>> list(Color) 2209 [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>] 2210 \end{python} 2211 (Note that BLACK, PURPLE, and WHITE do not show up.) 2212 2213 Inverting a flag member returns the corresponding positive value, rather than a negative value -- for example: 2214 \begin{python} 2215 >>> ~Color.RED 2216 <Color.GREEN|BLUE: 6> 2217 \end{python} 2218 Flag members have a length corresponding to the number of power-of-two values they contain. For example: 2219 \begin{python} 2220 >>> len(Color.PURPLE) 2221 2 2222 \end{python} 2223 2224 \subsection{Enum Cookbook} 2225 2226 While @Enum@, @IntEnum@, @StrEnum@, @Flag@, and @IntFlag@ are expected to cover the majority of use-cases, they cannot cover them all. Here are recipes for some different types of enumerations that can be used directly, or as examples for creating one's own. 2227 2228 \subsection{Omitting values} 2229 2230 In many use-cases, one doesn't care what the actual value of an enumeration is. There are several ways to define this type of simple enumeration: 2231 \begin{itemize} 2232 \item 2233 use instances of auto for the value 2234 \item 2235 use instances of object as the value 2236 \item 2237 use a descriptive string as the value 2238 \item 2239 use a tuple as the value and a custom @__new__()@ to replace the tuple with an @int@ value 2240 \end{itemize} 2241 Using any of these methods signifies to the user that these values are not important, and also enables one to add, remove, or reorder members without having to renumber the remaining members. 2242 2243 \subsection{Using \lstinline{auto}} 2244 2245 Using @auto@ would look like: 2246 \begin{python} 2247 >>> class Color(Enum): 2248 ... RED = auto() 2249 ... BLUE = auto() 2250 ... GREEN = auto() 2251 ... 2252 >>> Color.GREEN 2253 <Color.GREEN: 3> 2254 \end{python} 2255 2256 \subsection{Using \lstinline{object}} 2257 2258 Using @object@ would look like: 2259 \begin{python} 2260 >>> class Color(Enum): 2261 ... RED = object() 2262 ... GREEN = object() 2263 ... BLUE = object() 2264 ... 2265 >>> Color.GREEN 2266 <Color.GREEN: <object object at 0x...>> 2267 \end{python} 2268 This is also a good example of why you might want to write your own @__repr__()@: 2269 \begin{python} 2270 >>> class Color(Enum): 2271 ... RED = object() 2272 ... GREEN = object() 2273 ... BLUE = object() 2274 ... def __repr__(self): 2275 ... return "<%s.%s>" % (self.__class__.__name__, self._name_) 2276 ... 2277 >>> Color.GREEN 2278 <Color.GREEN> 2279 \end{python} 2280 2281 \subsection{Using a descriptive string} 2282 2283 Using a string as the value would look like: 2284 \begin{python} 2285 >>> class Color(Enum): 2286 ... RED = 'stop' 2287 ... GREEN = 'go' 2288 ... BLUE = 'too fast!' 2289 ... 2290 >>> Color.GREEN 2291 <Color.GREEN: 'go'> 2292 \end{python} 2293 2294 \subsection{Using a custom \lstinline{__new__()}} 2295 2296 Using an auto-numbering @__new__()@ would look like: 2297 \begin{python} 2298 >>> class AutoNumber(Enum): 2299 ... def __new__(cls): 2300 ... value = len(cls.__members__) + 1 2301 ... obj = object.__new__(cls) 2302 ... obj._value_ = value 2303 ... return obj 2304 ... 2305 >>> class Color(AutoNumber): 2306 ... RED = () 2307 ... GREEN = () 2308 ... BLUE = () 2309 ... 2310 >>> Color.GREEN 2311 <Color.GREEN: 2> 2312 \end{python} 2313 To make a more general purpose @AutoNumber@, add @*args@ to the signature: 2314 \begin{python} 2315 >>> class AutoNumber(Enum): 2316 ... def __new__(cls, *args): # this is the only change from above 2317 ... value = len(cls.__members__) + 1 2318 ... obj = object.__new__(cls) 2319 ... obj._value_ = value 2320 ... return obj 2321 \end{python} 2322 Then when you inherit from @AutoNumber@ you can write your own @__init__@ to handle any extra arguments: 2323 \begin{python} 2324 >>> class Swatch(AutoNumber): 2325 ... def __init__(self, pantone='unknown'): 2326 ... self.pantone = pantone 2327 ... AUBURN = '3497' 2328 ... SEA_GREEN = '1246' 2329 ... BLEACHED_CORAL = () # New color, no Pantone code yet! 2330 ... 2331 >>> Swatch.SEA_GREEN 2332 <Swatch.SEA_GREEN: 2> 2333 >>> Swatch.SEA_GREEN.pantone 2334 '1246' 2335 >>> Swatch.BLEACHED_CORAL.pantone 2336 'unknown' 2337 \end{python} 2338 Note: The @__new__()@ method, if defined, is used during creation of the Enum members; 2339 it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members. 2340 2341 Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; 2342 instead, use the data type directly -- e.g.: 2343 \begin{python} 2344 obj = int.__new__(cls, value) 2345 \end{python} 2346 2347 \subsection{OrderedEnum} 2348 2349 An ordered enumeration that is not based on @IntEnum@ and so maintains the normal @Enum@ invariants (such as not being comparable to other enumerations): 2350 \begin{python} 2351 >>> class OrderedEnum(Enum): 2352 ... def __ge__(self, other): 2353 ... if self.__class__ is other.__class__: 2354 ... return self.value >= other.value 2355 ... return NotImplemented 2356 ... def __gt__(self, other): 2357 ... if self.__class__ is other.__class__: 2358 ... return self.value > other.value 2359 ... return NotImplemented 2360 ... def __le__(self, other): 2361 ... if self.__class__ is other.__class__: 2362 ... return self.value <= other.value 2363 ... return NotImplemented 2364 ... def __lt__(self, other): 2365 ... if self.__class__ is other.__class__: 2366 ... return self.value < other.value 2367 ... return NotImplemented 2368 ... 2369 >>> class Grade(OrderedEnum): 2370 ... A = 5 2371 ... B = 4 2372 ... C = 3 2373 ... D = 2 2374 ... F = 1 2375 >>> Grade.C < Grade.A 2376 True 2377 \end{python} 2378 2379 \subsection{DuplicateFreeEnum} 2380 2381 Raises an error if a duplicate member value is found instead of creating an alias: 2382 \begin{python} 2383 >>> class DuplicateFreeEnum(Enum): 2384 ... def __init__(self, *args): 2385 ... cls = self.__class__ 2386 ... if any(self.value == e.value for e in cls): 2387 ... a = self.name 2388 ... e = cls(self.value).name 2389 ... raise ValueError( 2390 ... "aliases not allowed in DuplicateFreeEnum: %r --> %r" 2391 ... % (a, e)) 2392 >>> class Color(DuplicateFreeEnum): 2393 ... RED = 1 2394 ... GREEN = 2 2395 ... BLUE = 3 2396 ... GRENE = 2 2397 ... 2398 Traceback (most recent call last): 2399 ... 2400 ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN' 2401 \end{python} 2402 Note: This is a useful example for subclassing Enum to add or change other behaviors as well as disallowing aliases. 2403 If the only desired change is disallowing aliases, the @unique()@ decorator can be used instead. 2404 2405 \subsection{Planet} 2406 2407 If @__new__()@ or @__init__()@ is defined, the value of the enum member will be passed to those methods: 2408 \begin{python} 2409 >>> class Planet(Enum): 2410 ... MERCURY = (3.303e+23, 2.4397e6) 2411 ... VENUS = (4.869e+24, 6.0518e6) 2412 ... EARTH = (5.976e+24, 6.37814e6) 2413 ... MARS = (6.421e+23, 3.3972e6) 2414 ... JUPITER = (1.9e+27, 7.1492e7) 2415 ... SATURN = (5.688e+26, 6.0268e7) 2416 ... URANUS = (8.686e+25, 2.5559e7) 2417 ... NEPTUNE = (1.024e+26, 2.4746e7) 2418 ... def __init__(self, mass, radius): 2419 ... self.mass = mass # in kilograms 2420 ... self.radius = radius # in meters 2421 ... $\@$property 2422 ... def surface_gravity(self): 2423 ... # universal gravitational constant (m3 kg-1 s-2) 2424 ... G = 6.67300E-11 2425 ... return G * self.mass / (self.radius * self.radius) 2426 ... 2427 >>> Planet.EARTH.value 2428 (5.976e+24, 6378140.0) 2429 >>> Planet.EARTH.surface_gravity 2430 9.802652743337129 2431 \end{python} 2432 2433 \subsection{TimePeriod} 2434 2435 An example to show the @_ignore_@ attribute in use: 2436 \begin{python} 2437 >>> from datetime import timedelta 2438 >>> class Period(timedelta, Enum): 2439 ... "different lengths of time" 2440 ... _ignore_ = 'Period i' 2441 ... Period = vars() 2442 ... for i in range(367): 2443 ... Period['day_%d' % i] = i 2444 ... 2445 >>> list(Period)[:2] 2446 [<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>] 2447 >>> list(Period)[-2:] 2448 [<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>] 2449 \end{python} 2450 2451 \subsection{Subclassing EnumType} 2452 2453 While most enum needs can be met by customizing @Enum@ subclasses, either with class decorators or custom functions, @EnumType@ can be subclassed to provide a different Enum experience. 2454 1141 2455 1142 2456 \section{Algebraic Data Type} -
TabularUnified src/Parser/DeclarationNode.cc ¶
rbbf2cb1 rf6e8c67 82 82 83 83 DeclarationNode::~DeclarationNode() { 84 delete name; 85 84 86 // delete variable.name; 85 87 delete variable.assertions; … … 99 101 DeclarationNode * DeclarationNode::clone() const { 100 102 DeclarationNode * newnode = new DeclarationNode; 101 newnode-> set_next( maybeCopy( get_next() ));103 newnode->next = maybeCopy( next ); 102 104 newnode->name = name ? new string( *name ) : nullptr; 103 105 104 newnode->builtin = NoBuiltinType;105 106 newnode->type = maybeCopy( type ); 106 107 newnode->inLine = inLine; … … 170 171 171 172 void DeclarationNode::printList( std::ostream & os, int indent ) const { 172 Parse Node::printList( os, indent );173 ParseList::printList( os, indent ); 173 174 if ( hasEllipsis ) { 174 175 os << string( indent, ' ' ) << "and a variable number of other arguments" << endl; … … 433 434 DeclarationNode * newnode = new DeclarationNode; 434 435 newnode->type = new TypeData( TypeData::Builtin ); 435 newnode->builtin = bt; 436 newnode->type->builtintype = newnode->builtin; 436 newnode->type->builtintype = bt; 437 437 return newnode; 438 438 } // DeclarationNode::newBuiltinType … … 554 554 } // DeclarationNode::copySpecifiers 555 555 556 static void addQualifiersToType( TypeData *& src, TypeData * dst ) {557 if ( dst->base ) {558 addQualifiersToType( src, dst->base );559 } else if ( dst->kind == TypeData::Function ) {560 dst->base = src;561 src = nullptr;562 } else {563 dst->qualifiers |= src->qualifiers;564 } // if565 } // addQualifiersToType566 567 556 DeclarationNode * DeclarationNode::addQualifiers( DeclarationNode * q ) { 568 557 if ( ! q ) { return this; } // empty qualifier … … 580 569 } // if 581 570 582 if ( q->type->forall ) { // forall qualifier ?583 if ( type->forall ) { // polymorphic routine ?584 type->forall->appendList( q->type->forall ); // augment forall qualifier585 } else {586 if ( type->kind == TypeData::Aggregate ) { // struct/union ?587 if ( type->aggregate.params ) { // polymorphic ?588 type->aggregate.params->appendList( q->type->forall ); // augment forall qualifier589 } else { // not polymorphic590 type->aggregate.params = q->type->forall; // set forall qualifier591 } // if592 } else { // not polymorphic593 type->forall = q->type->forall; // make polymorphic routine594 } // if595 } // if596 q->type->forall = nullptr; // forall qualifier moved597 } // if598 599 571 checkQualifiers( type, q->type ); 572 BuiltinType const builtin = type->builtintype; 600 573 if ( (builtin == Zero || builtin == One) && q->type->qualifiers.any() && error.length() == 0 ) { 601 574 SemanticWarning( yylloc, Warning::BadQualifiersZeroOne, builtinTypeNames[builtin] ); 602 575 } // if 603 addQualifiersToType( q->type, type ); 576 type = ::addQualifiers( q->type, type ); 577 q->type = nullptr; 604 578 605 579 delete q; … … 607 581 } // addQualifiers 608 582 609 static void addTypeToType( TypeData *& src, TypeData *& dst ) {610 if ( src->forall && dst->kind == TypeData::Function ) {611 if ( dst->forall ) {612 dst->forall->appendList( src->forall );613 } else {614 dst->forall = src->forall;615 } // if616 src->forall = nullptr;617 } // if618 if ( dst->base ) {619 addTypeToType( src, dst->base );620 } else {621 switch ( dst->kind ) {622 case TypeData::Unknown:623 src->qualifiers |= dst->qualifiers;624 dst = src;625 src = nullptr;626 break;627 case TypeData::Basic:628 dst->qualifiers |= src->qualifiers;629 if ( src->kind != TypeData::Unknown ) {630 assert( src->kind == TypeData::Basic );631 632 if ( dst->basictype == DeclarationNode::NoBasicType ) {633 dst->basictype = src->basictype;634 } else if ( src->basictype != DeclarationNode::NoBasicType )635 SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",636 DeclarationNode::basicTypeNames[ dst->basictype ],637 DeclarationNode::basicTypeNames[ src->basictype ] );638 if ( dst->complextype == DeclarationNode::NoComplexType ) {639 dst->complextype = src->complextype;640 } else if ( src->complextype != DeclarationNode::NoComplexType )641 SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".",642 DeclarationNode::complexTypeNames[ src->complextype ],643 DeclarationNode::complexTypeNames[ src->complextype ] );644 if ( dst->signedness == DeclarationNode::NoSignedness ) {645 dst->signedness = src->signedness;646 } else if ( src->signedness != DeclarationNode::NoSignedness )647 SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",648 DeclarationNode::signednessNames[ dst->signedness ],649 DeclarationNode::signednessNames[ src->signedness ] );650 if ( dst->length == DeclarationNode::NoLength ) {651 dst->length = src->length;652 } else if ( dst->length == DeclarationNode::Long && src->length == DeclarationNode::Long ) {653 dst->length = DeclarationNode::LongLong;654 } else if ( src->length != DeclarationNode::NoLength )655 SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".",656 DeclarationNode::lengthNames[ dst->length ],657 DeclarationNode::lengthNames[ src->length ] );658 } // if659 break;660 default:661 switch ( src->kind ) {662 case TypeData::Aggregate:663 case TypeData::Enum:664 dst->base = new TypeData( TypeData::AggregateInst );665 dst->base->aggInst.aggregate = src;666 if ( src->kind == TypeData::Aggregate ) {667 dst->base->aggInst.params = maybeCopy( src->aggregate.actuals );668 } // if669 dst->base->qualifiers |= src->qualifiers;670 src = nullptr;671 break;672 default:673 if ( dst->forall ) {674 dst->forall->appendList( src->forall );675 } else {676 dst->forall = src->forall;677 } // if678 src->forall = nullptr;679 dst->base = src;680 src = nullptr;681 } // switch682 } // switch683 } // if684 }685 686 583 DeclarationNode * DeclarationNode::addType( DeclarationNode * o, bool copyattr ) { 687 if ( o ) { 688 checkSpecifiers( o ); 689 copySpecifiers( o, copyattr ); 690 if ( o->type ) { 691 if ( ! type ) { 692 if ( o->type->kind == TypeData::Aggregate || o->type->kind == TypeData::Enum ) { 693 // Hide type information aggregate instances. 694 type = new TypeData( TypeData::AggregateInst ); 695 type->aggInst.aggregate = o->type; // change ownership 696 type->aggInst.aggregate->aggregate.attributes.swap( o->attributes ); // change ownership 697 if ( o->type->kind == TypeData::Aggregate ) { 698 type->aggInst.hoistType = o->type->aggregate.body; 699 type->aggInst.params = maybeCopy( o->type->aggregate.actuals ); 700 } else { 701 type->aggInst.hoistType = o->type->enumeration.body; 702 } // if 703 type->qualifiers |= o->type->qualifiers; 704 } else { 705 type = o->type; 706 } // if 707 o->type = nullptr; // change ownership 708 } else { 709 addTypeToType( o->type, type ); 710 } // if 711 } // if 712 if ( o->bitfieldWidth ) { 713 bitfieldWidth = o->bitfieldWidth; 714 } // if 715 716 // there may be typedefs chained onto the type 717 if ( o->get_next() ) { 718 set_last( o->get_next()->clone() ); 719 } // if 720 } // if 584 if ( !o ) return this; 585 586 checkSpecifiers( o ); 587 copySpecifiers( o, copyattr ); 588 if ( o->type ) { 589 type = ::addType( o->type, type, o->attributes ); 590 o->type = nullptr; 591 } // if 592 if ( o->bitfieldWidth ) { 593 bitfieldWidth = o->bitfieldWidth; 594 } // if 595 596 // there may be typedefs chained onto the type 597 if ( o->next ) { 598 set_last( o->next->clone() ); 599 } // if 600 721 601 delete o; 722 723 602 return this; 724 603 } 725 604 726 605 DeclarationNode * DeclarationNode::addEnumBase( DeclarationNode * o ) { 727 if ( o && o->type ){728 type->base = o->type;606 if ( o && o->type ) { 607 type->base = o->type; 729 608 } // if 730 609 delete o; … … 745 624 if ( variable.tyClass != ast::TypeDecl::NUMBER_OF_KINDS ) { 746 625 if ( variable.assertions ) { 747 variable.assertions-> appendList( assertions );626 variable.assertions->set_last( assertions ); 748 627 } else { 749 628 variable.assertions = assertions; … … 756 635 case TypeData::Symbolic: 757 636 if ( type->symbolic.assertions ) { 758 type->symbolic.assertions-> appendList( assertions );637 type->symbolic.assertions->set_last( assertions ); 759 638 } else { 760 639 type->symbolic.assertions = assertions; … … 810 689 DeclarationNode * DeclarationNode::setBase( TypeData * newType ) { 811 690 if ( type ) { 812 TypeData * prevBase = type; 813 TypeData * curBase = type->base; 814 while ( curBase != nullptr ) { 815 prevBase = curBase; 816 curBase = curBase->base; 817 } // while 818 prevBase->base = newType; 691 type->setLastBase( newType ); 819 692 } else { 820 693 type = newType; … … 857 730 assert( p->type->kind == TypeData::Pointer || p->type->kind == TypeData::Reference ); 858 731 if ( type ) { 859 switch ( type->kind ) { 860 case TypeData::Aggregate: 861 case TypeData::Enum: 862 p->type->base = new TypeData( TypeData::AggregateInst ); 863 p->type->base->aggInst.aggregate = type; 864 if ( type->kind == TypeData::Aggregate ) { 865 p->type->base->aggInst.params = maybeCopy( type->aggregate.actuals ); 866 } // if 867 p->type->base->qualifiers |= type->qualifiers; 868 break; 869 870 default: 871 p->type->base = type; 872 } // switch 732 p->type->base = makeNewBase( type ); 873 733 type = nullptr; 874 734 } // if … … 880 740 } 881 741 882 static TypeData * findLast( TypeData * a ) {883 assert( a );884 TypeData * cur = a;885 while ( cur->base ) {886 cur = cur->base;887 } // while888 return cur;889 }890 891 742 DeclarationNode * DeclarationNode::addNewArray( DeclarationNode * a ) { 892 743 if ( ! a ) return this; 893 744 assert( a->type->kind == TypeData::Array ); 894 TypeData * lastArray = findLast( a->type );895 745 if ( type ) { 896 switch ( type->kind ) { 897 case TypeData::Aggregate: 898 case TypeData::Enum: 899 lastArray->base = new TypeData( TypeData::AggregateInst ); 900 lastArray->base->aggInst.aggregate = type; 901 if ( type->kind == TypeData::Aggregate ) { 902 lastArray->base->aggInst.params = maybeCopy( type->aggregate.actuals ); 903 } // if 904 lastArray->base->qualifiers |= type->qualifiers; 905 break; 906 default: 907 lastArray->base = type; 908 } // switch 746 a->type->setLastBase( makeNewBase( type ) ); 909 747 type = nullptr; 910 748 } // if … … 960 798 DeclarationNode * DeclarationNode::cloneBaseType( DeclarationNode * o, bool copyattr ) { 961 799 if ( ! o ) return nullptr; 962 963 800 o->copySpecifiers( this, copyattr ); 964 801 if ( type ) { 965 TypeData * srcType = type; 966 967 // search for the base type by scanning off pointers and array designators 968 while ( srcType->base ) { 969 srcType = srcType->base; 970 } // while 971 972 TypeData * newType = srcType->clone(); 973 if ( newType->kind == TypeData::AggregateInst ) { 974 // don't duplicate members 975 if ( newType->aggInst.aggregate->kind == TypeData::Enum ) { 976 delete newType->aggInst.aggregate->enumeration.constants; 977 newType->aggInst.aggregate->enumeration.constants = nullptr; 978 newType->aggInst.aggregate->enumeration.body = false; 979 } else { 980 assert( newType->aggInst.aggregate->kind == TypeData::Aggregate ); 981 delete newType->aggInst.aggregate->aggregate.fields; 982 newType->aggInst.aggregate->aggregate.fields = nullptr; 983 newType->aggInst.aggregate->aggregate.body = false; 984 } // if 985 // don't hoist twice 986 newType->aggInst.hoistType = false; 987 } // if 988 989 newType->forall = maybeCopy( type->forall ); 990 if ( ! o->type ) { 991 o->type = newType; 992 } else { 993 addTypeToType( newType, o->type ); 994 delete newType; 995 } // if 802 o->type = ::cloneBaseType( type, o->type ); 996 803 } // if 997 804 return o; … … 1099 906 std::back_insert_iterator<std::vector<ast::ptr<ast::Decl>>> out( outputList ); 1100 907 1101 for ( const DeclarationNode * cur = firstNode ; cur ; cur = strict_next( cur )) {908 for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) { 1102 909 try { 1103 910 bool extracted_named = false; … … 1167 974 std::back_insert_iterator<std::vector<ast::ptr<ast::DeclWithType>>> out( outputList ); 1168 975 1169 for ( const DeclarationNode * cur = firstNode; cur; cur = strict_next( cur )) {976 for ( const DeclarationNode * cur = firstNode; cur; cur = cur->next ) { 1170 977 try { 1171 978 ast::Decl * decl = cur->build(); … … 1217 1024 std::back_insert_iterator<std::vector<ast::ptr<ast::Type>>> out( outputList ); 1218 1025 1219 for ( const DeclarationNode * cur = firstNode ; cur ; cur = strict_next( cur )) {1026 for ( const DeclarationNode * cur = firstNode ; cur ; cur = cur->next ) { 1220 1027 try { 1221 1028 * out++ = cur->buildType(); -
TabularUnified src/Parser/DeclarationNode.h ¶
rbbf2cb1 rf6e8c67 19 19 20 20 struct TypeData; 21 classInitializerNode;22 23 struct DeclarationNode : public ParseNode{21 struct InitializerNode; 22 23 struct DeclarationNode final : public ParseList<DeclarationNode> { 24 24 // These enumerations must harmonize with their names in DeclarationNode.cc. 25 25 enum BasicType { … … 108 108 DeclarationNode * cloneBaseType( DeclarationNode * newdecl, bool = true ); 109 109 110 DeclarationNode * appendList( DeclarationNode * node ) {111 return (DeclarationNode *)set_last( node );112 }113 114 110 virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override; 115 111 virtual void printList( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const override; … … 129 125 DeclarationNode * set_inLine( bool inL ) { inLine = inL; return this; } 130 126 131 DeclarationNode * get_last() { return (DeclarationNode *)ParseNode::get_last(); }127 const std::string * name = nullptr; 132 128 133 129 struct Variable_t { … … 144 140 }; 145 141 StaticAssert_t assert; 146 147 BuiltinType builtin = NoBuiltinType;148 142 149 143 TypeData * type = nullptr; … … 177 171 } 178 172 179 template<typename NodeType>180 NodeType * strict_next( NodeType * node ) {181 ParseNode * next = node->get_next();182 if ( nullptr == next ) return nullptr;183 if ( NodeType * ret = dynamic_cast<NodeType *>( next ) ) return ret;184 SemanticError( next->location, "internal error, non-homogeneous nodes founds in buildList processing." );185 }186 187 173 // This generic buildList is here along side its overloads. 188 174 template<typename AstType, typename NodeType, … … 193 179 std::back_insert_iterator<Container<ast::ptr<AstType>, Args...>> out( output ); 194 180 195 for ( NodeType * cur = firstNode ; cur ; cur = strict_next( cur )) {181 for ( NodeType * cur = firstNode ; cur ; cur = cur->next ) { 196 182 try { 197 183 AstType * node = dynamic_cast<AstType *>( maybeBuild( cur ) ); -
TabularUnified src/Parser/ExpressionNode.h ¶
rbbf2cb1 rf6e8c67 18 18 #include "ParseNode.h" 19 19 20 classInitializerNode;20 struct InitializerNode; 21 21 22 class ExpressionNode final : public ParseNode { 23 public: 22 struct ExpressionNode final : public ParseList<ExpressionNode> { 24 23 ExpressionNode( ast::Expr * expr = nullptr ) : expr( expr ) {} 25 24 virtual ~ExpressionNode() {} 26 25 virtual ExpressionNode * clone() const override { 27 26 if ( nullptr == expr ) return nullptr; 28 return static_cast<ExpressionNode*>( 29 (new ExpressionNode( ast::shallowCopy( expr.get() ) ))->set_next( maybeCopy( get_next() ) )); 27 ExpressionNode * node = new ExpressionNode( ast::shallowCopy( expr.get() ) ); 28 node->next = maybeCopy( next ); 29 return node; 30 30 } 31 31 -
TabularUnified src/Parser/InitializerNode.cc ¶
rbbf2cb1 rf6e8c67 36 36 : expr( _expr ), aggregate( aggrp ), designator( des ), kids( nullptr ), maybeConstructed( true ), isDelete( false ) { 37 37 if ( aggrp ) 38 kids = dynamic_cast< InitializerNode * >( get_next() );38 kids = next; 39 39 40 40 if ( kids ) … … 48 48 49 49 if ( aggrp ) 50 kids = dynamic_cast< InitializerNode * >( get_next() );50 kids = next; 51 51 52 52 if ( kids ) 53 set_next( nullptr );53 next = nullptr; 54 54 } // InitializerNode::InitializerNode 55 55 … … 73 73 while ( curdes != nullptr) { 74 74 curdes->printOneLine(os); 75 curdes = (ExpressionNode *)(curdes->get_next());75 curdes = curdes->next; 76 76 if ( curdes ) os << ", "; 77 77 } // while … … 87 87 88 88 InitializerNode *moreInit; 89 if ( ( moreInit = dynamic_cast< InitializerNode * >( get_next() )) ) {89 if ( ( moreInit = next ) ) { 90 90 moreInit->printOneLine( os ); 91 91 } // if … … 98 98 std::vector<ast::ptr<ast::Designation>> designlist; 99 99 InitializerNode * child = next_init(); 100 for ( ; child != nullptr ; child = dynamic_cast< InitializerNode * >( child->get_next() )) {100 for ( ; child != nullptr ; child = child->next ) { 101 101 std::deque<ast::ptr<ast::Expr>> desList; 102 102 buildList( child->designator, desList ); -
TabularUnified src/Parser/InitializerNode.h ¶
rbbf2cb1 rf6e8c67 18 18 #include "ParseNode.h" 19 19 20 class InitializerNode : public ParseNode { 21 public: 20 struct InitializerNode final : public ParseList<InitializerNode> { 22 21 InitializerNode( ExpressionNode *, bool aggrp = false, ExpressionNode * des = nullptr ); 23 22 InitializerNode( InitializerNode *, bool aggrp = false, ExpressionNode * des = nullptr ); -
TabularUnified src/Parser/ParseNode.h ¶
rbbf2cb1 rf6e8c67 33 33 34 34 struct DeclarationNode; 35 classInitializerNode;36 classExpressionNode;35 struct InitializerNode; 36 struct ExpressionNode; 37 37 struct StatementNode; 38 38 … … 45 45 extern YYLTYPE yylloc; 46 46 47 class ParseNode { 48 public: 49 ParseNode() {}; 50 virtual ~ParseNode() { delete next; delete name; }; 47 struct ParseNode { 48 ParseNode() {} 49 virtual ~ParseNode() {} 51 50 virtual ParseNode * clone() const = 0; 52 51 53 ParseNode * get_next() const { return next; } 54 ParseNode * set_next( ParseNode * newlink ) { next = newlink; return this; } 52 virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {} 55 53 56 ParseNode * get_last() { 57 ParseNode * current; 58 for ( current = this; current->get_next() != nullptr; current = current->get_next() ); 54 static int indent_by; 55 56 CodeLocation location = yylloc; 57 }; // ParseNode 58 59 /// Only ever use in the form `struct NAME final : public ParseList<NAME>`! 60 template<typename Next> 61 struct ParseList : public ParseNode { 62 ParseList() {} 63 virtual ~ParseList() { delete next; }; 64 virtual ParseList<Next> * clone() const = 0; 65 66 Next * get_last() { 67 Next * current = static_cast<Next *>( this ); 68 while ( current->next != nullptr ) current = current->next; 59 69 return current; 60 70 } 61 ParseNode * set_last( ParseNode* newlast ) {62 if ( newlast != nullptr ) get_last()-> set_next( newlast );63 return this;71 Next * set_last( Next * newlast ) { 72 if ( newlast != nullptr ) get_last()->next = newlast; 73 return static_cast<Next *>( this ); 64 74 } 65 75 66 virtual void print( __attribute__((unused)) std::ostream & os, __attribute__((unused)) int indent = 0 ) const {}67 76 virtual void printList( std::ostream & os, int indent = 0 ) const { 68 77 print( os, indent ); … … 70 79 } 71 80 72 static int indent_by; 73 74 ParseNode * next = nullptr; 75 const std::string * name = nullptr; 76 CodeLocation location = yylloc; 77 }; // ParseNode 81 Next * next = nullptr; 82 }; 78 83 79 84 // Must harmonize with OperName. -
TabularUnified src/Parser/StatementNode.cc ¶
rbbf2cb1 rf6e8c67 54 54 StatementNode * nextStmt = new StatementNode( 55 55 new ast::DeclStmt( decl->location, maybeBuild( decl ) ) ); 56 set_next( nextStmt );57 if ( decl-> get_next()) {58 get_next()->set_next( new StatementNode( dynamic_cast< DeclarationNode * >(decl->get_next()) ));59 decl-> set_next( 0 );56 next = nextStmt; 57 if ( decl->next ) { 58 next->next = new StatementNode( decl->next ); 59 decl->next = nullptr; 60 60 } // if 61 61 } else { 62 if ( decl-> get_next()) {63 set_next( new StatementNode( dynamic_cast< DeclarationNode * >( decl->get_next() ) ));64 decl-> set_next( 0 );62 if ( decl->next ) { 63 next = new StatementNode( decl->next ); 64 decl->next = nullptr; 65 65 } // if 66 66 agg = decl; … … 87 87 ClauseNode * prev = this; 88 88 // find end of list and maintain previous pointer 89 for ( ClauseNode * curr = prev; curr != nullptr; curr = (ClauseNode *)curr->get_next()) {90 ClauseNode * node = strict_dynamic_cast< ClauseNode * >(curr);89 for ( ClauseNode * curr = prev; curr != nullptr; curr = curr->next ) { 90 ClauseNode * node = curr; 91 91 assert( dynamic_cast<ast::CaseClause *>( node->clause.get() ) ); 92 92 prev = curr; 93 93 } // for 94 ClauseNode * node = dynamic_cast< ClauseNode * >(prev);94 ClauseNode * node = prev; 95 95 // convert from StatementNode list to Statement list 96 96 std::vector<ast::ptr<ast::Stmt>> stmts; … … 332 332 clause->when_cond = notZeroExpr( maybeMoveBuild( when ) ); 333 333 334 ExpressionNode * next = dynamic_cast<ExpressionNode *>( targetExpr->get_next() );335 targetExpr-> set_next( nullptr );334 ExpressionNode * next = targetExpr->next; 335 targetExpr->next = nullptr; 336 336 buildMoveList( next, clause->target_args ); 337 337 -
TabularUnified src/Parser/StatementNode.h ¶
rbbf2cb1 rf6e8c67 18 18 #include "ParseNode.h" 19 19 20 struct StatementNode final : public Parse Node{20 struct StatementNode final : public ParseList<StatementNode> { 21 21 StatementNode() : stmt( nullptr ) {} 22 22 StatementNode( ast::Stmt * stmt ) : stmt( stmt ) {} … … 39 39 }; // StatementNode 40 40 41 struct ClauseNode final : public Parse Node{41 struct ClauseNode final : public ParseList<ClauseNode> { 42 42 ClauseNode( ast::StmtClause * clause ) : clause( clause ) {} 43 43 virtual ~ClauseNode() {} 44 45 ClauseNode * set_last( ParseNode * newlast ) {46 ParseNode::set_last( newlast );47 return this;48 }49 44 50 45 virtual ClauseNode * clone() const final { assert( false ); return nullptr; } -
TabularUnified src/Parser/TypeData.cc ¶
rbbf2cb1 rf6e8c67 479 479 480 480 481 TypeData * TypeData::getLastBase() { 482 TypeData * cur = this; 483 while ( cur->base ) cur = cur->base; 484 return cur; 485 } 486 487 void TypeData::setLastBase( TypeData * newBase ) { 488 getLastBase()->base = newBase; 489 } 490 491 // Takes ownership of src. 492 static void addQualifiersToType( TypeData * dst, TypeData * src ) { 493 if ( dst->base ) { 494 addQualifiersToType( dst->base, src ); 495 } else if ( dst->kind == TypeData::Function ) { 496 dst->base = src; 497 src = nullptr; 498 } else { 499 dst->qualifiers |= src->qualifiers; 500 delete src; 501 } // if 502 } 503 504 // Takes ownership of all arguments, gives ownership of return value. 505 TypeData * addQualifiers( TypeData * ltype, TypeData * rtype ) { 506 if ( ltype->forall ) { 507 if ( rtype->forall ) { 508 rtype->forall->set_last( ltype->forall ); 509 } else if ( TypeData::Aggregate != rtype->kind ) { 510 rtype->forall = ltype->forall; 511 } else if ( rtype->aggregate.params ) { 512 rtype->aggregate.params->set_last( ltype->forall ); 513 } else { 514 rtype->aggregate.params = ltype->forall; 515 } 516 ltype->forall = nullptr; 517 } 518 519 addQualifiersToType( rtype, ltype ); 520 return rtype; 521 } 522 523 // Helper for addType and cloneBaseType. 524 static void addTypeToType( TypeData *& dst, TypeData *& src ) { 525 if ( src->forall && dst->kind == TypeData::Function ) { 526 if ( dst->forall ) { 527 dst->forall->set_last( src->forall ); 528 } else { 529 dst->forall = src->forall; 530 } // if 531 src->forall = nullptr; 532 } // if 533 if ( dst->base ) { 534 addTypeToType( dst->base, src ); 535 return; 536 } 537 switch ( dst->kind ) { 538 case TypeData::Unknown: 539 src->qualifiers |= dst->qualifiers; 540 // LEAKS dst? 541 dst = src; 542 src = nullptr; 543 break; 544 case TypeData::Basic: 545 dst->qualifiers |= src->qualifiers; 546 if ( src->kind != TypeData::Unknown ) { 547 assert( src->kind == TypeData::Basic ); 548 549 if ( dst->basictype == DeclarationNode::NoBasicType ) { 550 dst->basictype = src->basictype; 551 } else if ( src->basictype != DeclarationNode::NoBasicType ) { 552 SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".", 553 DeclarationNode::basicTypeNames[ dst->basictype ], 554 DeclarationNode::basicTypeNames[ src->basictype ] ); 555 } 556 if ( dst->complextype == DeclarationNode::NoComplexType ) { 557 dst->complextype = src->complextype; 558 } else if ( src->complextype != DeclarationNode::NoComplexType ) { 559 SemanticError( yylloc, "multiple declaration types \"%s\" and \"%s\".", 560 DeclarationNode::complexTypeNames[ src->complextype ], 561 DeclarationNode::complexTypeNames[ src->complextype ] ); 562 } 563 if ( dst->signedness == DeclarationNode::NoSignedness ) { 564 dst->signedness = src->signedness; 565 } else if ( src->signedness != DeclarationNode::NoSignedness ) { 566 SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".", 567 DeclarationNode::signednessNames[ dst->signedness ], 568 DeclarationNode::signednessNames[ src->signedness ] ); 569 } 570 if ( dst->length == DeclarationNode::NoLength ) { 571 dst->length = src->length; 572 } else if ( dst->length == DeclarationNode::Long && src->length == DeclarationNode::Long ) { 573 dst->length = DeclarationNode::LongLong; 574 } else if ( src->length != DeclarationNode::NoLength ) { 575 SemanticError( yylloc, "conflicting type specifier \"%s\" and \"%s\".", 576 DeclarationNode::lengthNames[ dst->length ], 577 DeclarationNode::lengthNames[ src->length ] ); 578 } 579 } // if 580 break; 581 default: 582 switch ( src->kind ) { 583 case TypeData::Aggregate: 584 case TypeData::Enum: 585 dst->base = new TypeData( TypeData::AggregateInst ); 586 dst->base->aggInst.aggregate = src; 587 if ( src->kind == TypeData::Aggregate ) { 588 dst->base->aggInst.params = maybeCopy( src->aggregate.actuals ); 589 } // if 590 dst->base->qualifiers |= src->qualifiers; 591 src = nullptr; 592 break; 593 default: 594 if ( dst->forall ) { 595 dst->forall->set_last( src->forall ); 596 } else { 597 dst->forall = src->forall; 598 } // if 599 src->forall = nullptr; 600 dst->base = src; 601 src = nullptr; 602 } // switch 603 } // switch 604 } 605 606 // Takes ownership of all arguments, gives ownership of return value. 607 TypeData * addType( TypeData * ltype, TypeData * rtype, std::vector<ast::ptr<ast::Attribute>> & attributes ) { 608 if ( rtype ) { 609 addTypeToType( rtype, ltype ); 610 return rtype; 611 } else { 612 if ( ltype->kind == TypeData::Aggregate || ltype->kind == TypeData::Enum ) { 613 // Hide type information aggregate instances. 614 rtype = new TypeData( TypeData::AggregateInst ); 615 rtype->aggInst.aggregate = ltype; 616 rtype->aggInst.aggregate->aggregate.attributes.swap( attributes ); // change ownership 617 if ( ltype->kind == TypeData::Aggregate ) { 618 rtype->aggInst.hoistType = ltype->aggregate.body; 619 rtype->aggInst.params = maybeCopy( ltype->aggregate.actuals ); 620 } else { 621 rtype->aggInst.hoistType = ltype->enumeration.body; 622 } // if 623 rtype->qualifiers |= ltype->qualifiers; 624 } else { 625 rtype = ltype; 626 } // if 627 return rtype; 628 } // if 629 } 630 631 // Takes ownership of both arguments, gives ownership of return value. 632 TypeData * cloneBaseType( TypeData * type, TypeData * other ) { 633 TypeData * newType = type->getLastBase()->clone(); 634 if ( newType->kind == TypeData::AggregateInst ) { 635 // don't duplicate members 636 if ( newType->aggInst.aggregate->kind == TypeData::Enum ) { 637 delete newType->aggInst.aggregate->enumeration.constants; 638 newType->aggInst.aggregate->enumeration.constants = nullptr; 639 newType->aggInst.aggregate->enumeration.body = false; 640 } else { 641 assert( newType->aggInst.aggregate->kind == TypeData::Aggregate ); 642 delete newType->aggInst.aggregate->aggregate.fields; 643 newType->aggInst.aggregate->aggregate.fields = nullptr; 644 newType->aggInst.aggregate->aggregate.body = false; 645 } // if 646 // don't hoist twice 647 newType->aggInst.hoistType = false; 648 } // if 649 newType->forall = maybeCopy( type->forall ); 650 651 if ( other ) { 652 addTypeToType( other, newType ); 653 delete newType; 654 return other; 655 } // if 656 return newType; 657 } 658 659 TypeData * makeNewBase( TypeData * type ) { 660 switch ( type->kind ) { 661 case TypeData::Aggregate: 662 case TypeData::Enum: { 663 TypeData * out = new TypeData( TypeData::AggregateInst ); 664 out->aggInst.aggregate = type; 665 if ( TypeData::Aggregate == type->kind ) { 666 out->aggInst.params = maybeCopy( type->aggregate.actuals ); 667 } 668 out->qualifiers |= type->qualifiers; 669 return out; 670 } 671 default: 672 return type; 673 } // switch 674 } 675 676 481 677 void buildForall( 482 678 const DeclarationNode * firstNode, … … 494 690 for ( auto i = outputList.begin() ; 495 691 i != outputList.end() ; 496 ++i, n = (DeclarationNode*)n->get_next()) {692 ++i, n = n->next ) { 497 693 // Only the object type class adds additional assertions. 498 694 if ( n->variable.tyClass != ast::TypeDecl::Otype ) { … … 639 835 for ( auto i = outputForall.begin() ; 640 836 i != outputForall.end() ; 641 ++i, n = (DeclarationNode*)n->get_next()) {837 ++i, n = n->next ) { 642 838 // Only the object type class adds additional assertions. 643 839 if ( n->variable.tyClass != ast::TypeDecl::Otype ) { … … 1272 1468 auto members = ret->members.begin(); 1273 1469 ret->hide = td->enumeration.hiding == EnumHiding::Hide ? ast::EnumDecl::EnumHiding::Hide : ast::EnumDecl::EnumHiding::Visible; 1274 for ( const DeclarationNode * cur = td->enumeration.constants; cur != nullptr; cur = dynamic_cast< DeclarationNode * >( cur->get_next() ), ++members ) {1470 for ( const DeclarationNode * cur = td->enumeration.constants; cur != nullptr; cur = cur->next, ++members ) { 1275 1471 if ( cur->enumInLine ) { 1276 1472 // Do Nothing … … 1500 1696 assert( ! function.params ); 1501 1697 // loop over declaration first as it is easier to spot errors 1502 for ( DeclarationNode * decl = function.oldDeclList; decl != nullptr; decl = d ynamic_cast< DeclarationNode * >( decl->get_next() )) {1698 for ( DeclarationNode * decl = function.oldDeclList; decl != nullptr; decl = decl->next ) { 1503 1699 // scan ALL parameter names for each declaration name to check for duplicates 1504 for ( DeclarationNode * param = function.idList; param != nullptr; param = dynamic_cast< DeclarationNode * >( param->get_next() )) {1700 for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) { 1505 1701 if ( *decl->name == *param->name ) { 1506 1702 // type set => parameter name already transformed by a declaration names so there is a duplicate … … 1524 1720 // rtb( a, b, c ) const char * b; {} => int rtn( int a, const char * b, int c ) {} 1525 1721 1526 for ( DeclarationNode * param = function.idList; param != nullptr; param = dynamic_cast< DeclarationNode * >( param->get_next() )) {1722 for ( DeclarationNode * param = function.idList; param != nullptr; param = param->next ) { 1527 1723 if ( ! param->type ) { // generate type int for empty parameter type 1528 1724 param->type = new TypeData( TypeData::Basic ); -
TabularUnified src/Parser/TypeData.h ¶
rbbf2cb1 rf6e8c67 111 111 112 112 const std::string * leafName() const; 113 114 TypeData * getLastBase(); 115 void setLastBase( TypeData * ); 113 116 }; 117 118 TypeData * addQualifiers( TypeData * ltype, TypeData * rtype ); 119 TypeData * addType( TypeData * ltype, TypeData * rtype, std::vector<ast::ptr<ast::Attribute>> & ); 120 TypeData * cloneBaseType( TypeData * type, TypeData * other ); 121 TypeData * makeNewBase( TypeData * type ); 114 122 115 123 ast::Type * typebuild( const TypeData * ); -
TabularUnified src/Parser/lex.ll ¶
rbbf2cb1 rf6e8c67 10 10 * Created On : Sat Sep 22 08:58:10 2001 11 11 * Last Modified By : Peter A. Buhr 12 * Last Modified On : Tue Oct 3 17:10:57 202313 * Update Count : 77 312 * Last Modified On : Sat Feb 24 11:47:24 2024 13 * Update Count : 777 14 14 */ 15 15 … … 407 407 ";" { ASCIIOP_RETURN(); } 408 408 "." { ASCIIOP_RETURN(); } // also operator 409 "@@" { NAMEDOP_RETURN(ATTR); } // CFA, attribute shorthand 409 410 "..." { NAMEDOP_RETURN(ELLIPSIS); } 410 411 -
TabularUnified src/Parser/parser.yy ¶
rbbf2cb1 rf6e8c67 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Feb 23 18:25:46202413 // Update Count : 6 48412 // Last Modified On : Mon Mar 4 08:44:25 2024 13 // Update Count : 6562 14 14 // 15 15 … … 126 126 DeclarationNode * cl = (new DeclarationNode)->addType( typeSpec ); // typeSpec IS DELETED!!! 127 127 128 // Start at second variable in declaration list and clone the type specifiers for each variable. .129 for ( DeclarationNode * cur = d ynamic_cast<DeclarationNode *>( declList->get_next() ); cur != nullptr; cur = dynamic_cast<DeclarationNode *>( cur->get_next() )) {128 // Start at second variable in declaration list and clone the type specifiers for each variable. 129 for ( DeclarationNode * cur = declList->next ; cur != nullptr; cur = cur->next ) { 130 130 cl->cloneBaseType( cur, copyattr ); // cur is modified 131 131 } // for … … 139 139 void distExt( DeclarationNode * declaration ) { 140 140 // distribute EXTENSION across all declarations 141 for ( DeclarationNode *iter = declaration ; iter != nullptr; iter = (DeclarationNode *)iter->get_next()) {141 for ( DeclarationNode *iter = declaration ; iter != nullptr ; iter = iter->next ) { 142 142 iter->set_extension( true ); 143 143 } // for … … 146 146 void distInl( DeclarationNode * declaration ) { 147 147 // distribute INLINE across all declarations 148 for ( DeclarationNode *iter = declaration ; iter != nullptr; iter = (DeclarationNode *)iter->get_next()) {148 for ( DeclarationNode *iter = declaration ; iter != nullptr ; iter = iter->next ) { 149 149 iter->set_inLine( true ); 150 150 } // for … … 153 153 void distQual( DeclarationNode * declaration, DeclarationNode * qualifiers ) { 154 154 // distribute qualifiers across all non-variable declarations in a distribution statemement 155 for ( DeclarationNode * iter = declaration ; iter != nullptr; iter = (DeclarationNode *)iter->get_next()) {155 for ( DeclarationNode * iter = declaration ; iter != nullptr ; iter = iter->next ) { 156 156 // SKULLDUGGERY: Distributions are parsed inside out, so qualifiers are added to declarations inside out. Since 157 157 // addQualifiers appends to the back of the list, the forall clauses are in the wrong order (right to left). To … … 389 389 %token LE GE EQ NE // <= >= == != 390 390 %token ANDAND OROR // && || 391 %token ELLIPSIS //...391 %token ATTR ELLIPSIS // @@ ... 392 392 393 393 %token EXPassign MULTassign DIVassign MODassign // \= *= /= %= … … 433 433 %type<stmt> statement labeled_statement compound_statement 434 434 %type<stmt> statement_decl statement_decl_list statement_list_nodecl 435 %type<stmt> selection_statement if_statement435 %type<stmt> selection_statement 436 436 %type<clause> switch_clause_list_opt switch_clause_list 437 437 %type<expr> case_value … … 500 500 %type<decl> cfa_identifier_parameter_declarator_tuple cfa_identifier_parameter_ptr 501 501 502 %type<decl> cfa_parameter_declaration cfa_parameter_list cfa_parameter_ ellipsis_list_opt502 %type<decl> cfa_parameter_declaration cfa_parameter_list cfa_parameter_list_ellipsis_opt 503 503 504 504 %type<decl> cfa_typedef_declaration cfa_variable_declaration cfa_variable_specifier … … 508 508 %type<decl> KR_parameter_list KR_parameter_list_opt 509 509 510 %type<decl> parameter_declaration parameter_list parameter_ type_list_opt510 %type<decl> parameter_declaration parameter_list parameter_list_ellipsis_opt 511 511 512 512 %type<decl> paren_identifier paren_type … … 530 530 %type<decl> type_parameter type_parameter_list type_initializer_opt 531 531 532 %type<expr> type_parameters_opt type_list array_type_list 532 %type<expr> type_parameters_opt type_list array_type_list // array_dimension_list 533 533 534 534 %type<decl> type_qualifier type_qualifier_name forall type_qualifier_list_opt type_qualifier_list … … 1246 1246 ; 1247 1247 1248 // if, switch, and choose require parenthesis around the conditional because it can be followed by a statement. 1249 // For example, without parenthesis: 1250 // 1251 // if x + y + z; => "if ( x + y ) + z" or "if ( x ) + y + z" 1252 // switch ( S ) { ... } => switch ( S ) { compound literal... } ... or 1253 1248 1254 selection_statement: 1249 // pop causes a S/R conflict without separating the IF statement into a non-terminal even after resolving 1250 // the inherent S/R conflict with THEN/ELSE. 1251 push if_statement pop 1252 { $$ = $2; } 1255 IF '(' conditional_declaration ')' statement %prec THEN 1256 // explicitly deal with the shift/reduce conflict on if/else 1257 { $$ = new StatementNode( build_if( yylloc, $3, maybe_build_compound( yylloc, $5 ), nullptr ) ); } 1258 | IF '(' conditional_declaration ')' statement ELSE statement 1259 { $$ = new StatementNode( build_if( yylloc, $3, maybe_build_compound( yylloc, $5 ), maybe_build_compound( yylloc, $7 ) ) ); } 1253 1260 | SWITCH '(' comma_expression ')' case_clause 1254 1261 { $$ = new StatementNode( build_switch( yylloc, true, $3, $5 ) ); } … … 1274 1281 | CHOOSE '(' comma_expression ')' '{' error '}' // CFA, invalid syntax rule 1275 1282 { SemanticError( yylloc, "syntax error, declarations can only appear before the list of case clauses." ); $$ = nullptr; } 1276 ;1277 1278 if_statement:1279 IF '(' conditional_declaration ')' statement %prec THEN1280 // explicitly deal with the shift/reduce conflict on if/else1281 { $$ = new StatementNode( build_if( yylloc, $3, maybe_build_compound( yylloc, $5 ), nullptr ) ); }1282 | IF '(' conditional_declaration ')' statement ELSE statement1283 { $$ = new StatementNode( build_if( yylloc, $3, maybe_build_compound( yylloc, $5 ), maybe_build_compound( yylloc, $7 ) ) ); }1284 1283 ; 1285 1284 … … 1897 1896 declaration_list: 1898 1897 declaration 1899 | declaration_list declaration { $$ = $1->appendList( $2 ); } 1898 | declaration_list declaration 1899 { $$ = $1->set_last( $2 ); } 1900 1900 ; 1901 1901 … … 1910 1910 { $$ = $1; } 1911 1911 | KR_parameter_list c_declaration ';' 1912 { $$ = $1-> appendList( $2 ); }1912 { $$ = $1->set_last( $2 ); } 1913 1913 ; 1914 1914 … … 1968 1968 { $$ = $2->addQualifiers( $1 )->addInitializer( $3 ); } 1969 1969 | cfa_variable_declaration pop ',' push identifier_or_type_name initializer_opt 1970 { $$ = $1-> appendList( $1->cloneType( $5 )->addInitializer( $6 ) ); }1970 { $$ = $1->set_last( $1->cloneType( $5 )->addInitializer( $6 ) ); } 1971 1971 ; 1972 1972 … … 1990 1990 | declaration_qualifier_list type_qualifier_list cfa_function_specifier 1991 1991 { $$ = $3->addQualifiers( $1 )->addQualifiers( $2 ); } 1992 | cfa_function_declaration ',' identifier_or_type_name '(' push cfa_parameter_ ellipsis_list_opt pop ')'1992 | cfa_function_declaration ',' identifier_or_type_name '(' push cfa_parameter_list_ellipsis_opt pop ')' 1993 1993 { 1994 1994 // Append the return type at the start (left-hand-side) to each identifier in the list. 1995 1995 DeclarationNode * ret = new DeclarationNode; 1996 1996 ret->type = maybeCopy( $1->type->base ); 1997 $$ = $1-> appendList( DeclarationNode::newFunction( $3, ret, $6, nullptr ) );1997 $$ = $1->set_last( DeclarationNode::newFunction( $3, ret, $6, nullptr ) ); 1998 1998 } 1999 1999 ; 2000 2000 2001 2001 cfa_function_specifier: // CFA 2002 // '[' ']' identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' // S/R conflict 2003 // { 2004 // $$ = DeclarationNode::newFunction( $3, DeclarationNode::newTuple( 0 ), $6, nullptr, true ); 2005 // } 2006 // '[' ']' identifier '(' push cfa_parameter_ellipsis_list_opt pop ')' 2007 // { 2008 // typedefTable.setNextIdentifier( *$5 ); 2009 // $$ = DeclarationNode::newFunction( $5, DeclarationNode::newTuple( 0 ), $8, nullptr, true ); 2010 // } 2011 // | '[' ']' TYPEDEFname '(' push cfa_parameter_ellipsis_list_opt pop ')' 2012 // { 2013 // typedefTable.setNextIdentifier( *$5 ); 2014 // $$ = DeclarationNode::newFunction( $5, DeclarationNode::newTuple( 0 ), $8, nullptr, true ); 2015 // } 2016 // | '[' ']' typegen_name 2002 '[' ']' identifier '(' push cfa_parameter_list_ellipsis_opt pop ')' attribute_list_opt 2003 { $$ = DeclarationNode::newFunction( $3, DeclarationNode::newTuple( nullptr ), $6, nullptr )->addQualifiers( $9 ); } 2004 | '[' ']' TYPEDEFname '(' push cfa_parameter_list_ellipsis_opt pop ')' attribute_list_opt 2005 { $$ = DeclarationNode::newFunction( $3, DeclarationNode::newTuple( nullptr ), $6, nullptr )->addQualifiers( $9 ); } 2006 // | '[' ']' TYPEGENname '(' push cfa_parameter_list_ellipsis_opt pop ')' attribute_list_opt 2007 // { $$ = DeclarationNode::newFunction( $3, DeclarationNode::newTuple( nullptr ), $6, nullptr )->addQualifiers( $9 ); } 2008 2017 2009 // identifier_or_type_name must be broken apart because of the sequence: 2018 2010 // 2019 // '[' ']' identifier_or_type_name '(' cfa_parameter_ ellipsis_list_opt ')'2011 // '[' ']' identifier_or_type_name '(' cfa_parameter_list_ellipsis_opt ')' 2020 2012 // '[' ']' type_specifier 2021 2013 // 2022 2014 // type_specifier can resolve to just TYPEDEFname (e.g., typedef int T; int f( T );). Therefore this must be 2023 2015 // flattened to allow lookahead to the '(' without having to reduce identifier_or_type_name. 2024 cfa_abstract_tuple identifier_or_type_name '(' push cfa_parameter_ellipsis_list_opt pop ')' attribute_list_opt2016 | cfa_abstract_tuple identifier_or_type_name '(' push cfa_parameter_list_ellipsis_opt pop ')' attribute_list_opt 2025 2017 // To obtain LR(1 ), this rule must be factored out from function return type (see cfa_abstract_declarator). 2026 2018 { $$ = DeclarationNode::newFunction( $2, $1, $5, nullptr )->addQualifiers( $8 ); } 2027 | cfa_function_return identifier_or_type_name '(' push cfa_parameter_ ellipsis_list_opt pop ')' attribute_list_opt2019 | cfa_function_return identifier_or_type_name '(' push cfa_parameter_list_ellipsis_opt pop ')' attribute_list_opt 2028 2020 { $$ = DeclarationNode::newFunction( $2, $1, $5, nullptr )->addQualifiers( $8 ); } 2029 2021 ; … … 2032 2024 '[' push cfa_parameter_list pop ']' 2033 2025 { $$ = DeclarationNode::newTuple( $3 ); } 2034 | '[' push cfa_parameter_list pop ',' pushcfa_abstract_parameter_list pop ']'2026 | '[' push cfa_parameter_list ',' cfa_abstract_parameter_list pop ']' 2035 2027 // To obtain LR(1 ), the last cfa_abstract_parameter_list is added into this flattened rule to lookahead to the ']'. 2036 { $$ = DeclarationNode::newTuple( $3-> appendList( $7) ); }2028 { $$ = DeclarationNode::newTuple( $3->set_last( $5 ) ); } 2037 2029 ; 2038 2030 … … 2048 2040 $$ = $2->addTypedef(); 2049 2041 } 2050 | cfa_typedef_declaration pop ',' pushidentifier2051 { 2052 typedefTable.addToEnclosingScope( *$ 5, TYPEDEFname, "cfa_typedef_declaration 3" );2053 $$ = $1-> appendList( $1->cloneType( $5) );2042 | cfa_typedef_declaration ',' identifier 2043 { 2044 typedefTable.addToEnclosingScope( *$3, TYPEDEFname, "cfa_typedef_declaration 3" ); 2045 $$ = $1->set_last( $1->cloneType( $3 ) ); 2054 2046 } 2055 2047 ; … … 2069 2061 { 2070 2062 typedefTable.addToEnclosingScope( *$3->name, TYPEDEFname, "typedef_declaration 2" ); 2071 $$ = $1-> appendList( $1->cloneBaseType( $3 )->addTypedef() );2063 $$ = $1->set_last( $1->cloneBaseType( $3 )->addTypedef() ); 2072 2064 } 2073 2065 | type_qualifier_list TYPEDEF type_specifier declarator // remaining OBSOLESCENT (see 2 ) … … 2123 2115 2124 2116 | declaring_list ',' attribute_list_opt declarator asm_name_opt initializer_opt 2125 { $$ = $1-> appendList( $4->addQualifiers( $3 )->addAsmName( $5 )->addInitializer( $6 ) ); }2117 { $$ = $1->set_last( $4->addQualifiers( $3 )->addAsmName( $5 )->addInitializer( $6 ) ); } 2126 2118 ; 2127 2119 … … 2587 2579 { $$ = nullptr; } 2588 2580 | field_declaration_list_opt field_declaration 2589 { $$ = $1 ? $1-> appendList( $2 ) : $2; }2581 { $$ = $1 ? $1->set_last( $2 ) : $2; } 2590 2582 ; 2591 2583 … … 2635 2627 | field_declarator 2636 2628 | field_declaring_list_opt ',' attribute_list_opt field_declarator 2637 { $$ = $1-> appendList( $4->addQualifiers( $3 ) ); }2629 { $$ = $1->set_last( $4->addQualifiers( $3 ) ); } 2638 2630 ; 2639 2631 … … 2657 2649 | field_abstract 2658 2650 | field_abstract_list_opt ',' attribute_list_opt field_abstract 2659 { $$ = $1-> appendList( $4->addQualifiers( $3 ) ); }2651 { $$ = $1->set_last( $4->addQualifiers( $3 ) ); } 2660 2652 ; 2661 2653 … … 2670 2662 { $$ = $1->addName( $2 ); } 2671 2663 | cfa_field_declaring_list ',' identifier_or_type_name 2672 { $$ = $1-> appendList( $1->cloneType( $3 ) ); }2664 { $$ = $1->set_last( $1->cloneType( $3 ) ); } 2673 2665 ; 2674 2666 … … 2677 2669 cfa_abstract_declarator_tuple 2678 2670 | cfa_field_abstract_list ',' 2679 { $$ = $1-> appendList( $1->cloneType( 0 ) ); }2671 { $$ = $1->set_last( $1->cloneType( 0 ) ); } 2680 2672 ; 2681 2673 … … 2690 2682 { $$ = $2; } 2691 2683 ; 2684 2685 // *********** 2686 // Enumeration 2687 // *********** 2692 2688 2693 2689 enum_type: … … 2719 2715 | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt identifier attribute_list_opt 2720 2716 { 2721 if ( $3 && ($3->storageClasses.any() || $3->type->qualifiers.val != 0 )) {2717 if ( $3 && ($3->storageClasses.any() || $3->type->qualifiers.val != 0) ) { 2722 2718 SemanticError( yylloc, "syntax error, storage-class and CV qualifiers are not meaningful for enumeration constants, which are const." ); 2723 2719 } … … 2769 2765 { $$ = DeclarationNode::newEnumInLine( *$2->type->symbolic.name ); } 2770 2766 | enumerator_list ',' visible_hide_opt identifier_or_type_name enumerator_value_opt 2771 { $$ = $1-> appendList( DeclarationNode::newEnumValueGeneric( $4, $5 ) ); }2767 { $$ = $1->set_last( DeclarationNode::newEnumValueGeneric( $4, $5 ) ); } 2772 2768 | enumerator_list ',' INLINE type_name enumerator_value_opt 2773 { $$ = $1-> appendList( DeclarationNode::newEnumValueGeneric( new string("inline"), nullptr ) ); }2769 { $$ = $1->set_last( DeclarationNode::newEnumValueGeneric( new string("inline"), nullptr ) ); } 2774 2770 ; 2775 2771 … … 2789 2785 ; 2790 2786 2791 cfa_parameter_ellipsis_list_opt: // CFA, abstract + real 2792 // empty 2793 { $$ = DeclarationNode::newBasicType( DeclarationNode::Void ); } 2794 | ELLIPSIS 2795 { $$ = nullptr; } 2796 | cfa_abstract_parameter_list 2797 | cfa_parameter_list 2798 | cfa_parameter_list pop ',' push cfa_abstract_parameter_list 2799 { $$ = $1->appendList( $5 ); } 2800 | cfa_abstract_parameter_list pop ',' push ELLIPSIS 2801 { $$ = $1->addVarArgs(); } 2802 | cfa_parameter_list pop ',' push ELLIPSIS 2803 { $$ = $1->addVarArgs(); } 2804 ; 2805 2806 cfa_parameter_list: // CFA 2807 // To obtain LR(1) between cfa_parameter_list and cfa_abstract_tuple, the last cfa_abstract_parameter_list is 2808 // factored out from cfa_parameter_list, flattening the rules to get lookahead to the ']'. 2809 cfa_parameter_declaration 2810 | cfa_abstract_parameter_list pop ',' push cfa_parameter_declaration 2811 { $$ = $1->appendList( $5 ); } 2812 | cfa_parameter_list pop ',' push cfa_parameter_declaration 2813 { $$ = $1->appendList( $5 ); } 2814 | cfa_parameter_list pop ',' push cfa_abstract_parameter_list pop ',' push cfa_parameter_declaration 2815 { $$ = $1->appendList( $5 )->appendList( $9 ); } 2816 ; 2817 2818 cfa_abstract_parameter_list: // CFA, new & old style abstract 2819 cfa_abstract_parameter_declaration 2820 | cfa_abstract_parameter_list pop ',' push cfa_abstract_parameter_declaration 2821 { $$ = $1->appendList( $5 ); } 2822 ; 2823 2824 parameter_type_list_opt: 2787 // ******************* 2788 // Function parameters 2789 // ******************* 2790 2791 parameter_list_ellipsis_opt: 2825 2792 // empty 2826 2793 { $$ = nullptr; } … … 2833 2800 2834 2801 parameter_list: // abstract + real 2835 abstract_parameter_declaration 2836 | parameter_declaration 2802 parameter_declaration 2803 | abstract_parameter_declaration 2804 | parameter_list ',' parameter_declaration 2805 { $$ = $1->set_last( $3 ); } 2837 2806 | parameter_list ',' abstract_parameter_declaration 2838 { $$ = $1->appendList( $3 ); } 2839 | parameter_list ',' parameter_declaration 2840 { $$ = $1->appendList( $3 ); } 2807 { $$ = $1->set_last( $3 ); } 2808 ; 2809 2810 cfa_parameter_list_ellipsis_opt: // CFA, abstract + real 2811 // empty 2812 { $$ = DeclarationNode::newBasicType( DeclarationNode::Void ); } 2813 | ELLIPSIS 2814 { $$ = nullptr; } 2815 | cfa_parameter_list 2816 | cfa_abstract_parameter_list 2817 | cfa_parameter_list ',' cfa_abstract_parameter_list 2818 { $$ = $1->set_last( $3 ); } 2819 | cfa_parameter_list ',' ELLIPSIS 2820 { $$ = $1->addVarArgs(); } 2821 | cfa_abstract_parameter_list ',' ELLIPSIS 2822 { $$ = $1->addVarArgs(); } 2823 ; 2824 2825 cfa_parameter_list: // CFA 2826 // To obtain LR(1) between cfa_parameter_list and cfa_abstract_tuple, the last cfa_abstract_parameter_list is 2827 // factored out from cfa_parameter_list, flattening the rules to get lookahead to the ']'. 2828 cfa_parameter_declaration 2829 | cfa_abstract_parameter_list ',' cfa_parameter_declaration 2830 { $$ = $1->set_last( $3 ); } 2831 | cfa_parameter_list ',' cfa_parameter_declaration 2832 { $$ = $1->set_last( $3 ); } 2833 | cfa_parameter_list ',' cfa_abstract_parameter_list ',' cfa_parameter_declaration 2834 { $$ = $1->set_last( $3 )->set_last( $5 ); } 2835 ; 2836 2837 cfa_abstract_parameter_list: // CFA, new & old style abstract 2838 cfa_abstract_parameter_declaration 2839 | cfa_abstract_parameter_list ',' cfa_abstract_parameter_declaration 2840 { $$ = $1->set_last( $3 ); } 2841 2841 ; 2842 2842 2843 2843 // Provides optional identifier names (abstract_declarator/variable_declarator), no initialization, different semantics 2844 2844 // for typedef name by using type_parameter_redeclarator instead of typedef_redeclarator, and function prototypes. 2845 2846 parameter_declaration: 2847 // No SUE declaration in parameter list. 2848 declaration_specifier_nobody identifier_parameter_declarator default_initializer_opt 2849 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); } 2850 | declaration_specifier_nobody type_parameter_redeclarator default_initializer_opt 2851 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); } 2852 ; 2853 2854 abstract_parameter_declaration: 2855 declaration_specifier_nobody default_initializer_opt 2856 { $$ = $1->addInitializer( $2 ? new InitializerNode( $2 ) : nullptr ); } 2857 | declaration_specifier_nobody abstract_parameter_declarator default_initializer_opt 2858 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); } 2859 ; 2845 2860 2846 2861 cfa_parameter_declaration: // CFA, new & old style parameter declaration … … 2866 2881 ; 2867 2882 2868 parameter_declaration:2869 // No SUE declaration in parameter list.2870 declaration_specifier_nobody identifier_parameter_declarator default_initializer_opt2871 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); }2872 | declaration_specifier_nobody type_parameter_redeclarator default_initializer_opt2873 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); }2874 ;2875 2876 abstract_parameter_declaration:2877 declaration_specifier_nobody default_initializer_opt2878 { $$ = $1->addInitializer( $2 ? new InitializerNode( $2 ) : nullptr ); }2879 | declaration_specifier_nobody abstract_parameter_declarator default_initializer_opt2880 { $$ = $2->addType( $1 )->addInitializer( $3 ? new InitializerNode( $3 ) : nullptr ); }2881 ;2882 2883 2883 // ISO/IEC 9899:1999 Section 6.9.1(6) : "An identifier declared as a typedef name shall not be redeclared as a 2884 2884 // parameter." Because the scope of the K&R-style parameter-list sees the typedef first, the following is based only on … … 2889 2889 { $$ = DeclarationNode::newName( $1 ); } 2890 2890 | identifier_list ',' identifier 2891 { $$ = $1-> appendList( DeclarationNode::newName( $3 ) ); }2891 { $$ = $1->set_last( DeclarationNode::newName( $3 ) ); } 2892 2892 ; 2893 2893 … … 2990 2990 type_parameter 2991 2991 | type_parameter_list ',' type_parameter 2992 { $$ = $1-> appendList( $3 ); }2992 { $$ = $1->set_last( $3 ); } 2993 2993 ; 2994 2994 … … 3063 3063 assertion 3064 3064 | assertion_list assertion 3065 { $$ = $1-> appendList( $2 ); }3065 { $$ = $1->set_last( $2 ); } 3066 3066 ; 3067 3067 … … 3091 3091 { $$ = $3->addQualifiers( $1 ); } 3092 3092 | type_declaring_list ',' type_declarator 3093 { $$ = $1-> appendList( $3->copySpecifiers( $1 ) ); }3093 { $$ = $1->set_last( $3->copySpecifiers( $1 ) ); } 3094 3094 ; 3095 3095 … … 3134 3134 trait_declaration 3135 3135 | trait_declaration_list pop push trait_declaration 3136 { $$ = $1-> appendList( $4 ); }3136 { $$ = $1->set_last( $4 ); } 3137 3137 ; 3138 3138 … … 3146 3146 | cfa_function_specifier 3147 3147 | cfa_trait_declaring_list pop ',' push identifier_or_type_name 3148 { $$ = $1-> appendList( $1->cloneType( $5 ) ); }3148 { $$ = $1->set_last( $1->cloneType( $5 ) ); } 3149 3149 ; 3150 3150 … … 3153 3153 { $$ = $2->addType( $1 ); } 3154 3154 | trait_declaring_list pop ',' push declarator 3155 { $$ = $1-> appendList( $1->cloneBaseType( $5 ) ); }3155 { $$ = $1->set_last( $1->cloneBaseType( $5 ) ); } 3156 3156 ; 3157 3157 … … 3161 3161 // empty, input file 3162 3162 | external_definition_list 3163 { parseTree = parseTree ? parseTree-> appendList( $1 ) : $1; }3163 { parseTree = parseTree ? parseTree->set_last( $1 ) : $1; } 3164 3164 ; 3165 3165 … … 3168 3168 { $$ = $2; } 3169 3169 | external_definition_list push external_definition pop 3170 { $$ = $1 ? $1-> appendList( $3 ) : $3; }3170 { $$ = $1 ? $1->set_last( $3 ) : $3; } 3171 3171 ; 3172 3172 … … 3395 3395 ATTRIBUTE '(' '(' attribute_name_list ')' ')' 3396 3396 { $$ = $4; } 3397 | ATTRIBUTE '(' attribute_name_list ')' // CFA 3398 { $$ = $3; } 3399 | ATTR '(' attribute_name_list ')' // CFA 3400 { $$ = $3; } 3397 3401 ; 3398 3402 … … 3499 3503 3500 3504 variable_function: 3501 '(' variable_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3505 '(' variable_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3502 3506 { $$ = $2->addParamList( $5 ); } 3503 | '(' attribute_list variable_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3507 | '(' attribute_list variable_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3504 3508 { $$ = $3->addQualifiers( $2 )->addParamList( $6 ); } 3505 3509 | '(' variable_function ')' // redundant parenthesis … … 3522 3526 3523 3527 function_no_ptr: 3524 paren_identifier '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3528 paren_identifier '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3525 3529 { $$ = $1->addParamList( $3 ); } 3526 | '(' function_ptr ')' '(' parameter_ type_list_opt ')'3530 | '(' function_ptr ')' '(' parameter_list_ellipsis_opt ')' 3527 3531 { $$ = $2->addParamList( $5 ); } 3528 | '(' attribute_list function_ptr ')' '(' parameter_ type_list_opt ')'3532 | '(' attribute_list function_ptr ')' '(' parameter_list_ellipsis_opt ')' 3529 3533 { $$ = $3->addQualifiers( $2 )->addParamList( $6 ); } 3530 3534 | '(' function_no_ptr ')' // redundant parenthesis … … 3576 3580 paren_identifier '(' identifier_list ')' // function_declarator handles empty parameter 3577 3581 { $$ = $1->addIdList( $3 ); } 3578 | '(' KR_function_ptr ')' '(' parameter_ type_list_opt ')'3582 | '(' KR_function_ptr ')' '(' parameter_list_ellipsis_opt ')' 3579 3583 { $$ = $2->addParamList( $5 ); } 3580 | '(' attribute_list KR_function_ptr ')' '(' parameter_ type_list_opt ')'3584 | '(' attribute_list KR_function_ptr ')' '(' parameter_list_ellipsis_opt ')' 3581 3585 { $$ = $3->addQualifiers( $2 )->addParamList( $6 ); } 3582 3586 | '(' KR_function_no_ptr ')' // redundant parenthesis … … 3668 3672 3669 3673 variable_type_function: 3670 '(' variable_type_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3674 '(' variable_type_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3671 3675 { $$ = $2->addParamList( $5 ); } 3672 | '(' attribute_list variable_type_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3676 | '(' attribute_list variable_type_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3673 3677 { $$ = $3->addQualifiers( $2 )->addParamList( $6 ); } 3674 3678 | '(' variable_type_function ')' // redundant parenthesis … … 3691 3695 3692 3696 function_type_no_ptr: 3693 paren_type '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3697 paren_type '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3694 3698 { $$ = $1->addParamList( $3 ); } 3695 | '(' function_type_ptr ')' '(' parameter_ type_list_opt ')'3699 | '(' function_type_ptr ')' '(' parameter_list_ellipsis_opt ')' 3696 3700 { $$ = $2->addParamList( $5 ); } 3697 | '(' attribute_list function_type_ptr ')' '(' parameter_ type_list_opt ')'3701 | '(' attribute_list function_type_ptr ')' '(' parameter_list_ellipsis_opt ')' 3698 3702 { $$ = $3->addQualifiers( $2 )->addParamList( $6 ); } 3699 3703 | '(' function_type_no_ptr ')' // redundant parenthesis … … 3738 3742 { $$ = $1->addQualifiers( $2 ); } 3739 3743 | '&' MUTEX paren_identifier attribute_list_opt 3740 { $$ = $3->addPointer( DeclarationNode::newPointer( DeclarationNode::newTypeQualifier( ast::CV::Mutex ), OperKinds::AddressOf ) )->addQualifiers( $4 ); } 3744 { $$ = $3->addPointer( DeclarationNode::newPointer( DeclarationNode::newTypeQualifier( ast::CV::Mutex ), 3745 OperKinds::AddressOf ) )->addQualifiers( $4 ); } 3741 3746 | identifier_parameter_ptr 3742 3747 | identifier_parameter_array attribute_list_opt … … 3767 3772 3768 3773 identifier_parameter_function: 3769 paren_identifier '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3774 paren_identifier '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3770 3775 { $$ = $1->addParamList( $3 ); } 3771 | '(' identifier_parameter_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3776 | '(' identifier_parameter_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3772 3777 { $$ = $2->addParamList( $5 ); } 3773 3778 | '(' identifier_parameter_function ')' // redundant parenthesis … … 3788 3793 { $$ = $1->addQualifiers( $2 ); } 3789 3794 | '&' MUTEX typedef_name attribute_list_opt 3790 { $$ = $3->addPointer( DeclarationNode::newPointer( DeclarationNode::newTypeQualifier( ast::CV::Mutex ), OperKinds::AddressOf ) )->addQualifiers( $4 ); } 3795 { $$ = $3->addPointer( DeclarationNode::newPointer( DeclarationNode::newTypeQualifier( ast::CV::Mutex ), 3796 OperKinds::AddressOf ) )->addQualifiers( $4 ); } 3791 3797 | type_parameter_ptr 3792 3798 | type_parameter_array attribute_list_opt … … 3820 3826 3821 3827 type_parameter_function: 3822 typedef_name '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3828 typedef_name '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3823 3829 { $$ = $1->addParamList( $3 ); } 3824 | '(' type_parameter_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3830 | '(' type_parameter_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3825 3831 { $$ = $2->addParamList( $5 ); } 3826 3832 ; … … 3870 3876 3871 3877 abstract_function: 3872 '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3878 '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3873 3879 { $$ = DeclarationNode::newFunction( nullptr, nullptr, $2, nullptr ); } 3874 | '(' abstract_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)3880 | '(' abstract_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3875 3881 { $$ = $2->addParamList( $5 ); } 3876 3882 | '(' abstract_function ')' // redundant parenthesis … … 3888 3894 { $$ = DeclarationNode::newArray( $3, nullptr, false )->addArray( DeclarationNode::newArray( $6, nullptr, false ) ); } 3889 3895 // { SemanticError( yylloc, "New array dimension is currently unimplemented." ); $$ = nullptr; } 3896 3897 // If needed, the following parses and does not use comma_expression, so the array structure can be built. 3898 // | '[' push assignment_expression pop ',' push array_dimension_list pop ']' // CFA 3899 3890 3900 | '[' push array_type_list pop ']' // CFA 3891 3901 { $$ = DeclarationNode::newArray( $3, nullptr, false ); } 3892 3902 | multi_array_dimension 3893 3903 ; 3904 3905 // array_dimension_list: 3906 // assignment_expression 3907 // | array_dimension_list ',' assignment_expression 3908 // ; 3894 3909 3895 3910 array_type_list: … … 3993 4008 3994 4009 abstract_parameter_function: 3995 '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)4010 '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3996 4011 { $$ = DeclarationNode::newFunction( nullptr, nullptr, $2, nullptr ); } 3997 | '(' abstract_parameter_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)4012 | '(' abstract_parameter_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 3998 4013 { $$ = $2->addParamList( $5 ); } 3999 4014 | '(' abstract_parameter_function ')' // redundant parenthesis … … 4072 4087 4073 4088 variable_abstract_function: 4074 '(' variable_abstract_ptr ')' '(' parameter_ type_list_opt ')' // empty parameter list OBSOLESCENT (see 3)4089 '(' variable_abstract_ptr ')' '(' parameter_list_ellipsis_opt ')' // empty parameter list OBSOLESCENT (see 3) 4075 4090 { $$ = $2->addParamList( $5 ); } 4076 4091 | '(' variable_abstract_function ')' // redundant parenthesis … … 4158 4173 // 4159 4174 // cfa_abstract_tuple identifier_or_type_name 4160 // '[' cfa_parameter_list ']' identifier_or_type_name '(' cfa_parameter_ ellipsis_list_opt ')'4175 // '[' cfa_parameter_list ']' identifier_or_type_name '(' cfa_parameter_list_ellipsis_opt ')' 4161 4176 // 4162 4177 // since a function return type can be syntactically identical to a tuple type: … … 4224 4239 4225 4240 cfa_abstract_function: // CFA 4226 // '[' ']' '(' cfa_parameter_ellipsis_list_opt ')'4227 //{ $$ = DeclarationNode::newFunction( nullptr, DeclarationNode::newTuple( nullptr ), $4, nullptr ); }4228 cfa_abstract_tuple '(' push cfa_parameter_ellipsis_list_opt pop ')'4241 '[' ']' '(' cfa_parameter_list_ellipsis_opt ')' 4242 { $$ = DeclarationNode::newFunction( nullptr, DeclarationNode::newTuple( nullptr ), $4, nullptr ); } 4243 | cfa_abstract_tuple '(' push cfa_parameter_list_ellipsis_opt pop ')' 4229 4244 { $$ = DeclarationNode::newFunction( nullptr, $1, $4, nullptr ); } 4230 | cfa_function_return '(' push cfa_parameter_ ellipsis_list_opt pop ')'4245 | cfa_function_return '(' push cfa_parameter_list_ellipsis_opt pop ')' 4231 4246 { $$ = DeclarationNode::newFunction( nullptr, $1, $4, nullptr ); } 4232 4247 ; -
TabularUnified src/ResolvExpr/Resolver.cc ¶
rbbf2cb1 rf6e8c67 412 412 413 413 void resolveWithExprs(std::vector<ast::ptr<ast::Expr>> & exprs, std::list<ast::ptr<ast::Stmt>> & stmtsToAdd); 414 bool shouldGenCtorInit( const ast::ObjectDecl * ) const; 414 415 415 416 void beginScope() { managedTypes.beginScope(); } … … 581 582 } 582 583 584 bool Resolver::shouldGenCtorInit( ast::ObjectDecl const * decl ) const { 585 // If we shouldn't try to construct it, then don't. 586 if ( !InitTweak::tryConstruct( decl ) ) return false; 587 // Otherwise, if it is a managed type, we may construct it. 588 if ( managedTypes.isManaged( decl ) ) return true; 589 // Skip construction if it is trivial at compile-time. 590 if ( InitTweak::isConstExpr( decl->init ) ) return false; 591 // Skip construction for local declarations. 592 return ( !isInFunction() || decl->storage.is_static ); 593 } 594 583 595 const ast::ObjectDecl * Resolver::previsit( const ast::ObjectDecl * objectDecl ) { 584 596 // To handle initialization of routine pointers [e.g. int (*fp)(int) = foo()], … … 615 627 // this object in visitor pass, thus disabling CtorInit codegen. 616 628 // 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 ) )) ) {629 if ( shouldGenCtorInit( mutDecl ) ) { 618 630 // constructed objects cannot be designated 619 631 if ( InitTweak::isDesignated( mutDecl->init ) ) {
Note: See TracChangeset
for help on using the changeset viewer.