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()