1# Copyright 2019 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Intermediate representation (IR) for Emboss. 16 17This is limited to purely data and type annotations. 18""" 19 20import dataclasses 21import enum 22import sys 23from typing import ClassVar, Optional 24 25from compiler.util import ir_data_fields 26 27 28@dataclasses.dataclass 29class Message: 30 """Base class for IR data objects. 31 32 Historically protocol buffers were used for serializing this data which has 33 led to some legacy naming conventions and references. In particular this 34 class is named `Message` in the sense of a protocol buffer message, 35 indicating that it is intended to just be data that is used by other higher 36 level services. 37 38 There are some other legacy idioms leftover from the protocol buffer-based 39 definition such as support for "oneof" and optional fields. 40 """ 41 42 IR_DATACLASS: ClassVar[object] = object() 43 field_specs: ClassVar[ir_data_fields.FilteredIrFieldSpecs] 44 45 def __post_init__(self): 46 """Called by dataclass subclasses after init. 47 48 Post-processes any lists passed in to use our custom list type. 49 """ 50 # Convert any lists passed in to CopyValuesList 51 for spec in self.field_specs.sequence_field_specs: 52 cur_val = getattr(self, spec.name) 53 if isinstance(cur_val, ir_data_fields.TemporaryCopyValuesList): 54 copy_val = cur_val.temp_list 55 else: 56 copy_val = ir_data_fields.CopyValuesList(spec.data_type) 57 if cur_val: 58 copy_val.shallow_copy(cur_val) 59 setattr(self, spec.name, copy_val) 60 61 # This hook adds a 15% overhead to end-to-end code generation in some cases 62 # so we guard it in a `__debug__` block. Users can opt-out of this check by 63 # running python with the `-O` flag, ie: `python3 -O ./embossc`. 64 if __debug__: 65 def __setattr__(self, name: str, value) -> None: 66 """Debug-only hook that adds basic type checking for ir_data fields.""" 67 if spec := self.field_specs.all_field_specs.get(name): 68 if not ( 69 # Check if it's the expected type 70 isinstance(value, spec.data_type) or 71 # Oneof fields are a special case 72 spec.is_oneof or 73 # Optional fields can be set to None 74 (spec.container is ir_data_fields.FieldContainer.OPTIONAL and 75 value is None) or 76 # Sequences can be a few variants of lists 77 (spec.is_sequence and 78 isinstance(value, ( 79 list, ir_data_fields.TemporaryCopyValuesList, 80 ir_data_fields.CopyValuesList))) or 81 # An enum value can be an int 82 (spec.is_enum and isinstance(value, int))): 83 raise AttributeError( 84 f"Cannot set {value} (type {value.__class__}) for type" 85 "{spec.data_type}") 86 object.__setattr__(self, name, value) 87 88 # Non-PEP8 name to mimic the Google Protobuf interface. 89 def HasField(self, name): # pylint:disable=invalid-name 90 """Indicates if this class has the given field defined and it is set.""" 91 return getattr(self, name, None) is not None 92 93 # Non-PEP8 name to mimic the Google Protobuf interface. 94 def WhichOneof(self, oneof_name): # pylint:disable=invalid-name 95 """Indicates which field has been set for the oneof value. 96 97 Returns None if no field has been set. 98 """ 99 for field_name, oneof in self.field_specs.oneof_mappings: 100 if oneof == oneof_name and self.HasField(field_name): 101 return field_name 102 return None 103 104 105################################################################################ 106# From here to the end of the file are actual structure definitions. 107 108 109@dataclasses.dataclass 110class Position(Message): 111 """A zero-width position within a source file.""" 112 113 line: int = 0 114 """Line (starts from 1).""" 115 column: int = 0 116 """Column (starts from 1).""" 117 118 119@dataclasses.dataclass 120class Location(Message): 121 """A half-open start:end range within a source file.""" 122 123 start: Optional[Position] = None 124 """Beginning of the range""" 125 end: Optional[Position] = None 126 """One column past the end of the range.""" 127 128 is_disjoint_from_parent: Optional[bool] = None 129 """True if this Location is outside of the parent object's Location.""" 130 131 is_synthetic: Optional[bool] = None 132 """True if this Location's parent was synthesized, and does not directly 133 appear in the source file. 134 135 The Emboss front end uses this field to cull 136 irrelevant error messages. 137 """ 138 139 140@dataclasses.dataclass 141class Word(Message): 142 """IR for a bare word in the source file. 143 144 This is used in NameDefinitions and References. 145 """ 146 147 text: Optional[str] = None 148 source_location: Optional[Location] = None 149 150 151@dataclasses.dataclass 152class String(Message): 153 """IR for a string in the source file.""" 154 155 text: Optional[str] = None 156 source_location: Optional[Location] = None 157 158 159@dataclasses.dataclass 160class Documentation(Message): 161 text: Optional[str] = None 162 source_location: Optional[Location] = None 163 164 165@dataclasses.dataclass 166class BooleanConstant(Message): 167 """IR for a boolean constant.""" 168 169 value: Optional[bool] = None 170 source_location: Optional[Location] = None 171 172 173@dataclasses.dataclass 174class Empty(Message): 175 """Placeholder message for automatic element counts for arrays.""" 176 177 source_location: Optional[Location] = None 178 179 180@dataclasses.dataclass 181class NumericConstant(Message): 182 """IR for any numeric constant.""" 183 184 # Numeric constants are stored as decimal strings; this is the simplest way 185 # to store the full -2**63..+2**64 range. 186 # 187 # TODO(bolms): switch back to int, and just use strings during 188 # serialization, now that we're free of proto. 189 value: Optional[str] = None 190 source_location: Optional[Location] = None 191 192 193class FunctionMapping(int, enum.Enum): 194 """Enum of supported function types""" 195 196 UNKNOWN = 0 197 ADDITION = 1 198 """`+`""" 199 SUBTRACTION = 2 200 """`-`""" 201 MULTIPLICATION = 3 202 """`*`""" 203 EQUALITY = 4 204 """`==`""" 205 INEQUALITY = 5 206 """`!=`""" 207 AND = 6 208 """`&&`""" 209 OR = 7 210 """`||`""" 211 LESS = 8 212 """`<`""" 213 LESS_OR_EQUAL = 9 214 """`<=`""" 215 GREATER = 10 216 """`>`""" 217 GREATER_OR_EQUAL = 11 218 """`>=`""" 219 CHOICE = 12 220 """`?:`""" 221 MAXIMUM = 13 222 """`$max()`""" 223 PRESENCE = 14 224 """`$present()`""" 225 UPPER_BOUND = 15 226 """`$upper_bound()`""" 227 LOWER_BOUND = 16 228 """`$lower_bound()`""" 229 230 231@dataclasses.dataclass 232class Function(Message): 233 """IR for a single function (+, -, *, ==, $max, etc.) in an expression.""" 234 235 function: Optional[FunctionMapping] = None 236 args: list["Expression"] = ir_data_fields.list_field(lambda: Expression) 237 function_name: Optional[Word] = None 238 source_location: Optional[Location] = None 239 240 241@dataclasses.dataclass 242class CanonicalName(Message): 243 """CanonicalName is the unique, absolute name for some object. 244 245 A CanonicalName is the unique, absolute name for some object (Type, field, 246 etc.) in the IR. It is used both in the definitions of objects ("struct 247 Foo"), and in references to objects (a field of type "Foo"). 248 """ 249 250 module_file: str = ir_data_fields.str_field() 251 """The module_file is the Module.source_file_name of the Module in which this 252 object's definition appears. 253 254 Note that the Prelude always has a Module.source_file_name of "", and thus 255 references to Prelude names will have module_file == "". 256 """ 257 258 object_path: list[str] = ir_data_fields.list_field(str) 259 """The object_path is the canonical path to the object definition within its 260 module file. 261 262 For example, the field "bar" would have an object path of 263 ["Foo", "bar"]: 264 265 struct Foo: 266 0:3 UInt bar 267 268 269 The enumerated name "BOB" would have an object path of ["Baz", "Qux", 270 "BOB"]: 271 272 struct Baz: 273 0:3 Qux qux 274 275 enum Qux: 276 BOB = 0 277 """ 278 279 280@dataclasses.dataclass 281class NameDefinition(Message): 282 """NameDefinition is IR for the name of an object, within the object. 283 284 That is, a TypeDefinition or Field will hold a NameDefinition as its 285 name. 286 """ 287 288 name: Optional[Word] = None 289 """The name, as directly generated from the source text. 290 291 name.text will match the last element of canonical_name.object_path. Note 292 that in some cases, the exact string in name.text may not appear in the 293 source text. 294 """ 295 296 canonical_name: Optional[CanonicalName] = None 297 """The CanonicalName that will appear in References. 298 This field is technically redundant: canonical_name.module_file should always 299 match the source_file_name of the enclosing Module, and 300 canonical_name.object_path should always match the names of parent nodes. 301 """ 302 303 is_anonymous: Optional[bool] = None 304 """If true, indicates that this is an automatically-generated name, which 305 should not be visible outside of its immediate namespace. 306 """ 307 308 source_location: Optional[Location] = None 309 """The location of this NameDefinition in source code.""" 310 311 312@dataclasses.dataclass 313class Reference(Message): 314 """A Reference holds the canonical name of something defined elsewhere. 315 316 For example, take this fragment: 317 318 struct Foo: 319 0:3 UInt size (s) 320 4:s Int:8[] payload 321 322 "Foo", "size", and "payload" will become NameDefinitions in their 323 corresponding Field and Message IR objects, while "UInt", the second "s", 324 and "Int" are References. Note that the second "s" will have a 325 canonical_name.object_path of ["Foo", "size"], not ["Foo", "s"]: the 326 Reference always holds the single "true" name of the object, regardless of 327 what appears in the .emb. 328 """ 329 330 canonical_name: Optional[CanonicalName] = None 331 """The canonical name of the object being referred to. 332 333 This name should be used to find the object in the IR. 334 """ 335 336 source_name: list[Word] = ir_data_fields.list_field(Word) 337 """The source_name is the name the user entered in the source file. 338 339 The source_name could be either relative or absolute, and may be an alias 340 (and thus not match any part of the canonical_name). Back ends should use 341 canonical_name for name lookup, and reserve source_name for error messages. 342 """ 343 344 is_local_name: Optional[bool] = None 345 """If true, then symbol resolution should only look at local names when 346 resolving source_name. 347 348 This is used so that the names of inline types aren't "ambiguous" if there 349 happens to be another type with the same name at a parent scope. 350 """ 351 352 # TODO(bolms): Allow absolute paths starting with ".". 353 354 source_location: Optional[Location] = None 355 """Note that this is the source_location of the *Reference*, not of the 356 object to which it refers. 357 """ 358 359 360@dataclasses.dataclass 361class FieldReference(Message): 362 """IR for a "field" or "field.sub.subsub" reference in an expression. 363 364 The first element of "path" is the "base" field, which should be directly 365 readable in the (runtime) context of the expression. For example: 366 367 struct Foo: 368 0:1 UInt header_size (h) 369 0:h UInt:8[] header_bytes 370 371 The "h" will translate to ["Foo", "header_size"], which will be the first 372 (and in this case only) element of "path". 373 374 Subsequent path elements should be treated as subfields. For example, in: 375 376 struct Foo: 377 struct Sizes: 378 0:1 UInt header_size 379 1:2 UInt body_size 380 0 [+2] Sizes sizes 381 0 [+sizes.header_size] UInt:8[] header 382 sizes.header_size [+sizes.body_size] UInt:8[] body 383 384 The references to "sizes.header_size" will have a path of [["Foo", 385 "sizes"], ["Foo", "Sizes", "header_size"]]. Note that each path element is 386 a fully-qualified reference; some back ends (C++, Python) may only use the 387 last element, while others (C) may use the complete path. 388 389 This representation is a bit awkward, and is fundamentally limited to a 390 dotted list of static field names. It does not allow an expression like 391 `array[n]` on the left side of a `.`. At this point, it is an artifact of 392 the era during which I (bolms@) thought I could get away with skipping 393 compiler-y things. 394 """ 395 396 # TODO(bolms): Add composite types to the expression type system, and 397 # replace FieldReference with a "member access" Expression kind. Further, 398 # move the symbol resolution for FieldReferences that is currently in 399 # symbol_resolver.py into type_check.py. 400 401 # TODO(bolms): Make the above change before declaring the IR to be "stable". 402 403 path: list[Reference] = ir_data_fields.list_field(Reference) 404 source_location: Optional[Location] = None 405 406 407@dataclasses.dataclass 408class OpaqueType(Message): 409 pass 410 411 412@dataclasses.dataclass 413class IntegerType(Message): 414 """Type of an integer expression.""" 415 416 # For optimization, the modular congruence of an integer expression is 417 # tracked. This consists of a modulus and a modular_value, such that for 418 # all possible values of expression, expression MOD modulus == 419 # modular_value. 420 # 421 # The modulus may be the special value "infinity" to indicate that the 422 # expression's value is exactly modular_value; otherwise, it should be a 423 # positive integer. 424 # 425 # A modulus of 1 places no constraints on the value. 426 # 427 # The modular_value should always be a nonnegative integer that is smaller 428 # than the modulus. 429 # 430 # Note that this is specifically the *modulus*, which is not equivalent to 431 # the value from C's '%' operator when the dividend is negative: in C, -7 % 432 # 4 == -3, but the modular_value here would be 1. Python uses modulus: in 433 # Python, -7 % 4 == 1. 434 modulus: Optional[str] = None 435 """The modulus portion of the modular congruence of an integer expression. 436 437 The modulus may be the special value "infinity" to indicate that the 438 expression's value is exactly modular_value; otherwise, it should be a 439 positive integer. 440 441 A modulus of 1 places no constraints on the value. 442 """ 443 modular_value: Optional[str] = None 444 """ The modular_value portion of the modular congruence of an integer expression. 445 446 The modular_value should always be a nonnegative integer that is smaller 447 than the modulus. 448 """ 449 450 # The minimum and maximum values of an integer are tracked and checked so 451 # that Emboss can implement reliable arithmetic with no operations 452 # overflowing either 64-bit unsigned or 64-bit signed 2's-complement 453 # integers. 454 # 455 # Note that constant subexpressions are allowed to overflow, as long as the 456 # final, computed constant value of the subexpression fits in a 64-bit 457 # value. 458 # 459 # The minimum_value may take the value "-infinity", and the maximum_value 460 # may take the value "infinity". These sentinel values indicate that 461 # Emboss has no bound information for the Expression, and therefore the 462 # Expression may only be evaluated during compilation; the back end should 463 # never need to compile such an expression into the target language (e.g., 464 # C++). 465 minimum_value: Optional[str] = None 466 maximum_value: Optional[str] = None 467 468 469@dataclasses.dataclass 470class BooleanType(Message): 471 value: Optional[bool] = None 472 473 474@dataclasses.dataclass 475class EnumType(Message): 476 name: Optional[Reference] = None 477 value: Optional[str] = None 478 479 480@dataclasses.dataclass 481class ExpressionType(Message): 482 opaque: Optional[OpaqueType] = ir_data_fields.oneof_field("type") 483 integer: Optional[IntegerType] = ir_data_fields.oneof_field("type") 484 boolean: Optional[BooleanType] = ir_data_fields.oneof_field("type") 485 enumeration: Optional[EnumType] = ir_data_fields.oneof_field("type") 486 487 488@dataclasses.dataclass 489class Expression(Message): 490 """IR for an expression. 491 492 An Expression is a potentially-recursive data structure. It can either 493 represent a leaf node (constant or reference) or an operation combining 494 other Expressions (function). 495 """ 496 497 constant: Optional[NumericConstant] = ir_data_fields.oneof_field("expression") 498 constant_reference: Optional[Reference] = ir_data_fields.oneof_field( 499 "expression" 500 ) 501 function: Optional[Function] = ir_data_fields.oneof_field("expression") 502 field_reference: Optional[FieldReference] = ir_data_fields.oneof_field( 503 "expression" 504 ) 505 boolean_constant: Optional[BooleanConstant] = ir_data_fields.oneof_field( 506 "expression" 507 ) 508 builtin_reference: Optional[Reference] = ir_data_fields.oneof_field( 509 "expression" 510 ) 511 512 type: Optional[ExpressionType] = None 513 source_location: Optional[Location] = None 514 515 516@dataclasses.dataclass 517class ArrayType(Message): 518 """IR for an array type ("Int:8[12]" or "Message[2]" or "UInt[3][2]").""" 519 520 base_type: Optional["Type"] = None 521 522 element_count: Optional[Expression] = ir_data_fields.oneof_field("size") 523 automatic: Optional[Empty] = ir_data_fields.oneof_field("size") 524 525 source_location: Optional[Location] = None 526 527 528@dataclasses.dataclass 529class AtomicType(Message): 530 """IR for a non-array type ("UInt" or "Foo(Version.SIX)").""" 531 532 reference: Optional[Reference] = None 533 runtime_parameter: list[Expression] = ir_data_fields.list_field(Expression) 534 source_location: Optional[Location] = None 535 536 537@dataclasses.dataclass 538class Type(Message): 539 """IR for a type reference ("UInt", "Int:8[12]", etc.).""" 540 541 atomic_type: Optional[AtomicType] = ir_data_fields.oneof_field("type") 542 array_type: Optional[ArrayType] = ir_data_fields.oneof_field("type") 543 544 size_in_bits: Optional[Expression] = None 545 source_location: Optional[Location] = None 546 547 548@dataclasses.dataclass 549class AttributeValue(Message): 550 """IR for a attribute value.""" 551 552 # TODO(bolms): Make String a type of Expression, and replace 553 # AttributeValue with Expression. 554 expression: Optional[Expression] = ir_data_fields.oneof_field("value") 555 string_constant: Optional[String] = ir_data_fields.oneof_field("value") 556 557 source_location: Optional[Location] = None 558 559 560@dataclasses.dataclass 561class Attribute(Message): 562 """IR for a [name = value] attribute.""" 563 564 name: Optional[Word] = None 565 value: Optional[AttributeValue] = None 566 back_end: Optional[Word] = None 567 is_default: Optional[bool] = None 568 source_location: Optional[Location] = None 569 570 571@dataclasses.dataclass 572class WriteTransform(Message): 573 """IR which defines an expression-based virtual field write scheme. 574 575 E.g., for a virtual field like `x_plus_one`: 576 577 struct Foo: 578 0 [+1] UInt x 579 let x_plus_one = x + 1 580 581 ... the `WriteMethod` would be `transform`, with `$logical_value - 1` for 582 `function_body` and `x` for `destination`. 583 """ 584 585 function_body: Optional[Expression] = None 586 destination: Optional[FieldReference] = None 587 588 589@dataclasses.dataclass 590class WriteMethod(Message): 591 """IR which defines the method used for writing to a virtual field.""" 592 593 physical: Optional[bool] = ir_data_fields.oneof_field("method") 594 """A physical Field can be written directly.""" 595 596 read_only: Optional[bool] = ir_data_fields.oneof_field("method") 597 """A read_only Field cannot be written.""" 598 599 alias: Optional[FieldReference] = ir_data_fields.oneof_field("method") 600 """An alias is a direct, untransformed forward of another field; it can be 601 implemented by directly returning a reference to the aliased field. 602 603 Aliases are the only kind of virtual field that may have an opaque type. 604 """ 605 606 transform: Optional[WriteTransform] = ir_data_fields.oneof_field("method") 607 """A transform is a way of turning a logical value into a value which should 608 be written to another field. 609 610 A virtual field like `let y = x + 1` would 611 have a transform WriteMethod to subtract 1 from the new `y` value, and 612 write that to `x`. 613 """ 614 615 616@dataclasses.dataclass 617class FieldLocation(Message): 618 """IR for a field location.""" 619 620 start: Optional[Expression] = None 621 size: Optional[Expression] = None 622 source_location: Optional[Location] = None 623 624 625@dataclasses.dataclass 626class Field(Message): # pylint:disable=too-many-instance-attributes 627 """IR for a field in a struct definition. 628 629 There are two kinds of Field: physical fields have location and (physical) 630 type; virtual fields have read_transform. Although there are differences, 631 in many situations physical and virtual fields are treated the same way, 632 and they can be freely intermingled in the source file. 633 """ 634 635 location: Optional[FieldLocation] = None 636 """The physical location of the field.""" 637 type: Optional[Type] = None 638 """The physical type of the field.""" 639 640 read_transform: Optional[Expression] = None 641 """The value of a virtual field.""" 642 643 write_method: Optional[WriteMethod] = None 644 """How this virtual field should be written.""" 645 646 name: Optional[NameDefinition] = None 647 """The name of the field.""" 648 abbreviation: Optional[Word] = None 649 """An optional short name for the field, only visible inside the enclosing bits/struct.""" 650 attribute: list[Attribute] = ir_data_fields.list_field(Attribute) 651 """Field-specific attributes.""" 652 documentation: list[Documentation] = ir_data_fields.list_field(Documentation) 653 """Field-specific documentation.""" 654 655 # TODO(bolms): Document conditional fields better, and replace some of this 656 # explanation with a reference to the documentation. 657 existence_condition: Optional[Expression] = None 658 """The field only exists when existence_condition evaluates to true. 659 660 For example: 661 ``` 662 struct Message: 663 0 [+4] UInt length 664 4 [+8] MessageType message_type 665 if message_type == MessageType.FOO: 666 8 [+length] Foo foo 667 if message_type == MessageType.BAR: 668 8 [+length] Bar bar 669 8+length [+4] UInt crc 670 ``` 671 For `length`, `message_type`, and `crc`, existence_condition will be 672 `boolean_constant { value: true }` 673 674 For `foo`, existence_condition will be: 675 ``` 676 function { function: EQUALITY 677 args: [reference to message_type] 678 args: { [reference to MessageType.FOO] } } 679 ``` 680 681 The `bar` field will have a similar existence_condition to `foo`: 682 ``` 683 function { function: EQUALITY 684 args: [reference to message_type] 685 args: { [reference to MessageType.BAR] } } 686 ``` 687 688 When `message_type` is `MessageType.BAR`, the `Message` struct does not contain 689 field `foo`, and vice versa for `message_type == MessageType.FOO` and field 690 `bar`: those fields only conditionally exist in the structure. 691 """ 692 693 source_location: Optional[Location] = None 694 695 696@dataclasses.dataclass 697class Structure(Message): 698 """IR for a bits or struct definition.""" 699 700 field: list[Field] = ir_data_fields.list_field(Field) 701 702 fields_in_dependency_order: list[int] = ir_data_fields.list_field(int) 703 """The fields in `field` are listed in the order they appear in the original 704 .emb. 705 706 For text format output, this can lead to poor results. Take the following 707 struct: 708 ``` 709 struct Foo: 710 b [+4] UInt a 711 0 [+4] UInt b 712 ``` 713 Here, the location of `a` depends on the current value of `b`. Because of 714 this, if someone calls 715 ``` 716 emboss::UpdateFromText(foo_view, "{ a: 10, b: 4 }"); 717 ``` 718 then foo_view will not be updated the way one would expect: if `b`'s value 719 was something other than 4 to start with, then `UpdateFromText` will write 720 the 10 to some other location, then update `b` to 4. 721 722 To avoid surprises, `emboss::DumpAsText` should return `"{ b: 4, a: 10 723 }"`. 724 725 The `fields_in_dependency_order` field provides a permutation of `field` 726 such that each field appears after all of its dependencies. For example, 727 `struct Foo`, above, would have `{ 1, 0 }` in 728 `fields_in_dependency_order`. 729 730 The exact ordering of `fields_in_dependency_order` is not guaranteed, but 731 some effort is made to keep the order close to the order fields are listed 732 in the original `.emb` file. In particular, if the ordering 0, 1, 2, 3, 733 ... satisfies dependency ordering, then `fields_in_dependency_order` will 734 be `{ 0, 1, 2, 3, ... }`. 735 """ 736 737 source_location: Optional[Location] = None 738 739 740@dataclasses.dataclass 741class External(Message): 742 """IR for an external type declaration.""" 743 744 # Externals have no values other than name and attribute list, which are 745 # common to all type definitions. 746 747 source_location: Optional[Location] = None 748 749 750@dataclasses.dataclass 751class EnumValue(Message): 752 """IR for a single value within an enumerated type.""" 753 754 name: Optional[NameDefinition] = None 755 """The name of the enum value.""" 756 value: Optional[Expression] = None 757 """The value of the enum value.""" 758 documentation: list[Documentation] = ir_data_fields.list_field(Documentation) 759 """Value-specific documentation.""" 760 attribute: list[Attribute] = ir_data_fields.list_field(Attribute) 761 """Value-specific attributes.""" 762 763 source_location: Optional[Location] = None 764 765 766@dataclasses.dataclass 767class Enum(Message): 768 """IR for an enumerated type definition.""" 769 770 value: list[EnumValue] = ir_data_fields.list_field(EnumValue) 771 source_location: Optional[Location] = None 772 773 774@dataclasses.dataclass 775class Import(Message): 776 """IR for an import statement in a module.""" 777 778 file_name: Optional[String] = None 779 """The file to import.""" 780 local_name: Optional[Word] = None 781 """The name to use within this module.""" 782 source_location: Optional[Location] = None 783 784 785@dataclasses.dataclass 786class RuntimeParameter(Message): 787 """IR for a runtime parameter definition.""" 788 789 name: Optional[NameDefinition] = None 790 """The name of the parameter.""" 791 type: Optional[ExpressionType] = None 792 """The type of the parameter.""" 793 794 # TODO(bolms): Actually implement the set builder type notation. 795 physical_type_alias: Optional[Type] = None 796 """For convenience and readability, physical types may be used in the .emb 797 source instead of a full expression type. 798 799 That way, users can write 800 something like: 801 ``` 802 struct Foo(version :: UInt:8): 803 ``` 804 instead of: 805 ``` 806 struct Foo(version :: {$int x |: 0 <= x <= 255}): 807 ``` 808 In these cases, physical_type_alias holds the user-supplied type, and type 809 is filled in after initial parsing is finished. 810 """ 811 812 source_location: Optional[Location] = None 813 814 815class AddressableUnit(int, enum.Enum): 816 """The "addressable unit" is the size of the smallest unit that can be read 817 818 from the backing store that this type expects. For `struct`s, this is 819 BYTE; for `enum`s and `bits`, this is BIT, and for `external`s it depends 820 on the specific type 821 """ 822 823 NONE = 0 824 BIT = 1 825 BYTE = 8 826 827 828@dataclasses.dataclass 829class TypeDefinition(Message): 830 """Container IR for a type definition (struct, union, etc.)""" 831 832 external: Optional[External] = ir_data_fields.oneof_field("type") 833 enumeration: Optional[Enum] = ir_data_fields.oneof_field("type") 834 structure: Optional[Structure] = ir_data_fields.oneof_field("type") 835 836 name: Optional[NameDefinition] = None 837 """The name of the type.""" 838 attribute: list[Attribute] = ir_data_fields.list_field(Attribute) 839 """All attributes attached to the type.""" 840 documentation: list[Documentation] = ir_data_fields.list_field(Documentation) 841 """Docs for the type.""" 842 # pylint:disable=undefined-variable 843 subtype: list["TypeDefinition"] = ir_data_fields.list_field( 844 lambda: TypeDefinition 845 ) 846 """Subtypes of this type.""" 847 addressable_unit: Optional[AddressableUnit] = None 848 849 runtime_parameter: list[RuntimeParameter] = ir_data_fields.list_field( 850 RuntimeParameter 851 ) 852 """If the type requires parameters at runtime, these are its parameters. 853 854 These are currently only allowed on structures, but in the future they 855 should be allowed on externals. 856 """ 857 source_location: Optional[Location] = None 858 859 860@dataclasses.dataclass 861class Module(Message): 862 """The IR for an individual Emboss module (file).""" 863 864 attribute: list[Attribute] = ir_data_fields.list_field(Attribute) 865 """Module-level attributes.""" 866 type: list[TypeDefinition] = ir_data_fields.list_field(TypeDefinition) 867 """Module-level type definitions.""" 868 documentation: list[Documentation] = ir_data_fields.list_field(Documentation) 869 """Module-level docs.""" 870 foreign_import: list[Import] = ir_data_fields.list_field(Import) 871 """Other modules imported.""" 872 source_text: Optional[str] = None 873 """The original source code.""" 874 source_location: Optional[Location] = None 875 """Source code covered by this IR.""" 876 source_file_name: Optional[str] = None 877 """Name of the source file.""" 878 879 880@dataclasses.dataclass 881class EmbossIr(Message): 882 """The top-level IR for an Emboss module and all of its dependencies.""" 883 884 module: list[Module] = ir_data_fields.list_field(Module) 885 """All modules. 886 887 The first entry will be the main module; back ends should 888 generate code corresponding to that module. The second entry will be the 889 prelude module. 890 """ 891 892 893# Post-process the dataclasses to add cached fields. 894ir_data_fields.cache_message_specs(sys.modules[Message.__module__], Message) 895