xref: /aosp_15_r20/external/emboss/compiler/util/ir_data.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
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