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