correction to ra
[openpower-isa.git] / src / openpower / decoder / power_fields.py
index f168cab8f52a76301e5972cb5279ddf9bf259af0..57678d071c639d63b3aee7721658d6dfab48e775 100644 (file)
@@ -1,13 +1,14 @@
 from collections import namedtuple
 
-import operator as _operator
-import functools as _functools
+import operator
+import functools
 
-from openpower.decoder.power_enums import find_wiki_file as _find_wiki_file
+from openpower.decoder.power_enums import find_wiki_file
 from openpower.decoder.selectable_int import (
-    SelectableInt as _SelectableInt,
-    BitRange as _BitRange,
-    selectconcat as _selectconcat,
+    SelectableInt,
+    BitRange,
+    selectconcat,
+    selectltu,
 )
 
 
@@ -31,10 +32,10 @@ class Descriptor:
         self.__cls(storage=instance.storage).assign(value)
 
 
-@_functools.total_ordering
+@functools.total_ordering
 class Reference:
     def __init__(self, storage, *args, **kwargs):
-        if not isinstance(storage, _SelectableInt):
+        if not isinstance(storage, SelectableInt):
             raise ValueError(storage)
 
         self.storage = storage
@@ -47,17 +48,17 @@ class Reference:
 
     def __binary_operator(self, op, other):
         span = dict.fromkeys(self.__class__.span).keys()
-        lhs = _selectconcat(*(self.storage[bit] for bit in span))
+        lhs = selectconcat(*(self.storage[bit] for bit in span))
 
         if isinstance(other, Reference):
             span = dict.fromkeys(other.__class__.span).keys()
-            rhs = _selectconcat(*(other.storage[bit] for bit in span))
+            rhs = selectconcat(*(other.storage[bit] for bit in span))
         elif isinstance(other, int):
             bits = len(self.__class__)
             if other.bit_length() > bits:
                 raise OverflowError(other)
-            rhs = _SelectableInt(value=other, bits=bits)
-        elif isinstance(other, _SelectableInt):
+            rhs = SelectableInt(value=other, bits=bits)
+        elif isinstance(other, SelectableInt):
             rhs = other
         else:
             raise ValueError(other)
@@ -65,14 +66,17 @@ class Reference:
         return op(lhs, rhs)
 
     def __lt__(self, other):
-        return self.__binary_operator(_operator.lt, other)
+        return self.__binary_operator(selectltu, other)
 
     def __eq__(self, other):
-        return self.__binary_operator(_operator.eq, other)
+        return self.__binary_operator(operator.eq, other)
+
+    def __bool__(self):
+        return bool(int(self))
 
     def __int__(self):
         span = dict.fromkeys(self.__class__.span).keys()
-        return int(_selectconcat(*(self.storage[bit] for bit in span)))
+        return int(selectconcat(*(self.storage[bit] for bit in span)))
 
     def __index__(self):
         return int(self).__index__()
@@ -83,21 +87,40 @@ class Reference:
 
     @storage.setter
     def storage(self, storage):
-        if not isinstance(storage, _SelectableInt):
+        if not isinstance(storage, SelectableInt):
             raise ValueError(storage)
 
         self.__storage = storage
 
-    def assign(self, value):
-        if isinstance(value, int):
-            bits = len(self.__class__)
-            value = _SelectableInt(value=value, bits=bits)
-        if not isinstance(value, _SelectableInt):
+    def assign(self, value, bits=None):
+        if bits is None:
+            bits = range(len(self.__class__))
+        elif isinstance(bits, int):
+            bits = (bits,)
+        elif isinstance(bits, slice):
+            assert bits.step is None or bits.step == 1
+            bits = range(bits.start, bits.stop)
+        bits = tuple(bits)
+
+        if isinstance(value, (int, self.__class__)):
+            value = int(value)
+            if value.bit_length() > len(bits):
+                raise OverflowError(value)
+            value = SelectableInt(value=value, bits=len(bits))
+        if not isinstance(value, SelectableInt):
             raise ValueError(value)
+        if value.bits != len(bits):
+            raise OverflowError(value)
 
-        span = frozenset(self.__class__.span)
-        for (src_bit, dst_bit) in enumerate(span):
-            self.storage[dst_bit] = value[src_bit]
+        span = tuple(self.__class__.span)
+        mapping = dict(enumerate(span))
+        for (src, bit) in enumerate(bits):
+            if src >= value.bits:
+                raise OverflowError(src)
+            dst = mapping.get(bit)
+            if dst is None:
+                raise OverflowError(bit)
+            self.storage[dst] = value[src]
 
 
 class FieldMeta(type):
@@ -116,11 +139,6 @@ class FieldMeta(type):
 
         return super().__new__(metacls, clsname, bases, ns)
 
-    def __getitem__(cls, size):
-        clsname = f"{cls.__name__}[{size}]"
-        items = ((Field,) * size)
-        return ArrayMeta(clsname, (Array,), {}, items=items)
-
     def __repr__(cls):
         if not cls.__members__:
             return cls.__name__
