summaryrefslogtreecommitdiff
path: root/lib9p/idl/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/idl/__init__.py')
-rw-r--r--lib9p/idl/__init__.py148
1 files changed, 89 insertions, 59 deletions
diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py
index 04e1791..70eaf57 100644
--- a/lib9p/idl/__init__.py
+++ b/lib9p/idl/__init__.py
@@ -16,7 +16,7 @@ __all__ = [
"Type",
"Primitive",
"Number",
- *["Bitfield", "BitfieldVal"],
+ *["Bitfield", "Bit", "BitCat", "BitAlias"],
*["Struct", "StructMember", "Expr", "ExprOp", "ExprSym", "ExprLit"],
"Message",
]
@@ -74,27 +74,49 @@ class Number:
return self.static_size
-class BitfieldVal:
+class BitCat(enum.Enum):
+ UNUSED = 1
+ USED = 2
+ RESERVED = 3
+ SUBFIELD = 4
+
+
+class Bit:
bitname: str
in_versions: set[str]
+ num: int
+ cat: BitCat
- val: str
+ def __init__(self, num: int) -> None:
+ self.bitname = ""
+ self.in_versions = set()
+ self.num = num
+ self.cat = BitCat.UNUSED
- def __init__(self) -> None:
+
+class BitAlias:
+ bitname: str
+ in_versions: set[str]
+ val: str # FIXME: Don't have bitfield aliases be raw C expressions
+
+ def __init__(self, name: str, val: str) -> None:
+ self.bitname = name
self.in_versions = set()
+ self.val = val
class Bitfield:
typname: str
in_versions: set[str]
-
prim: Primitive
+ bits: list[Bit]
+ names: dict[str, Bit | BitAlias]
- bits: list[str] # bitnames
- names: dict[str, BitfieldVal] # bits *and* aliases
-
- def __init__(self) -> None:
+ def __init__(self, name: str, prim: Primitive) -> None:
+ self.typname = name
self.in_versions = set()
+ self.prim = prim
+ self.bits = [Bit(i) for i in range(prim.static_size * 8)]
self.names = {}
@property
@@ -107,21 +129,6 @@ class Bitfield:
def max_size(self, version: str) -> int:
return self.static_size
- def bit_is_valid(self, bit: str | int, ver: str | None = None) -> bool:
- """Return whether the given bit is valid in the given protocol
- version.
-
- """
- bitname = self.bits[bit] if isinstance(bit, int) else bit
- assert bitname in self.bits
- if not bitname:
- return False
- if bitname.startswith("_"):
- return False
- if ver and (ver not in self.names[bitname].in_versions):
- return False
- return True
-
class ExprLit:
val: int
@@ -278,6 +285,8 @@ T = typing.TypeVar("T", Number, Bitfield, Struct, Message)
re_priname = "(?:1|2|4|8)" # primitive names
re_symname = "(?:[a-zA-Z_][a-zA-Z_0-9]*)" # "symbol" names; most *.9p-defined names
+re_symname_u = "(?:[A-Z_][A-Z_0-9]*)" # upper-case "symbol" names; bit names
+re_symname_l = "(?:[a-z_][a-z_0-9]*)" # lower-case "symbol" names; bit names
re_impname = r"(?:\*|" + re_symname + ")" # names we can import
re_msgname = r"(?:[TR][a-zA-Z_0-9]*)" # names a message can be
@@ -287,8 +296,18 @@ re_expr = f"(?:(?:-|\\+|[0-9]+|&?{re_symname})+)"
re_numspec = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>\\S+)"
-re_bitspec_bit = f"(?P<bit>[0-9]+)\\s*=\\s*(?P<name>{re_symname})"
-re_bitspec_alias = f"(?P<name>{re_symname})\\s*=\\s*(?P<val>\\S+)"
+re_bitspec_bit = (
+ "(?P<bitnum>[0-9]+)\\s*=\\s*(?:"
+ + "|".join(
+ [
+ f"(?P<name_used>{re_symname_u})",
+ f"reserved\\((?P<name_reserved>{re_symname_u})\\)",
+ f"subfield\\((?P<name_subfield>{re_symname_l})\\)",
+ ]
+ )
+ + ")"
+)
+re_bitspec_alias = f"(?P<name>{re_symname_u})\\s*=\\s*(?P<val>\\S+)"
re_memberspec = f"(?:(?P<cnt>{re_symname})\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?"
@@ -309,36 +328,48 @@ def parse_numspec(ver: str, n: Number, spec: str) -> None:
def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None:
spec = spec.strip()
- bit: int | None
- val: BitfieldVal
if m := re.fullmatch(re_bitspec_bit, spec):
- bit = int(m.group("bit"))
- name = m.group("name")
-
- val = BitfieldVal()
- val.bitname = name
- val.val = f"1<<{bit}"
- val.in_versions.add(ver)
-
- if bit < 0 or bit >= len(bf.bits):
- raise ValueError(f"{bf.typname}: bit {bit} is out-of-bounds")
- if bf.bits[bit]:
- raise ValueError(f"{bf.typname}: bit {bit} already assigned")
- bf.bits[bit] = val.bitname
+ bitnum = int(m.group("bitnum"))
+ if bitnum < 0 or bitnum >= len(bf.bits):
+ raise ValueError(f"{bf.typname}: bit num {bitnum} out-of-bounds")
+ bit = bf.bits[bitnum]
+ if bit.cat != BitCat.UNUSED:
+ raise ValueError(f"{bf.typname}: bit num {bitnum} already assigned")
+ if name := m.group("name_used"):
+ bit.bitname = name
+ bit.cat = BitCat.USED
+ bit.in_versions.add(ver)
+ elif name := m.group("name_reserved"):
+ bit.bitname = name
+ bit.cat = BitCat.RESERVED
+ bit.in_versions.add(ver)
+ elif name := m.group("name_subfield"):
+ bit.bitname = name
+ bit.cat = BitCat.SUBFIELD
+ bit.in_versions.add(ver)
+ if bit.bitname:
+ if bit.bitname in bf.names:
+ other = bf.names[bit.bitname]
+ if (
+ isinstance(other, Bit)
+ and other.cat == bit.cat
+ and bit.cat == BitCat.SUBFIELD
+ ):
+ return
+ raise ValueError(
+ f"{bf.typname}: bit name {bit.bitname!r} already assigned"
+ )
+ bf.names[bit.bitname] = bit
elif m := re.fullmatch(re_bitspec_alias, spec):
- name = m.group("name")
- valstr = m.group("val")
-
- val = BitfieldVal()
- val.bitname = name
- val.val = valstr
- val.in_versions.add(ver)
+ alias = BitAlias(m.group("name"), m.group("val"))
+ alias.in_versions.add(ver)
+ if alias.bitname in bf.names:
+ raise ValueError(
+ f"{bf.typname}: bit name {alias.bitname!r} already assigned"
+ )
+ bf.names[alias.bitname] = alias
else:
- raise SyntaxError(f"invalid bitfield spec {repr(spec)}")
-
- if val.bitname in bf.names:
- raise ValueError(f"{bf.typname}: name {val.bitname} already assigned")
- bf.names[val.bitname] = val
+ raise SyntaxError(f"invalid bitfield spec {spec!r}")
def parse_expr(expr: str) -> Expr:
@@ -467,6 +498,9 @@ def parse_file(
typ.in_versions.add(version)
case Bitfield():
typ.in_versions.add(version)
+ for bit in typ.bits:
+ if other_version in bit.in_versions:
+ bit.in_versions.add(version)
for val in typ.names.values():
if other_version in val.in_versions:
val.in_versions.add(version)
@@ -498,15 +532,11 @@ def parse_file(
env[num.typname] = num
prev = num
elif m := re.fullmatch(re_line_bitfield, line):
- bf = Bitfield()
- bf.typname = m.group("name")
- bf.in_versions.add(version)
-
prim = env[m.group("prim")]
assert isinstance(prim, Primitive)
- bf.prim = prim
- bf.bits = (prim.static_size * 8) * [""]
+ bf = Bitfield(m.group("name"), prim)
+ bf.in_versions.add(version)
if bf.typname in env:
raise ValueError(f"duplicate type name {bf.typname!r}")