Cleanup: pushing various changes to ir_pb2.py.
(Primarily whitespace, naming, and comments. No functional changes.)
diff --git a/public/ir_pb2.py b/public/ir_pb2.py
index b33a06b..e175349 100644
--- a/public/ir_pb2.py
+++ b/public/ir_pb2.py
@@ -12,926 +12,996 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Intermediate representation (IR) for Emboss.
-#
-# This was originally a Google Protocol Buffer file, but as of 2019 it turns
-# out that a) the public Google Python Protocol Buffer implementation is
-# extremely slow, and b) all the ways of getting Bazel+Python+Protocol Buffers
-# to play nice are hacky and fragile.
-#
-# Thus, this file, which presents a similar-enough interface that the rest of
-# Emboss can use it with minimal changes.
-#
-# Protobufs have a really, really strange, un-Pythonic interface, with tricky
-# implicit semantics -- mostly around magically instantiating protos when you
-# assign to some deeply-nested field. I (bolms@) would *strongly* prefer to
-# have a more explicit interface, but don't (currently) have time to refactor
-# everything that touches the IR (i.e., the entire compiler).
+"""Intermediate representation (IR) for Emboss.
+
+This was originally a Google Protocol Buffer file, but as of 2019 it turns
+out that a) the public Google Python Protocol Buffer implementation is
+extremely slow, and b) all the ways of getting Bazel+Python+Protocol Buffers
+to play nice are hacky and fragile.
+
+Thus, this file, which presents a similar-enough interface that the rest of
+Emboss can use it with minimal changes.
+
+Protobufs have a really, really strange, un-Pythonic interface, with tricky
+implicit semantics -- mostly around magically instantiating protos when you
+assign to some deeply-nested field. I (bolms@) would *strongly* prefer to
+have a more explicit interface, but don't (currently) have time to refactor
+everything that touches the IR (i.e., the entire compiler).
+"""
import json
-import re
import sys
if sys.version_info[0] == 2:
- _text = unicode
- _text_types = (unicode, str)
- _int = long
- _int_types = (int, long)
+ _Text = unicode
+ _text_types = (unicode, str)
+ _Int = long
+ _int_types = (int, long)
else:
- _text = str
- _text_types = (str,)
- _int = int
- _int_types = (int,)
+ _Text = str
+ _text_types = (str,)
+ _Int = int
+ _int_types = (int,)
_BASIC_TYPES = _text_types + _int_types + (bool,)
class Optional(object):
- def __init__(self, type, oneof=None, names=None):
- self._type = type
- self._oneof = oneof
- self._names = names
+ """Property implementation for "optional"-like fields."""
- def __get__(self, obj, type=None):
- result = obj.raw_fields.get(self.name, None)
- if result is not None:
- return result
- if self.type in _BASIC_TYPES:
- return self._type()
- result = self._type()
+ def __init__(self, type_, oneof=None, decode_names=None):
+ """Creates a Proto "optional"-like data member.
- def on_write():
- self._set_value(obj, result)
+ Args:
+ type_: The type of the field; e.g., _Int or _Text.
+ oneof: If set, the name of the proto-like "oneof" that this field is a
+ member of. Within a structure, at most one field of a particular
+ "oneof" may be set at a time; setting a member of the "oneof" will
+ clear any other member that might be set.
+ decode_names: An optional callable that takes a str and returns a
+ value of type type_; allows strs to be used to set "enums" using
+ their symbolic names.
+ """
+ self._type = type_
+ self._oneof = oneof
+ self._decode_names = decode_names
- result.set_on_write(on_write)
- return result
+ def __get__(self, obj, type_=None):
+ result = obj.raw_fields.get(self.name, None)
+ if result is not None:
+ return result
+ if self.type in _BASIC_TYPES:
+ return self._type()
+ result = self._type()
- def __set__(self, obj, value):
- if issubclass(self._type, _BASIC_TYPES):
- self.set(obj, value)
- else:
- raise AttributeError("Cannot set {} (type {}) for type {}".format(
- value, value.__class__, self._type))
+ def on_write():
+ self._set_value(obj, result)
- def _set_value(self, obj, value):
- if self._oneof is not None:
- current = obj.oneofs.get(self._oneof)
- if current in obj.raw_fields:
- del obj.raw_fields[current]
- obj.oneofs[self._oneof] = self.name
- obj.raw_fields[self.name] = value
- obj.on_write()
+ result.set_on_write(on_write)
+ return result
- def set(self, obj, value):
- if value is None:
- return
- if isinstance(value, dict):
- self._set_value(obj, self._type(**value))
- elif isinstance(value, _text) and self._names:
- self._set_value(obj, self._type(self._names(value)))
- elif (not isinstance(value, self._type) and
- not (self._type == _int and isinstance(value, _int_types)) and
- not (self._type == _text and isinstance(value, _text_types))):
- raise AttributeError("Cannot set {} (type {}) for type {}".format(
- value, value.__class__, self._type))
- elif issubclass(self._type, Message):
- self._set_value(obj, self._type(**value.raw_fields))
- else:
- self._set_value(obj, self._type(value))
+ def __set__(self, obj, value):
+ if issubclass(self._type, _BASIC_TYPES):
+ self.set(obj, value)
+ else:
+ raise AttributeError("Cannot set {} (type {}) for type {}".format(
+ value, value.__class__, self._type))
- def resolve_type(self):
- if isinstance(self._type, type(lambda: None)):
- self._type = self._type()
+ def _set_value(self, obj, value):
+ if self._oneof is not None:
+ current = obj.oneofs.get(self._oneof)
+ if current in obj.raw_fields:
+ del obj.raw_fields[current]
+ obj.oneofs[self._oneof] = self.name
+ obj.raw_fields[self.name] = value
+ obj.on_write()
- @property
- def type(self):
- return self._type
+ def set(self, obj, value):
+ """Sets the given value to the property."""
+ if value is None:
+ return
+ if isinstance(value, dict):
+ self._set_value(obj, self._type(**value))
+ elif isinstance(value, _Text) and self._decode_names:
+ self._set_value(obj, self._type(self._decode_names(value)))
+ elif (not isinstance(value, self._type) and
+ not (self._type == _Int and isinstance(value, _int_types)) and
+ not (self._type == _Text and isinstance(value, _text_types))):
+ raise AttributeError("Cannot set {} (type {}) for type {}".format(
+ value, value.__class__, self._type))
+ elif issubclass(self._type, Message):
+ self._set_value(obj, self._type(**value.raw_fields))
+ else:
+ self._set_value(obj, self._type(value))
-class Oneof(object):
- def __init__(self, **members):
- self.members = members
+ def resolve_type(self):
+ if isinstance(self._type, type(lambda: None)):
+ self._type = self._type()
- def __get__(self, obj, type=None):
- return obj.oneofs[self.name]
-
- def __set__(self, obj, value):
- raise AttributeError("Cannot set {}".format(self.name))
+ @property
+ def type(self):
+ return self._type
class TypedScopedList(object):
- def __init__(self, type, on_write=lambda: None):
- self._type = type
- self._list = []
- self._on_write = on_write
+ """A list with typechecking that notifies its parent when written to.
- def __iter__(self):
- return iter(self._list)
+ TypedScopedList implements roughly the same semantics as the value of a
+ Protobuf repeated field. In particular, it checks that any values added
+ to the list are of the correct type, and it calls the on_write callable
+ when a value is added to the list, in order to implement the Protobuf
+ "autovivification" semantics.
+ """
- def __delitem__(self, key):
- del self._list[key]
+ def __init__(self, type_, on_write=lambda: None):
+ self._type = type_
+ self._list = []
+ self._on_write = on_write
- def __getitem__(self, key):
- return self._list[key]
+ def __iter__(self):
+ return iter(self._list)
- def extend(self, values):
- for value in values:
- if isinstance(value, dict):
- self._list.append(self._type(**value))
- elif (not isinstance(value, self._type) and
- not (self._type == _int and isinstance(value, _int_types)) and
- not (self._type == _text and isinstance(value, _text_types))):
- raise TypeError(
- "Needed {}, got {} ({!r})".format(
- self._type, value.__class__, value))
- else:
- if self._type in _BASIC_TYPES:
- self._list.append(self._type(value))
- else:
- self._list.append(self._type(**value.raw_fields))
- self._on_write()
+ def __delitem__(self, key):
+ del self._list[key]
- def __repr__(self):
- return repr(self._list)
+ def __getitem__(self, key):
+ return self._list[key]
- def __len__(self):
- return len(self._list)
+ def extend(self, values):
+ """list-like extend()."""
- def __eq__(self, other):
- return ((self.__class__ == other.__class__ and
- self._list == other._list) or
- (isinstance(other, list) and self._list == other))
+ for value in values:
+ if isinstance(value, dict):
+ self._list.append(self._type(**value))
+ elif (not isinstance(value, self._type) and
+ not (self._type == _Int and isinstance(value, _int_types)) and
+ not (self._type == _Text and isinstance(value, _text_types))):
+ raise TypeError(
+ "Needed {}, got {} ({!r})".format(
+ self._type, value.__class__, value))
+ else:
+ if self._type in _BASIC_TYPES:
+ self._list.append(self._type(value))
+ else:
+ self._list.append(self._type(**value.raw_fields))
+ self._on_write()
- def __ne__(self, other):
- return not (self == other)
+ def __repr__(self):
+ return repr(self._list)
+
+ def __len__(self):
+ return len(self._list)
+
+ def __eq__(self, other):
+ return ((self.__class__ == other.__class__ and
+ self._list == other._list) or # pylint:disable=protected-access
+ (isinstance(other, list) and self._list == other))
+
+ def __ne__(self, other):
+ return not (self == other) # pylint:disable=superfluous-parens
class Repeated(object):
- def __init__(self, type):
- self._type = type
+ """A proto-"repeated"-like property."""
- def __get__(self, obj, type=None):
- return obj.raw_fields[self.name]
+ def __init__(self, type_):
+ self._type = type_
- def __set__(self, obj, value):
- raise AttributeError("Cannot set {}".format(self.name))
+ def __get__(self, obj, type_=None):
+ return obj.raw_fields[self.name]
- def set(self, obj, values):
- typed_list = obj.raw_fields[self.name]
- if not isinstance(values, (list, TypedScopedList)):
- raise TypeError("Cannot initialize repeated field {} from {}".format(
- self.name, values.__class__))
- del typed_list[:]
- typed_list.extend(values)
+ def __set__(self, obj, value):
+ raise AttributeError("Cannot set {}".format(self.name))
- def resolve_type(self):
- if isinstance(self._type, type(lambda: None)):
- self._type = self._type()
+ def set(self, obj, values):
+ typed_list = obj.raw_fields[self.name]
+ if not isinstance(values, (list, TypedScopedList)):
+ raise TypeError("Cannot initialize repeated field {} from {}".format(
+ self.name, values.__class__))
+ del typed_list[:]
+ typed_list.extend(values)
- @property
- def type(self):
- return self._type
+ def resolve_type(self):
+ if isinstance(self._type, type(lambda: None)):
+ self._type = self._type()
+
+ @property
+ def type(self):
+ return self._type
_deferred_specs = []
+
def message(cls):
- # TODO(bolms): move this into __init_subclass__ after dropping Python 2
- # support.
- _deferred_specs.append(cls)
- return cls
+ # TODO(bolms): move this into __init_subclass__ after dropping Python 2
+ # support.
+ _deferred_specs.append(cls)
+ return cls
+
class Message(object):
+ """Base class for proto "message"-like objects."""
- def __init__(self, **field_values):
- self.oneofs = {}
- self._on_write = lambda: None
- self._initialize_raw_fields_from(field_values)
+ def __init__(self, **field_values):
+ self.oneofs = {}
+ self._on_write = lambda: None
+ self._initialize_raw_fields_from(field_values)
- def _initialize_raw_fields_from(self, field_values):
- self.raw_fields = {}
- for name, type in self.repeated_fields.items():
- self.raw_fields[name] = TypedScopedList(type, self.on_write)
- for k, v in field_values.items():
- spec = self.field_specs.get(k)
- if spec is None:
- raise AttributeError("No field {} on {}.".format(
- k, self.__class__.__name__))
- spec.set(self, v)
+ def _initialize_raw_fields_from(self, field_values):
+ self.raw_fields = {}
+ for name, type_ in self.repeated_fields.items():
+ self.raw_fields[name] = TypedScopedList(type_, self.on_write)
+ for k, v in field_values.items():
+ spec = self.field_specs.get(k)
+ if spec is None:
+ raise AttributeError("No field {} on {}.".format(
+ k, self.__class__.__name__))
+ spec.set(self, v)
- @classmethod
- def from_json(cls, text):
- as_dict = json.loads(text)
- return cls(**as_dict)
+ @classmethod
+ def from_json(cls, text):
+ as_dict = json.loads(text)
+ return cls(**as_dict)
- def on_write(self):
- self._on_write()
- self._on_write = lambda: None
+ def on_write(self):
+ self._on_write()
+ self._on_write = lambda: None
- def set_on_write(self, on_write):
- self._on_write = on_write
+ def set_on_write(self, on_write):
+ self._on_write = on_write
- def __eq__(self, other):
- return (self.__class__ == other.__class__ and
- self.raw_fields == other.raw_fields)
+ def __eq__(self, other):
+ return (self.__class__ == other.__class__ and
+ self.raw_fields == other.raw_fields)
- def CopyFrom(self, other):
- if self.__class__ != other.__class__:
- raise TypeError("{} cannot CopyFrom {}".format(
- self.__class__.__name__, other.__class__.__name__))
- self._initialize_raw_fields_from(other.raw_fields)
- self.on_write()
+ # Non-PEP8 name to mimic the Google Protobuf interface.
+ def CopyFrom(self, other): # pylint:disable=invalid-name
+ if self.__class__ != other.__class__:
+ raise TypeError("{} cannot CopyFrom {}".format(
+ self.__class__.__name__, other.__class__.__name__))
+ self._initialize_raw_fields_from(other.raw_fields)
+ self.on_write()
- def HasField(self, name):
- return name in self.raw_fields
+ # Non-PEP8 name to mimic the Google Protobuf interface.
+ def HasField(self, name): # pylint:disable=invalid-name
+ return name in self.raw_fields
- def WhichOneof(self, oneof_name):
- return self.oneofs.get(oneof_name)
+ # Non-PEP8 name to mimic the Google Protobuf interface.
+ def WhichOneof(self, oneof_name): # pylint:disable=invalid-name
+ return self.oneofs.get(oneof_name)
- def to_dict(self):
- result = {}
- for k, v in self.raw_fields.items():
- if isinstance(v, _BASIC_TYPES):
- result[k] = v
- elif isinstance(v, TypedScopedList):
- if len(v):
- # For compatibility with the proto world, empty lists are just
- # elided.
- result[k] = [
- item if isinstance(item, _BASIC_TYPES) else item.to_dict()
- for item in v
- ]
- else:
- result[k] = v.to_dict()
- return result
+ def to_dict(self):
+ """Converts the message to a dict."""
- def __repr__(self):
- return self.to_json(separators=(',', ':'), sort_keys=True)
+ result = {}
+ for k, v in self.raw_fields.items():
+ if isinstance(v, _BASIC_TYPES):
+ result[k] = v
+ elif isinstance(v, TypedScopedList):
+ if v:
+ # For compatibility with the proto world, empty lists are just
+ # elided.
+ result[k] = [
+ item if isinstance(item, _BASIC_TYPES) else item.to_dict()
+ for item in v
+ ]
+ else:
+ result[k] = v.to_dict()
+ return result
- def to_json(self, *args, **kwargs):
- return json.dumps(self.to_dict(), *args, **kwargs)
+ def __repr__(self):
+ return self.to_json(separators=(",", ":"), sort_keys=True)
- def __str__(self):
- return _text(self.to_dict())
+ def to_json(self, *args, **kwargs):
+ return json.dumps(self.to_dict(), *args, **kwargs)
+
+ def __str__(self):
+ return _Text(self.to_dict())
def _initialize_deferred_specs():
- for cls in _deferred_specs:
- field_specs = {}
- repeated_fields = {}
- for k, v in cls.__dict__.items():
- if k[0] == "_":
- continue
- if isinstance(v, (Optional, Repeated)):
- v.name = k
- v.resolve_type()
- field_specs[k] = v
- if isinstance(v, Repeated):
- repeated_fields[k] = v.type
- cls.field_specs = field_specs
- cls.repeated_fields = repeated_fields
+ """Calls any lambdas in specs, to facilitate late binding.
+
+ When two Message subclasses are mutually recursive, the standard way of
+ referencing one of the classes will not work, because its name is not
+ yet defined. E.g.:
+
+ class A(Message):
+ b = Optional(B)
+
+ class B(Message):
+ a = Optional(A)
+
+ In this case, Python complains when trying to construct the class A,
+ because it cannot resolve B.
+
+ To accommodate this, Optional and Repeated will accept callables instead of
+ types, like:
+
+ class A(Message):
+ b = Optional(lambda: B)
+
+ class B(Message):
+ a = Optional(A)
+
+ Once all of the message classes have been defined, it is safe to go back and
+ resolve all of the names by calling all of the lambdas that were used in place
+ of types. This function just iterates through the message types, and asks
+ their Optional and Repeated properties to call the lambdas that were used in
+ place of types.
+ """
+
+ for cls in _deferred_specs:
+ field_specs = {}
+ repeated_fields = {}
+ for k, v in cls.__dict__.items():
+ if k.startswith("_"):
+ continue
+ if isinstance(v, (Optional, Repeated)):
+ v.name = k
+ v.resolve_type()
+ field_specs[k] = v
+ if isinstance(v, Repeated):
+ repeated_fields[k] = v.type
+ cls.field_specs = field_specs
+ cls.repeated_fields = repeated_fields
################################################################################
+# From here to (nearly) the end of the file are actual structure definitions.
@message
class Position(Message):
- """A zero-width position within a source file."""
- line = Optional(int) # Line (starts from 1).
- column = Optional(int) # Column (starts from 1).
+ """A zero-width position within a source file."""
+ line = Optional(int) # Line (starts from 1).
+ column = Optional(int) # Column (starts from 1).
@message
class Location(Message):
- """A half-open start:end range within a source file."""
- start = Optional(Position) # Beginning of the range.
- end = Optional(Position) # One column past the end of the range.
+ """A half-open start:end range within a source file."""
+ start = Optional(Position) # Beginning of the range.
+ end = Optional(Position) # One column past the end of the range.
- # True if this Location is outside of the parent object's Location.
- is_disjoint_from_parent = Optional(bool)
+ # True if this Location is outside of the parent object's Location.
+ is_disjoint_from_parent = Optional(bool)
- # True if this Location's parent was synthesized, and does not directly
- # appear in the source file. The Emboss front end uses this field to cull
- # irrelevant error messages.
- is_synthetic = Optional(bool)
+ # True if this Location's parent was synthesized, and does not directly
+ # appear in the source file. The Emboss front end uses this field to cull
+ # irrelevant error messages.
+ is_synthetic = Optional(bool)
@message
class Word(Message):
- """IR for a bare word in the source file.
+ """IR for a bare word in the source file.
- This is used in NameDefinitions and References."""
+ This is used in NameDefinitions and References.
+ """
- text = Optional(_text)
- source_location = Optional(Location)
+ text = Optional(_Text)
+ source_location = Optional(Location)
@message
class String(Message):
- """IR for a string in the source file."""
- text = Optional(_text)
- source_location = Optional(Location)
+ """IR for a string in the source file."""
+ text = Optional(_Text)
+ source_location = Optional(Location)
@message
class Documentation(Message):
- text = Optional(_text)
- source_location = Optional(Location)
+ text = Optional(_Text)
+ source_location = Optional(Location)
@message
class BooleanConstant(Message):
- """IR for a boolean constant."""
- value = Optional(bool)
- source_location = Optional(Location)
+ """IR for a boolean constant."""
+ value = Optional(bool)
+ source_location = Optional(Location)
@message
class Empty(Message):
- """Placeholder message for automatic element counts for arrays."""
- source_location = Optional(Location)
+ """Placeholder message for automatic element counts for arrays."""
+ source_location = Optional(Location)
@message
class NumericConstant(Message):
- """IR for any numeric constant."""
+ """IR for any numeric constant."""
- # Numeric constants are stored as decimal strings; this is the simplest way
- # to store the full -2**63..+2**64 range.
- #
- # TODO(bolms): switch back to int, and just use strings during
- # serialization, now that we're free of proto.
- value = Optional(_text)
- source_location = Optional(Location)
+ # Numeric constants are stored as decimal strings; this is the simplest way
+ # to store the full -2**63..+2**64 range.
+ #
+ # TODO(bolms): switch back to int, and just use strings during
+ # serialization, now that we're free of proto.
+ value = Optional(_Text)
+ source_location = Optional(Location)
@message
class Function(Message):
- """IR for a single function (+, -, *, ==, $max, etc.) in an expression."""
- UNKNOWN = 0
- ADDITION = 1 # +
- SUBTRACTION = 2 # -
- MULTIPLICATION = 3 # *
- EQUALITY = 4 # ==
- INEQUALITY = 5 # !=
- AND = 6 # &&
- OR = 7 # ||
- LESS = 8 # <
- LESS_OR_EQUAL = 9 # <=
- GREATER = 10 # >
- GREATER_OR_EQUAL = 11 # >=
- CHOICE = 12 # ?:
- MAXIMUM = 13 # $max()
- PRESENCE = 14 # $present()
- UPPER_BOUND = 15 # $upper_bound()
- LOWER_BOUND = 16 # $lower_bound()
+ """IR for a single function (+, -, *, ==, $max, etc.) in an expression."""
+ UNKNOWN = 0
+ ADDITION = 1 # +
+ SUBTRACTION = 2 # -
+ MULTIPLICATION = 3 # *
+ EQUALITY = 4 # ==
+ INEQUALITY = 5 # !=
+ AND = 6 # &&
+ OR = 7 # ||
+ LESS = 8 # <
+ LESS_OR_EQUAL = 9 # <=
+ GREATER = 10 # >
+ GREATER_OR_EQUAL = 11 # >=
+ CHOICE = 12 # ?:
+ MAXIMUM = 13 # $max()
+ PRESENCE = 14 # $present()
+ UPPER_BOUND = 15 # $upper_bound()
+ LOWER_BOUND = 16 # $lower_bound()
- function = Optional(int, names=lambda x: getattr(Function, x))
- args = Repeated(lambda: Expression)
- function_name = Optional(Word)
- source_location = Optional(Location)
+ # pylint:disable=undefined-variable
+ function = Optional(int, decode_names=lambda x: getattr(Function, x))
+ args = Repeated(lambda: Expression)
+ function_name = Optional(Word)
+ source_location = Optional(Location)
@message
class CanonicalName(Message):
- """CanonicalName is the unique, absolute name for some object.
+ """CanonicalName is the unique, absolute name for some object.
- A CanonicalName is the unique, absolute name for some object (Type, field,
- etc.) in the IR. It is used both in the definitions of objects ("struct
- Foo"), and in references to objects (a field of type "Foo")."""
+ A CanonicalName is the unique, absolute name for some object (Type, field,
+ etc.) in the IR. It is used both in the definitions of objects ("struct
+ Foo"), and in references to objects (a field of type "Foo").
+ """
- # The module_file is the Module.source_file_name of the Module in which this
- # object's definition appears. Note that the Prelude always has a
- # Module.source_file_name of "", and thus references to Prelude names will
- # have module_file == "".
- module_file = Optional(_text)
+ # The module_file is the Module.source_file_name of the Module in which this
+ # object's definition appears. Note that the Prelude always has a
+ # Module.source_file_name of "", and thus references to Prelude names will
+ # have module_file == "".
+ module_file = Optional(_Text)
- # The object_path is the canonical path to the object definition within its
- # module file. For example, the field "bar" would have an object path of
- # ["Foo", "bar"]:
- #
- # struct Foo:
- # 0:3 UInt bar
- #
- #
- # The enumerated name "BOB" would have an object path of ["Baz", "Qux",
- # "BOB"]:
- #
- # struct Baz:
- # 0:3 Qux qux
- #
- # enum Qux:
- # BOB = 0
- object_path = Repeated(_text)
+ # The object_path is the canonical path to the object definition within its
+ # module file. For example, the field "bar" would have an object path of
+ # ["Foo", "bar"]:
+ #
+ # struct Foo:
+ # 0:3 UInt bar
+ #
+ #
+ # The enumerated name "BOB" would have an object path of ["Baz", "Qux",
+ # "BOB"]:
+ #
+ # struct Baz:
+ # 0:3 Qux qux
+ #
+ # enum Qux:
+ # BOB = 0
+ object_path = Repeated(_Text)
@message
class NameDefinition(Message):
- """NameDefinition is IR for the name of an object, within the object.
+ """NameDefinition is IR for the name of an object, within the object.
- That is, a TypeDefinition or Field will hold a NameDefinition as its
- name."""
+ That is, a TypeDefinition or Field will hold a NameDefinition as its
+ name.
+ """
- # The name, as directly generated from the source text. name.text will
- # match the last element of canonical_name.object_path. Note that in some
- # cases, the exact string in name.text may not appear in the source text.
- name = Optional(Word)
+ # The name, as directly generated from the source text. name.text will
+ # match the last element of canonical_name.object_path. Note that in some
+ # cases, the exact string in name.text may not appear in the source text.
+ name = Optional(Word)
- # The CanonicalName that will appear in References. This field is
- # technically redundant: canonical_name.module_file should always match the
- # source_file_name of the enclosing Module, and canonical_name.object_path
- # should always match the names of parent nodes.
- canonical_name = Optional(CanonicalName)
+ # The CanonicalName that will appear in References. This field is
+ # technically redundant: canonical_name.module_file should always match the
+ # source_file_name of the enclosing Module, and canonical_name.object_path
+ # should always match the names of parent nodes.
+ canonical_name = Optional(CanonicalName)
- # If true, indicates that this is an automatically-generated name, which
- # should not be visible outside of its immediate namespace.
- is_anonymous = Optional(bool)
+ # If true, indicates that this is an automatically-generated name, which
+ # should not be visible outside of its immediate namespace.
+ is_anonymous = Optional(bool)
- # The location of this NameDefinition in source code.
- source_location = Optional(Location)
+ # The location of this NameDefinition in source code.
+ source_location = Optional(Location)
@message
class Reference(Message):
- """A Reference holds the canonical name of something defined elsewhere.
+ """A Reference holds the canonical name of something defined elsewhere.
- For example, take this fragment:
+ For example, take this fragment:
- struct Foo:
- 0:3 UInt size (s)
- 4:s Int:8[] payload
+ struct Foo:
+ 0:3 UInt size (s)
+ 4:s Int:8[] payload
- "Foo", "size", and "payload" will become NameDefinitions in their
- corresponding Field and Message IR objects, while "UInt", the second "s",
- and "Int" are References. Note that the second "s" will have a
- canonical_name.object_path of ["Foo", "size"], not ["Foo", "s"]: the
- Reference always holds the single "true" name of the object, regardless of
- what appears in the .emb."""
+ "Foo", "size", and "payload" will become NameDefinitions in their
+ corresponding Field and Message IR objects, while "UInt", the second "s",
+ and "Int" are References. Note that the second "s" will have a
+ canonical_name.object_path of ["Foo", "size"], not ["Foo", "s"]: the
+ Reference always holds the single "true" name of the object, regardless of
+ what appears in the .emb.
+ """
- # The canonical name of the object being referred to. This name should be
- # used to find the object in the IR.
- canonical_name = Optional(CanonicalName)
+ # The canonical name of the object being referred to. This name should be
+ # used to find the object in the IR.
+ canonical_name = Optional(CanonicalName)
- # The source_name is the name the user entered in the source file; it could
- # be either relative or absolute, and may be an alias (and thus not match
- # any part of the canonical_name). Back ends should use canonical_name for
- # name lookup, and reserve source_name for error messages.
- source_name = Repeated(Word)
+ # The source_name is the name the user entered in the source file; it could
+ # be either relative or absolute, and may be an alias (and thus not match
+ # any part of the canonical_name). Back ends should use canonical_name for
+ # name lookup, and reserve source_name for error messages.
+ source_name = Repeated(Word)
- # If true, then symbol resolution should only look at local names when
- # resolving source_name. This is used so that the names of inline types
- # aren't "ambiguous" if there happens to be another type with the same name
- # at a parent scope.
- is_local_name = Optional(bool)
+ # If true, then symbol resolution should only look at local names when
+ # resolving source_name. This is used so that the names of inline types
+ # aren't "ambiguous" if there happens to be another type with the same name
+ # at a parent scope.
+ is_local_name = Optional(bool)
- # TODO(bolms): Allow absolute paths starting with ".".
+ # TODO(bolms): Allow absolute paths starting with ".".
- # Note that this is the source_location of the *Reference*, not of the
- # object to which it refers.
- source_location = Optional(Location)
+ # Note that this is the source_location of the *Reference*, not of the
+ # object to which it refers.
+ source_location = Optional(Location)
@message
class FieldReference(Message):
- """IR for a "field" or "field.sub.subsub" reference in an expression.
+ """IR for a "field" or "field.sub.subsub" reference in an expression.
- The first element of "path" is the "base" field, which should be directly
- readable in the (runtime) context of the expression. For example:
-
- struct Foo:
- 0:1 UInt header_size (h)
- 0:h UInt:8[] header_bytes
-
- The "h" will translate to ["Foo", "header_size"], which will be the first
- (and in this case only) element of "path".
-
- Subsequent path elements should be treated as subfields. For example, in:
-
- struct Foo:
- struct Sizes:
- 0:1 UInt header_size
- 1:2 UInt body_size
- 0 [+2] Sizes sizes
- 0 [+sizes.header_size] UInt:8[] header
- sizes.header_size [+sizes.body_size] UInt:8[] body
-
- The references to "sizes.header_size" will have a path of [["Foo",
- "sizes"], ["Foo", "Sizes", "header_size"]]. Note that each path element is
- a fully-qualified reference; some back ends (C++, Python) may only use the
- last element, while others (C) may use the complete path.
+ The first element of "path" is the "base" field, which should be directly
+ readable in the (runtime) context of the expression. For example:
- This representation is a bit awkward, and is fundamentally limited to a
- dotted list of static field names. It does not allow an expression like
- `array[n]` on the left side of a `.`. At this point, it is an artifact of
- the era during which I (bolms@) thought I could get away with skipping
- compiler-y things."""
+ struct Foo:
+ 0:1 UInt header_size (h)
+ 0:h UInt:8[] header_bytes
- # TODO(bolms): Add composite types to the expression type system, and
- # replace FieldReference with a "member access" Expression kind. Further,
- # move the symbol resolution for FieldReferences that is currently in
- # symbol_resolver.py into type_check.py.
+ The "h" will translate to ["Foo", "header_size"], which will be the first
+ (and in this case only) element of "path".
- # TODO(bolms): Make the above change before declaring the IR to be "stable".
+ Subsequent path elements should be treated as subfields. For example, in:
- path = Repeated(Reference)
- source_location = Optional(Location)
+ struct Foo:
+ struct Sizes:
+ 0:1 UInt header_size
+ 1:2 UInt body_size
+ 0 [+2] Sizes sizes
+ 0 [+sizes.header_size] UInt:8[] header
+ sizes.header_size [+sizes.body_size] UInt:8[] body
+
+ The references to "sizes.header_size" will have a path of [["Foo",
+ "sizes"], ["Foo", "Sizes", "header_size"]]. Note that each path element is
+ a fully-qualified reference; some back ends (C++, Python) may only use the
+ last element, while others (C) may use the complete path.
+
+ This representation is a bit awkward, and is fundamentally limited to a
+ dotted list of static field names. It does not allow an expression like
+ `array[n]` on the left side of a `.`. At this point, it is an artifact of
+ the era during which I (bolms@) thought I could get away with skipping
+ compiler-y things.
+ """
+
+ # TODO(bolms): Add composite types to the expression type system, and
+ # replace FieldReference with a "member access" Expression kind. Further,
+ # move the symbol resolution for FieldReferences that is currently in
+ # symbol_resolver.py into type_check.py.
+
+ # TODO(bolms): Make the above change before declaring the IR to be "stable".
+
+ path = Repeated(Reference)
+ source_location = Optional(Location)
@message
class OpaqueType(Message):
- pass
+ pass
@message
class IntegerType(Message):
- """Type of an integer expression."""
+ """Type of an integer expression."""
- # For optimization, the modular congruence of an integer expression is
- # tracked. This consists of a modulus and a modular_value, such that for
- # all possible values of expression, expression MOD modulus ==
- # modular_value.
- #
- # The modulus may be the special value "infinity" to indicate that the
- # expression's value is exactly modular_value; otherwise, it should be a
- # positive integer.
- #
- # A modulus of 1 places no constraints on the value.
- #
- # The modular_value should always be a nonnegative integer that is smaller
- # than the modulus.
- #
- # Note that this is specifically the *modulus*, which is not equivalent to
- # the value from C's '%' operator when the dividend is negative: in C, -7 %
- # 4 == -3, but the modular_value here would be 1. Python uses modulus: in
- # Python, -7 % 4 == 1.
- modulus = Optional(_text)
- modular_value = Optional(_text)
+ # For optimization, the modular congruence of an integer expression is
+ # tracked. This consists of a modulus and a modular_value, such that for
+ # all possible values of expression, expression MOD modulus ==
+ # modular_value.
+ #
+ # The modulus may be the special value "infinity" to indicate that the
+ # expression's value is exactly modular_value; otherwise, it should be a
+ # positive integer.
+ #
+ # A modulus of 1 places no constraints on the value.
+ #
+ # The modular_value should always be a nonnegative integer that is smaller
+ # than the modulus.
+ #
+ # Note that this is specifically the *modulus*, which is not equivalent to
+ # the value from C's '%' operator when the dividend is negative: in C, -7 %
+ # 4 == -3, but the modular_value here would be 1. Python uses modulus: in
+ # Python, -7 % 4 == 1.
+ modulus = Optional(_Text)
+ modular_value = Optional(_Text)
- # The minimum and maximum values of an integer are tracked and checked so
- # that Emboss can implement reliable arithmetic with no operations
- # overflowing either 64-bit unsigned or 64-bit signed 2's-complement
- # integers.
- #
- # Note that constant subexpressions are allowed to overflow, as long as the
- # final, computed constant value of the subexpression fits in a 64-bit
- # value.
- #
- # The minimum_value may take the value "-infinity", and the maximum_value
- # may take the value "infinity". These sentinel values indicate that
- # Emboss has no bound information for the Expression, and therefore the
- # Expression may only be evaluated during compilation; the back end should
- # never need to compile such an expression into the target language (e.g.,
- # C++).
- minimum_value = Optional(_text)
- maximum_value = Optional(_text)
+ # The minimum and maximum values of an integer are tracked and checked so
+ # that Emboss can implement reliable arithmetic with no operations
+ # overflowing either 64-bit unsigned or 64-bit signed 2's-complement
+ # integers.
+ #
+ # Note that constant subexpressions are allowed to overflow, as long as the
+ # final, computed constant value of the subexpression fits in a 64-bit
+ # value.
+ #
+ # The minimum_value may take the value "-infinity", and the maximum_value
+ # may take the value "infinity". These sentinel values indicate that
+ # Emboss has no bound information for the Expression, and therefore the
+ # Expression may only be evaluated during compilation; the back end should
+ # never need to compile such an expression into the target language (e.g.,
+ # C++).
+ minimum_value = Optional(_Text)
+ maximum_value = Optional(_Text)
@message
class BooleanType(Message):
- value = Optional(bool)
+ value = Optional(bool)
@message
class EnumType(Message):
- name = Optional(Reference)
- value = Optional(_text)
+ name = Optional(Reference)
+ value = Optional(_Text)
@message
class ExpressionType(Message):
- opaque = Optional(OpaqueType, "type")
- integer = Optional(IntegerType, "type")
- boolean = Optional(BooleanType, "type")
- enumeration = Optional(EnumType, "type")
+ opaque = Optional(OpaqueType, "type")
+ integer = Optional(IntegerType, "type")
+ boolean = Optional(BooleanType, "type")
+ enumeration = Optional(EnumType, "type")
@message
class Expression(Message):
- """ IR for an expression.
+ """IR for an expression.
- An Expression is a potentially-recursive data structure. It can either
- represent a leaf node (constant or reference) or an operation combining
- other Expressions (function)."""
+ An Expression is a potentially-recursive data structure. It can either
+ represent a leaf node (constant or reference) or an operation combining
+ other Expressions (function).
+ """
- constant = Optional(NumericConstant, "expression")
- constant_reference = Optional(Reference, "expression")
- function = Optional(Function, "expression")
- field_reference = Optional(FieldReference, "expression")
- boolean_constant = Optional(BooleanConstant, "expression")
- builtin_reference = Optional(Reference, "expression")
+ constant = Optional(NumericConstant, "expression")
+ constant_reference = Optional(Reference, "expression")
+ function = Optional(Function, "expression")
+ field_reference = Optional(FieldReference, "expression")
+ boolean_constant = Optional(BooleanConstant, "expression")
+ builtin_reference = Optional(Reference, "expression")
- type = Optional(ExpressionType)
- source_location = Optional(Location)
+ type = Optional(ExpressionType)
+ source_location = Optional(Location)
@message
class ArrayType(Message):
- """IR for an array type ("Int:8[12]" or "Message[2]" or "UInt[3][2]")."""
- base_type = Optional(lambda: Type)
+ """IR for an array type ("Int:8[12]" or "Message[2]" or "UInt[3][2]")."""
+ base_type = Optional(lambda: Type)
- element_count = Optional(Expression, "size")
- automatic = Optional(Empty, "size")
+ element_count = Optional(Expression, "size")
+ automatic = Optional(Empty, "size")
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class AtomicType(Message):
- """IR for a non-array type ("UInt" or "Foo(Version.SIX)")."""
- reference = Optional(Reference)
- runtime_parameter = Repeated(Expression)
- source_location = Optional(Location)
+ """IR for a non-array type ("UInt" or "Foo(Version.SIX)")."""
+ reference = Optional(Reference)
+ runtime_parameter = Repeated(Expression)
+ source_location = Optional(Location)
@message
class Type(Message):
- """IR for a type reference ("UInt", "Int:8[12]", etc.)."""
- atomic_type = Optional(AtomicType, "type")
- array_type = Optional(ArrayType, "type")
+ """IR for a type reference ("UInt", "Int:8[12]", etc.)."""
+ atomic_type = Optional(AtomicType, "type")
+ array_type = Optional(ArrayType, "type")
- size_in_bits = Optional(Expression)
- source_location = Optional(Location)
+ size_in_bits = Optional(Expression)
+ source_location = Optional(Location)
@message
class AttributeValue(Message):
- """IR for a attribute value."""
- # TODO(bolms): Make String a type of Expression, and replace
- # AttributeValue with Expression.
- expression = Optional(Expression, "value")
- string_constant = Optional(String, "value")
+ """IR for a attribute value."""
+ # TODO(bolms): Make String a type of Expression, and replace
+ # AttributeValue with Expression.
+ expression = Optional(Expression, "value")
+ string_constant = Optional(String, "value")
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class Attribute(Message):
- """IR for a [name = value] attribute."""
- name = Optional(Word)
- value = Optional(AttributeValue)
- back_end = Optional(Word)
- is_default = Optional(bool)
- source_location = Optional(Location)
+ """IR for a [name = value] attribute."""
+ name = Optional(Word)
+ value = Optional(AttributeValue)
+ back_end = Optional(Word)
+ is_default = Optional(bool)
+ source_location = Optional(Location)
@message
class WriteTransform(Message):
- """IR which defines an expression-based virtual field write scheme.
-
- E.g., for a virtual field like `x_plus_one`:
-
- struct Foo:
- 0 [+1] UInt x
- let x_plus_one = x + 1
-
- ... the `WriteMethod` would be `transform`, with `$logical_value - 1` for
- `function_body` and `x` for `destination`."""
+ """IR which defines an expression-based virtual field write scheme.
- function_body = Optional(Expression)
- destination = Optional(FieldReference)
+ E.g., for a virtual field like `x_plus_one`:
+
+ struct Foo:
+ 0 [+1] UInt x
+ let x_plus_one = x + 1
+
+ ... the `WriteMethod` would be `transform`, with `$logical_value - 1` for
+ `function_body` and `x` for `destination`.
+ """
+
+ function_body = Optional(Expression)
+ destination = Optional(FieldReference)
@message
class WriteMethod(Message):
- """IR which defines the method used for writing to a virtual field."""
+ """IR which defines the method used for writing to a virtual field."""
- # A physical Field can be written directly.
- physical = Optional(bool, "method")
+ # A physical Field can be written directly.
+ physical = Optional(bool, "method")
- # A read_only Field cannot be written.
- read_only = Optional(bool, "method")
+ # A read_only Field cannot be written.
+ read_only = Optional(bool, "method")
- # An alias is a direct, untransformed forward of another field; it can be
- # implemented by directly returning a reference to the aliased field.
- #
- # Aliases are the only kind of virtual field that may have an opaque type.
- alias = Optional(FieldReference, "method")
+ # An alias is a direct, untransformed forward of another field; it can be
+ # implemented by directly returning a reference to the aliased field.
+ #
+ # Aliases are the only kind of virtual field that may have an opaque type.
+ alias = Optional(FieldReference, "method")
- # A transform is a way of turning a logical value into a value which should
- # be written to another field: A virtual field like `let y = x + 1` would
- # have a transform WriteMethod to subtract 1 from the new `y` value, and
- # write that to `x`.
- transform = Optional(WriteTransform, "method")
+ # A transform is a way of turning a logical value into a value which should
+ # be written to another field: A virtual field like `let y = x + 1` would
+ # have a transform WriteMethod to subtract 1 from the new `y` value, and
+ # write that to `x`.
+ transform = Optional(WriteTransform, "method")
@message
class FieldLocation(Message):
- """IR for a field location."""
- start = Optional(Expression)
- size = Optional(Expression)
- source_location = Optional(Location)
+ """IR for a field location."""
+ start = Optional(Expression)
+ size = Optional(Expression)
+ source_location = Optional(Location)
@message
class Field(Message):
- """IR for a field in a struct definition.
+ """IR for a field in a struct definition.
- There are two kinds of Field: physical fields have location and (physical)
- type; virtual fields have read_transform. Although there are differences,
- in many situations physical and virtual fields are treated the same way,
- and they can be freely intermingled in the source file."""
- location = Optional(FieldLocation) # The physical location of the field.
- type = Optional(Type) # The physical type of the field.
+ There are two kinds of Field: physical fields have location and (physical)
+ type; virtual fields have read_transform. Although there are differences,
+ in many situations physical and virtual fields are treated the same way,
+ and they can be freely intermingled in the source file.
+ """
- read_transform = Optional(Expression) # The value of a virtual field.
+ location = Optional(FieldLocation) # The physical location of the field.
+ type = Optional(Type) # The physical type of the field.
- # How this virtual field should be written.
- write_method = Optional(WriteMethod)
+ read_transform = Optional(Expression) # The value of a virtual field.
- name = Optional(NameDefinition) # The name of the field.
- abbreviation = Optional(Word) # An optional short name for the field, only
- # visible inside the enclosing bits/struct.
- attribute = Repeated(Attribute) # Field-specific attributes.
- documentation = Repeated(Documentation) # Field-specific documentation.
+ # How this virtual field should be written.
+ write_method = Optional(WriteMethod)
- # The field only exists when existence_condition evaluates to true. For
- # example:
- #
- # struct Message:
- # 0 [+4] UInt length
- # 4 [+8] MessageType message_type
- # if message_type == MessageType.FOO:
- # 8 [+length] Foo foo
- # if message_type == MessageType.BAR:
- # 8 [+length] Bar bar
- # 8+length [+4] UInt crc
- #
- # For length, message_type, and crc, existence_condition will be
- # "boolean_constant { value: true }"
- #
- # For "foo", existence_condition will be:
- # function { function: EQUALITY
- # args: [reference to message_type]
- # args: { [reference to MessageType.FOO] } }
- #
- # The "bar" field will have a similar existence_condition to "foo":
- # function { function: EQUALITY
- # args: [reference to message_type]
- # args: { [reference to MessageType.BAR] } }
- #
- # When message_type is MessageType.BAR, the Message struct does not contain
- # field "foo", and vice versa for message_type == MessageType.FOO and field
- # "bar": those fields only conditionally exist in the structure.
- #
- # TODO(bolms): Document conditional fields better, and replace some of this
- # explanation with a reference to the documentation.
- existence_condition = Optional(Expression)
- source_location = Optional(Location)
+ name = Optional(NameDefinition) # The name of the field.
+ abbreviation = Optional(Word) # An optional short name for the field, only
+ # visible inside the enclosing bits/struct.
+ attribute = Repeated(Attribute) # Field-specific attributes.
+ documentation = Repeated(Documentation) # Field-specific documentation.
+
+ # The field only exists when existence_condition evaluates to true. For
+ # example:
+ #
+ # struct Message:
+ # 0 [+4] UInt length
+ # 4 [+8] MessageType message_type
+ # if message_type == MessageType.FOO:
+ # 8 [+length] Foo foo
+ # if message_type == MessageType.BAR:
+ # 8 [+length] Bar bar
+ # 8+length [+4] UInt crc
+ #
+ # For length, message_type, and crc, existence_condition will be
+ # "boolean_constant { value: true }"
+ #
+ # For "foo", existence_condition will be:
+ # function { function: EQUALITY
+ # args: [reference to message_type]
+ # args: { [reference to MessageType.FOO] } }
+ #
+ # The "bar" field will have a similar existence_condition to "foo":
+ # function { function: EQUALITY
+ # args: [reference to message_type]
+ # args: { [reference to MessageType.BAR] } }
+ #
+ # When message_type is MessageType.BAR, the Message struct does not contain
+ # field "foo", and vice versa for message_type == MessageType.FOO and field
+ # "bar": those fields only conditionally exist in the structure.
+ #
+ # TODO(bolms): Document conditional fields better, and replace some of this
+ # explanation with a reference to the documentation.
+ existence_condition = Optional(Expression)
+ source_location = Optional(Location)
@message
class Structure(Message):
- """IR for a bits or struct definition."""
- field = Repeated(Field)
+ """IR for a bits or struct definition."""
+ field = Repeated(Field)
- # The fields in `field` are listed in the order they appear in the original
- # .emb.
- #
- # For text format output, this can lead to poor results. Take the following
- # struct:
- #
- # struct Foo:
- # b [+4] UInt a
- # 0 [+4] UInt b
- #
- # Here, the location of `a` depends on the current value of `b`. Because of
- # this, if someone calls
- #
- # emboss::UpdateFromText(foo_view, "{ a: 10, b: 4 }");
- #
- # then foo_view will not be updated the way one would expect: if `b`'s value
- # was something other than 4 to start with, then `UpdateFromText` will write
- # the 10 to some other location, then update `b` to 4.
- #
- # To avoid surprises, `emboss::DumpAsText` should return `"{ b: 4, a: 10
- # }"`.
- #
- # The `fields_in_dependency_order` field provides a permutation of `field`
- # such that each field appears after all of its dependencies. For example,
- # `struct Foo`, above, would have `{ 1, 0 }` in
- # `fields_in_dependency_order`.
- #
- # The exact ordering of `fields_in_dependency_order` is not guaranteed, but
- # some effort is made to keep the order close to the order fields are listed
- # in the original `.emb` file. In particular, if the ordering 0, 1, 2, 3,
- # ... satisfies dependency ordering, then `fields_in_dependency_order` will
- # be `{ 0, 1, 2, 3, ... }`.
- fields_in_dependency_order = Repeated(int)
+ # The fields in `field` are listed in the order they appear in the original
+ # .emb.
+ #
+ # For text format output, this can lead to poor results. Take the following
+ # struct:
+ #
+ # struct Foo:
+ # b [+4] UInt a
+ # 0 [+4] UInt b
+ #
+ # Here, the location of `a` depends on the current value of `b`. Because of
+ # this, if someone calls
+ #
+ # emboss::UpdateFromText(foo_view, "{ a: 10, b: 4 }");
+ #
+ # then foo_view will not be updated the way one would expect: if `b`'s value
+ # was something other than 4 to start with, then `UpdateFromText` will write
+ # the 10 to some other location, then update `b` to 4.
+ #
+ # To avoid surprises, `emboss::DumpAsText` should return `"{ b: 4, a: 10
+ # }"`.
+ #
+ # The `fields_in_dependency_order` field provides a permutation of `field`
+ # such that each field appears after all of its dependencies. For example,
+ # `struct Foo`, above, would have `{ 1, 0 }` in
+ # `fields_in_dependency_order`.
+ #
+ # The exact ordering of `fields_in_dependency_order` is not guaranteed, but
+ # some effort is made to keep the order close to the order fields are listed
+ # in the original `.emb` file. In particular, if the ordering 0, 1, 2, 3,
+ # ... satisfies dependency ordering, then `fields_in_dependency_order` will
+ # be `{ 0, 1, 2, 3, ... }`.
+ fields_in_dependency_order = Repeated(int)
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class External(Message):
- """IR for an external type declaration."""
- # Externals have no values other than name and attribute list, which are
- # common to all type definitions.
+ """IR for an external type declaration."""
+ # Externals have no values other than name and attribute list, which are
+ # common to all type definitions.
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class EnumValue(Message):
- """IR for a single value within an enumerated type."""
- name = Optional(NameDefinition) # The name of the enum value.
- value = Optional(Expression) # The value of the enum value.
- documentation = Repeated(Documentation) # Value-specific documentation.
+ """IR for a single value within an enumerated type."""
+ name = Optional(NameDefinition) # The name of the enum value.
+ value = Optional(Expression) # The value of the enum value.
+ documentation = Repeated(Documentation) # Value-specific documentation.
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class Enum(Message):
- """IR for an enumerated type definition."""
- value = Repeated(EnumValue)
- source_location = Optional(Location)
+ """IR for an enumerated type definition."""
+ value = Repeated(EnumValue)
+ source_location = Optional(Location)
@message
class Import(Message):
- """IR for an import statement in a module."""
- file_name = Optional(String) # The file to import.
- local_name = Optional(Word) # The name to use within this module.
- source_location = Optional(Location)
+ """IR for an import statement in a module."""
+ file_name = Optional(String) # The file to import.
+ local_name = Optional(Word) # The name to use within this module.
+ source_location = Optional(Location)
@message
class RuntimeParameter(Message):
- name = Optional(NameDefinition) # The name of the parameter.
- type = Optional(ExpressionType) # The type of the parameter.
+ """IR for a runtime parameter definition."""
+ name = Optional(NameDefinition) # The name of the parameter.
+ type = Optional(ExpressionType) # The type of the parameter.
- # For convenience and readability, physical types may be used in the .emb
- # source instead of a full expression type. That way, users can write
- # something like:
- #
- # struct Foo(version :: UInt:8):
- #
- # instead of:
- #
- # struct Foo(version :: {$int x |: 0 <= x <= 255}):
- #
- # In these cases, physical_type_alias holds the user-supplied type, and type
- # is filled in after initial parsing is finished.
- #
- # TODO(bolms): Actually implement the set builder type notation.
- physical_type_alias = Optional(Type)
+ # For convenience and readability, physical types may be used in the .emb
+ # source instead of a full expression type. That way, users can write
+ # something like:
+ #
+ # struct Foo(version :: UInt:8):
+ #
+ # instead of:
+ #
+ # struct Foo(version :: {$int x |: 0 <= x <= 255}):
+ #
+ # In these cases, physical_type_alias holds the user-supplied type, and type
+ # is filled in after initial parsing is finished.
+ #
+ # TODO(bolms): Actually implement the set builder type notation.
+ physical_type_alias = Optional(Type)
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class TypeDefinition(Message):
- """Container IR for a type definition (struct, union, etc.)"""
+ """Container IR for a type definition (struct, union, etc.)"""
- # The "addressable unit" is the size of the smallest unit that can be read
- # from the backing store that this type expects. For `struct`s, this is
- # BYTE; for `enum`s and `bits`, this is BIT, and for `external`s it depends
- # on the specific type.
+ # The "addressable unit" is the size of the smallest unit that can be read
+ # from the backing store that this type expects. For `struct`s, this is
+ # BYTE; for `enum`s and `bits`, this is BIT, and for `external`s it depends
+ # on the specific type.
- NONE = 0
- BIT = 1
- BYTE = 8
+ NONE = 0
+ BIT = 1
+ BYTE = 8
- external = Optional(External, "type")
- enumeration = Optional(Enum, "type")
- structure = Optional(Structure, "type")
+ external = Optional(External, "type")
+ enumeration = Optional(Enum, "type")
+ structure = Optional(Structure, "type")
- name = Optional(NameDefinition) # The name of the type.
- attribute = Repeated(Attribute) # All attributes attached to the type.
- documentation = Repeated(Documentation) # Docs for the type.
- subtype = Repeated(lambda: TypeDefinition) # Subtypes of this type.
- addressable_unit = Optional(
- int, names=lambda x: getattr(TypeDefinition, x))
+ name = Optional(NameDefinition) # The name of the type.
+ attribute = Repeated(Attribute) # All attributes attached to the type.
+ documentation = Repeated(Documentation) # Docs for the type.
+ # pylint:disable=undefined-variable
+ subtype = Repeated(lambda: TypeDefinition) # Subtypes of this type.
+ addressable_unit = Optional(
+ int, decode_names=lambda x: getattr(TypeDefinition, x))
- # If the type requires parameters at runtime, these are its parameters.
- # These are currently only allowed on structures, but in the future they
- # should be allowed on externals.
- runtime_parameter = Repeated(RuntimeParameter)
+ # If the type requires parameters at runtime, these are its parameters.
+ # These are currently only allowed on structures, but in the future they
+ # should be allowed on externals.
+ runtime_parameter = Repeated(RuntimeParameter)
- source_location = Optional(Location)
+ source_location = Optional(Location)
@message
class Module(Message):
- """The IR for an individual Emboss module (file)."""
- attribute = Repeated(Attribute) # Module-level attributes.
- type = Repeated(TypeDefinition) # Module-level type definitions.
- documentation = Repeated(Documentation) # Module-level docs.
- foreign_import = Repeated(Import) # Other modules imported.
- source_location = Optional(Location) # Source code covered by this IR.
- source_file_name = Optional(_text) # Name of the source file.
+ """The IR for an individual Emboss module (file)."""
+ attribute = Repeated(Attribute) # Module-level attributes.
+ type = Repeated(TypeDefinition) # Module-level type definitions.
+ documentation = Repeated(Documentation) # Module-level docs.
+ foreign_import = Repeated(Import) # Other modules imported.
+ source_location = Optional(Location) # Source code covered by this IR.
+ source_file_name = Optional(_Text) # Name of the source file.
@message
class EmbossIr(Message):
- """The top-level IR for an Emboss module and all of its dependencies."""
- # All modules. The first entry will be the main module; back ends should
- # generate code corresponding to that module. The second entry will be the
- # prelude module.
- module = Repeated(Module)
+ """The top-level IR for an Emboss module and all of its dependencies."""
+ # All modules. The first entry will be the main module; back ends should
+ # generate code corresponding to that module. The second entry will be the
+ # prelude module.
+ module = Repeated(Module)
_initialize_deferred_specs()