@@ -132,6 +150,19 @@ class FieldMeta(type):
     def __len__(cls):
         return len(cls.__members__)
 
+    def __getitem__(cls, selector):
+        if isinstance(selector, int):
+            selector = (selector,)
+
+        items = []
+        for idx in selector:
+            if not isinstance(idx, int):
+                raise ValueError(selector)
+            item = cls.__members__[idx]
+            items.append(item)
+
+        return cls.__class__(cls.__name__, (Field,), {}, items=items)
+
     def remap(cls, scheme):
         if isinstance(scheme, type) and issubclass(scheme, Mapping):
             scheme = range(len(scheme))
@@ -144,22 +175,18 @@ class FieldMeta(type):
             rlen = f"len({cls.__name__})"
             raise RemapError(f"{llen} != {rlen}")
 
+        ns = {}
+        ns["__doc__"] = cls.__doc__
         items = map(lambda item: scheme.__members__[item], cls)
 
-        return cls.__class__(cls.__name__, (cls,), {}, items=items)
+        return cls.__class__(cls.__name__, (cls,), ns, items=items)
 
     @property
     def span(cls):
         return cls.__members__
 
-    def traverse(cls, path):
-        yield (path, cls.__members__)
-
 
 class Field(Reference, metaclass=FieldMeta):
-    def __len__(self):
-        return self.__class__.__len__()
-
     def __repr__(self):
         return f"[{len(self.__class__)}]0x{int(self):x}"
 
@@ -167,112 +194,22 @@ class Field(Reference, metaclass=FieldMeta):
         for bit in self.__class__:
             yield self.storage[bit]
 
-
-class ArrayMeta(type):
-    def __new__(metacls, clsname, bases, ns, items=()):
-        assert "__members__" not in ns
-
-        members = []
-        for item in items:
-            if not (isinstance(item, type) and issubclass(item, Field)):
-                item = FieldMeta("Field", (Field,), {}, items=item)
-            members.append(item)
-
-        ns["__members__"] = tuple(members)
-
-        return super().__new__(metacls, clsname, bases, ns)
-
-    def __repr__(cls):
-        if not cls.__members__:
-            return cls.__name__
-        return f"{cls.__name__}{cls.__members__!r}"
-
-    def __iter__(cls):
-        yield from enumerate(cls.__members__)
-
-    def __len__(cls):
-        length = 0
-        for field in cls.__members__:
-            length += len(field)
-        return length
-
-    def remap(cls, scheme):
-        scheme_md = []
-        scheme_sd = []
-
-        for item in scheme:
-            if not isinstance(item, int):
-                scheme_md.append(item)
-            else:
-                scheme_sd.append(item)
-
-        if scheme_md and scheme_sd:
-            raise ValueError(scheme)
-
-        def remap_md(scheme):
-            scheme = cls.__class__(cls.__name__, (cls,), {}, items=scheme)
-            if len(cls) == 0:
-                if len(cls.__members__) != len(scheme.__members__):
-                    llen = f"len(scheme.__members__)"
-                    rlen = f"len({cls.__name__}.__members__)"
-                    raise RemapError(f"{llen} != {rlen}")
-                return scheme
-            elif len(scheme) != len(cls):
-                llen = f"len(scheme)"
-                rlen = f"len({cls.__name__})"
-                raise RemapError(f"{llen} != {rlen}")
-
-            items = []
-            for (idx, field) in enumerate(cls):
-                try:
-                    item = field.remap(scheme.__members__[idx])
-                except RemapError as error:
-                    raise RemapError(f"[{idx}]: {error}")
-                items.append(item)
-
-            return cls.__class__(cls.__name__, (cls,), {}, items=items)
-
-        def remap_sd(scheme):
-            items = tuple(item.remap(scheme) for item in cls.__members__)
-            return cls.__class__(cls.__name__, (cls,), {}, items=items)
-
-        if scheme_md:
-            return remap_md(scheme_md)
-        else:
-            return remap_sd(scheme_sd)
-
-    @property
-    def span(cls):
-        for field in cls.__members__:
-            yield from field.span
-
-    def traverse(cls, path=""):
-        for (idx, field) in cls:
-            yield from field.traverse(path=f"{path}[{idx}]")
-
-
-class Array(Reference, metaclass=ArrayMeta):
-    def __init__(self, storage):
-        members = []
-        for (idx, cls) in self.__class__:
-            members.append(cls(storage))
-
-        self.__members = tuple(members)
-
-        return super().__init__(storage)
-
-    def __repr__(self):
-        items = tuple(f"[{idx}]={field!r}" for (idx, field) in self)
-        return f"[{', '.join(items)}]"
-
-    def __iter__(self):
-        yield from enumerate(self.__members)
-
     def __getitem__(self, key):
-        return self.__members[key]
+        if isinstance(key, int):
+            bit = self.storage[self.__class__.__members__[key]]
+            return SelectableInt(value=bit, bits=1)
+        if isinstance(key, slice):
+            assert key.step is None or key.step == 1
+            key = range(key.start, key.stop)
+
+        return selectconcat(*(self[bit] for bit in key))
 
     def __setitem__(self, key, value):
