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