diff options
Diffstat (limited to 'bits.py')
| -rw-r--r-- | bits.py | 108 |
1 files changed, 70 insertions, 38 deletions
@@ -2,6 +2,7 @@ import re from operator import __and__ from functools import reduce + class WordBase(object): def slice_allowempty(self, range): if not isinstance(range, slice): @@ -18,15 +19,16 @@ class WordBase(object): return BitsUnion(self, other) raise NotImplementedError() + class BitsUnion(WordBase): def __init__(self, *parts): self.parts = parts def __repr__(self): - return "BitsUnion({})".format(', '.join(repr(part) for part in self.parts)) + return "BitsUnion({})".format(", ".join(repr(part) for part in self.parts)) def as_value(self, **kwargs): - """ reduce down to a single Bitfield value. + """reduce down to a single Bitfield value. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).as_value() Bitfield(0x1234, 16) @@ -37,7 +39,7 @@ class BitsUnion(WordBase): @property def size(self): - """ get the total size. + """get the total size. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4)).size 16 @@ -47,7 +49,7 @@ class BitsUnion(WordBase): return sum(part.size for part in self.parts) def __getitem__(self, range): - """ slice a union given hi and lo bit indices. + """slice a union given hi and lo bit indices. >>> BitsUnion(Bitfield(0x12, 8), Bitfield(0x3, 4), Bitfield(0x4, 4))[11:4] Bitfield(0x23, 8) @@ -62,7 +64,9 @@ class BitsUnion(WordBase): if hi < lo: raise ValueError("cant slice reverse range") elif lo < 0 or hi >= size: - raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)) + raise ValueError( + "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) + ) i = size result = Bitfield(0, 0) @@ -91,6 +95,7 @@ class BitsUnion(WordBase): return BitsUnion(*self.parts, other) raise NotImplementedError() + class Bitfield(WordBase): def __init__(self, val, size, extra=()): if val < 0: @@ -98,7 +103,9 @@ class Bitfield(WordBase): max = -1 - min if val > max or val < min: - raise ValueError("value {} out of i{} range [{};{}]".format(val, size, min, max)) + raise ValueError( + "value {} out of i{} range [{};{}]".format(val, size, min, max) + ) if val < 0: val = (1 << size) + val @@ -108,7 +115,7 @@ class Bitfield(WordBase): self.extra = extra def __repr__(self): - """ format for debugging. + """format for debugging. >>> Bitfield(0, 8) Bitfield(0x0, 8) >>> Bitfield(16, 8) @@ -120,13 +127,13 @@ class Bitfield(WordBase): >>> Bitfield(3, 4, extra=('hello',)) Bitfield(0x3, 4, extra=('hello',)) """ - extra = '' + extra = "" if self.extra: extra = ", extra={!r}".format(self.extra) return "Bitfield(0x{:x}, {}{})".format(self.val, self.size, extra) def __str__(self): - """ format as a word. + """format as a word. >>> str(Bitfield(0, 8)) '00/8' >>> str(Bitfield(16, 8)) @@ -142,14 +149,14 @@ class Bitfield(WordBase): """ digits = 1 + ((self.size - 1) // 4) base = "{:x}".format(self.val) - base = (digits - len(base)) * '0' + base - return '/'.join([base, *self.extra, str(self.size)]) + base = (digits - len(base)) * "0" + base + return "/".join([base, *self.extra, str(self.size)]) def as_value(self, **kwargs): return self def __getitem__(self, range): - """ slice a bitfield given hi and lo bit indices. + """slice a bitfield given hi and lo bit indices. >>> Bitfield(0x7f, 8)[7:4] Bitfield(0x7, 4) @@ -182,14 +189,16 @@ class Bitfield(WordBase): if hi < lo: raise ValueError("cant slice reverse range") elif lo < 0 or hi >= self.size: - raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)) + raise ValueError( + "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) + ) size = hi - lo + 1 val = (self.val >> lo) & ((1 << size) - 1) return Bitfield(val, size) def __and__(self, other): - """ concatenate multiple bitfields. + """concatenate multiple bitfields. >>> Bitfield(0b00, 2) & Bitfield(0b10, 2) Bitfield(0x2, 4) @@ -202,9 +211,12 @@ class Bitfield(WordBase): return BitsUnion(self, other) raise NotImplementedError() + global_slice = slice + + class LabelRef(WordBase): - mode_re = re.compile(r'^(imm|off)(\d+)?$') + mode_re = re.compile(r"^(imm|off)(\d+)?$") def __init__(self, label, offset, mode, slice=None): if isinstance(mode, str): @@ -214,11 +226,11 @@ class LabelRef(WordBase): self.label = label self.mode = mode - self.slice = slice or global_slice(self.mode[1]-1, 0) + self.slice = slice or global_slice(self.mode[1] - 1, 0) self.offset = offset def __repr__(self): - """ format for debugging. + """format for debugging. >>> LabelRef("label", 0, ("imm", 32), slice(31,31)) LabelRef('label', 0, ('imm', 32), 31:31) @@ -229,10 +241,12 @@ class LabelRef(WordBase): >>> LabelRef("test", 0, "off") LabelRef('test', 0, ('off', 32), 31:0) """ - return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format(self.label, self.offset, self.mode, self.slice) + return "LabelRef({0!r}, {1}, {2}, {3.start}:{3.stop})".format( + self.label, self.offset, self.mode, self.slice + ) def __str__(self): - """ format as a word. + """format as a word. >>> str(LabelRef("label", 0, ("imm", 32), slice(31,31))) 'label/imm32/[31:31]' @@ -243,18 +257,20 @@ class LabelRef(WordBase): >>> str(LabelRef("test", -12, "off")) 'test-12/off32/[31:0]' """ - offset = '' + offset = "" if self.offset != 0: offset = "{:+}".format(self.offset) - return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format(self.label, offset, self.mode, self.slice) + return "{0}{1}/{2[0]}{2[1]}/[{3.start}:{3.stop}]".format( + self.label, offset, self.mode, self.slice + ) @property def size(self): return self.slice.start - self.slice.stop + 1 def as_value(self, labels={}, **kwargs): - """ resolve the reference. + """resolve the reference. >>> labels = {'test': 0xabcd1234, 'main': 0x8000} >>> LabelRef("test", 0, "imm").as_value(labels=labels) @@ -287,15 +303,17 @@ class LabelRef(WordBase): value = labels[self.label] + self.offset - if self.mode[0] == 'off': - assert 'pc' in kwargs, TypeError("'pc' is required to resolve offset-references") - value = value - kwargs['pc'] + if self.mode[0] == "off": + assert "pc" in kwargs, TypeError( + "'pc' is required to resolve offset-references" + ) + value = value - kwargs["pc"] field = Bitfield(value, self.mode[1]) return field[self.slice] def __getitem__(self, range): - """ slice a label using hi and lo bit indices. + """slice a label using hi and lo bit indices. >>> label = LabelRef("label", 0, "imm32", slice(31,0)) >>> label.size @@ -327,15 +345,19 @@ class LabelRef(WordBase): if hi < lo: raise ValueError("cant slice reverse range") elif lo < 0 or hi >= self.size: - raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self)) + raise ValueError( + "slice [{0.start}:{0.stop}] out of range of {1}".format(range, self) + ) new_slice = slice(self.slice.stop + hi, self.slice.stop + lo) return LabelRef(self.label, self.offset, self.mode, new_slice) + empty = Bitfield(0, 0) + def u(num, bits): - """ parse an unsigned integer into a bitfield. + """parse an unsigned integer into a bitfield. >>> u(0x08, 8) Bitfield(0x8, 8) @@ -355,13 +377,15 @@ def u(num, bits): raise ValueError("negative value not allowed: {}".format(num)) if num.bit_length() > bits: - raise ValueError("value {} (u{}) too large for u{} field" - .format(num, num.bit_length(), bits)) + raise ValueError( + "value {} (u{}) too large for u{} field".format(num, num.bit_length(), bits) + ) return Bitfield(num, bits) + def i(num, bits): - """ parse a signed integer into a bitfield. + """parse a signed integer into a bitfield. >>> i(8, 8) Bitfield(0x8, 8) @@ -385,16 +409,20 @@ def i(num, bits): max = -1 - min if num > max or num < min: - raise ValueError("value {} (i{}) too large for i{} field [{};{}]" - .format(num, num.bit_length()+1, bits, min, max)) + raise ValueError( + "value {} (i{}) too large for i{} field [{};{}]".format( + num, num.bit_length() + 1, bits, min, max + ) + ) if num < 0: num = (1 << bits) + num return u(num, bits) + def from_part(part): - """ parse a size-tagged subv part into a bit. + """parse a size-tagged subv part into a bit. >>> from_part((0x12, 2)) Bitfield(0x12, 2) @@ -405,12 +433,14 @@ def from_part(part): size = part[1] return Bitfield(val, int(size)) + global_slice = slice -ref_re = re.compile(r'^([^\[+-]+)(?:([+-]\d+))?$') -slice_re = re.compile(r'^\[(\d+):(\d+)]$') +ref_re = re.compile(r"^([^\[+-]+)(?:([+-]\d+))?$") +slice_re = re.compile(r"^\[(\d+):(\d+)]$") + def ref(val, default_slice=None): - """ add a default slice spec to labels if missing. + """add a default slice spec to labels if missing. >>> ref(('label', 'imm12')) LabelRef('label', 0, ('imm', 12), 11:0) @@ -451,6 +481,8 @@ def ref(val, default_slice=None): if default_slice: expected = default_slice.start - default_slice.stop + 1 - assert expected == ref.size, ValueError("expected {} bit slice, got {}".format(expected, ref)) + assert expected == ref.size, ValueError( + "expected {} bit slice, got {}".format(expected, ref) + ) return ref |