-        self.__members[key].assign(value)
+        return self.assign(value=value, bits=key)
+
+    @classmethod
+    def traverse(cls, path):
+        yield (path, cls.__members__)
 
 
 class MappingMeta(type):
@@ -285,7 +222,7 @@ class MappingMeta(type):
 
         for (name, cls) in ns.get("__annotations__", {}).items():
             if not (isinstance(cls, type) and
-                    issubclass(cls, (Mapping, Array, Field))):
+                    issubclass(cls, (Mapping, Field))):
                 raise ValueError(f"{clsname}.{name}: {cls!r}")
 
             if name in ns:
@@ -294,9 +231,8 @@ class MappingMeta(type):
                 except RemapError as error:
                     raise RemapError(f"{name}: {error}")
             else:
-                if cls in (Array, Field):
-                    raise ValueError(f"{clsname}.{name}: " + \
-                        "base class without initializer")
+                if cls is Field:
+                    raise ValueError(f"{clsname}.{name}: missing initializer")
                 members[name] = cls
 
         ns["__members__"] = members
@@ -317,6 +253,9 @@ class MappingMeta(type):
             length = max(length, len(field))
         return length
 
+    def __getitem__(cls, selector):
+        return cls.__members__["_"][selector]
+
     def remap(cls, scheme):
         ns = {}
         annotations = {}
@@ -324,6 +263,7 @@ class MappingMeta(type):
         for (name, field) in cls:
             annotations[name] = field.remap(scheme)
         ns["__annotations__"] = annotations
+        ns["__doc__"] = cls.__doc__
 
         return cls.__class__(cls.__name__, (cls,), ns)
 
@@ -332,15 +272,6 @@ class MappingMeta(type):
         for field in cls.__members__.values():
             yield from field.span
 
-    def traverse(cls, path=""):
-        for (name, field) in cls:
-            if name == "_":
-                yield from field.traverse(path=path)
-            elif path == "":
-                yield from field.traverse(path=name)
-            else:
-                yield from field.traverse(path=f"{path}.{name}")
-
 
 class Mapping(Reference, metaclass=MappingMeta):
     def __init__(self, storage, **kwargs):
@@ -361,9 +292,28 @@ class Mapping(Reference, metaclass=MappingMeta):
 
     def __getitem__(self, key):
         if isinstance(key, (int, slice, list, tuple, range)):
-            return self.storage[key]
+            return self["_"].__getitem__(key)
+
+        return self.__members.__getitem__(key)
+
+    def __setitem__(self, key, value):
+        if isinstance(key, (int, slice, list, tuple, range)):
+            return self["_"].assign(value=value, bits=key)
+
+        return self.assign(value=value, bits=key)
+
+    def __getattr__(self, key):
+        raise AttributeError(key)
 
-        return self.__members[key]
+    @classmethod
+    def traverse(cls, path):
+        for (name, member) in cls.__members__.items():
+            if name == "_":
+                yield from member.traverse(path=path)
+            elif path == "":
+                yield from member.traverse(path=name)
+            else:
+                yield from member.traverse(path=f"{path}.{name}")
 
 
 def decode_instructions(form):
@@ -461,7 +411,7 @@ def decode_form(form):
 
 class DecodeFields:
 
-    def __init__(self, bitkls=_BitRange, bitargs=(), fname=None,
+    def __init__(self, bitkls=BitRange, bitargs=(), fname=None,
                  name_on_wiki=None):
         self.bitkls = bitkls
         self.bitargs = bitargs
@@ -469,7 +419,7 @@ class DecodeFields:
             assert name_on_wiki is None
             fname = "fields.txt"
             name_on_wiki = "fields.text"
-        self.fname = _find_wiki_file(name_on_wiki)
+        self.fname = find_wiki_file(name_on_wiki)
 
     @property
     def form_names(self):
@@ -539,7 +489,7 @@ class DecodeFields:
         #print ("decode", txt)
         forms = {}
         reading_data = False
-        for l in txt:
+        for lineno, l in enumerate(txt):
             l = l.strip()
             if len(l) == 0:
                 continue
@@ -547,7 +497,20 @@ class DecodeFields:
                 if l[0] == '#':
                     reading_data = False
                 else:
-                    forms[heading].append(l)
+                    form = forms[heading]
+                    form.append(l)
+                    if len(form) <= 1:
+                        continue
+                    # check separators line up with header
+                    for i, ch in enumerate(l):
+                        if ch != '|':
+                            continue
+                        if i >= len(form[0]) or form[0][i] != '|':
+                            col = len(txt[lineno]) - len(txt[lineno].lstrip())
+                            col += i + 1
+                            raise SyntaxError("form line field separator ('|') "
+                                    "with no corresponding separator in header",
+                                    (self.fname, lineno + 1, col, txt[lineno]))
             if not reading_data:
                 assert l[0] == '#'
                 heading = l[1:].strip()