diff options
Diffstat (limited to 'lib9p/idl/__init__.py')
-rw-r--r-- | lib9p/idl/__init__.py | 148 |
1 files changed, 78 insertions, 70 deletions
diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py index a01c38f..042a438 100644 --- a/lib9p/idl/__init__.py +++ b/lib9p/idl/__init__.py @@ -6,8 +6,9 @@ import enum import os.path import re -from typing import Callable, Literal, TypeVar, cast +import typing +# pylint: disable=unused-variable __all__ = [ # entrypoint "Parser", @@ -36,7 +37,7 @@ class Primitive(enum.Enum): return set() @property - def name(self) -> str: + def typname(self) -> str: return str(self.value) @property @@ -51,7 +52,7 @@ class Primitive(enum.Enum): class Number: - name: str + typname: str in_versions: set[str] prim: Primitive @@ -74,7 +75,7 @@ class Number: class BitfieldVal: - name: str + bitname: str in_versions: set[str] val: str @@ -84,7 +85,7 @@ class BitfieldVal: class Bitfield: - name: str + typname: str in_versions: set[str] prim: Primitive @@ -130,16 +131,16 @@ class ExprLit: class ExprSym: - name: str + symname: str def __init__(self, name: str) -> None: - self.name = name + self.symname = name class ExprOp: - op: Literal["-", "+"] + op: typing.Literal["-", "+"] - def __init__(self, op: Literal["-", "+"]) -> None: + def __init__(self, op: typing.Literal["-", "+"]) -> None: self.op = op @@ -156,7 +157,7 @@ class Expr: class StructMember: # from left-to-right when parsing cnt: "StructMember | None" = None - name: str + membname: str typ: "Type" max: Expr val: Expr @@ -168,10 +169,12 @@ class StructMember: assert self.cnt if not isinstance(self.cnt.typ, Primitive): raise ValueError( - f"list count must be an integer type: {repr(self.cnt.name)}" + f"list count must be an integer type: {repr(self.cnt.membname)}" ) if self.cnt.val: # TODO: allow this? - raise ValueError(f"list count may not have ,val=: {repr(self.cnt.name)}") + raise ValueError( + f"list count may not have ,val=: {repr(self.cnt.membname)}" + ) return 0 @property @@ -179,26 +182,28 @@ class StructMember: assert self.cnt if not isinstance(self.cnt.typ, Primitive): raise ValueError( - f"list count must be an integer type: {repr(self.cnt.name)}" + f"list count must be an integer type: {repr(self.cnt.membname)}" ) if self.cnt.val: # TODO: allow this? - raise ValueError(f"list count may not have ,val=: {repr(self.cnt.name)}") + raise ValueError( + f"list count may not have ,val=: {repr(self.cnt.membname)}" + ) if self.cnt.max: # TODO: be more flexible? if len(self.cnt.max.tokens) != 1: raise ValueError( - f"list count ,max= may only have 1 token: {repr(self.cnt.name)}" + f"list count ,max= may only have 1 token: {repr(self.cnt.membname)}" ) match tok := self.cnt.max.tokens[0]: case ExprLit(): return tok.val - case ExprSym(name="s32_max"): + case ExprSym(symname="s32_max"): return (1 << 31) - 1 - case ExprSym(name="s64_max"): + case ExprSym(symname="s64_max"): return (1 << 63) - 1 case _: raise ValueError( - f'list count ,max= only allows literal, "s32_max", and "s64_max" tokens: {repr(self.cnt.name)}' + f'list count ,max= only allows literal, "s32_max", and "s64_max" tokens: {repr(self.cnt.membname)}' ) return (1 << (self.cnt.typ.value * 8)) - 1 @@ -218,7 +223,7 @@ class StructMember: class Struct: - name: str + typname: str in_versions: set[str] members: list[StructMember] @@ -257,7 +262,7 @@ class Message(Struct): @property def msgid(self) -> int: assert len(self.members) >= 3 - assert self.members[1].name == "typ" + assert self.members[1].membname == "typ" assert self.members[1].static_size == 1 assert self.members[1].val assert len(self.members[1].val.tokens) == 1 @@ -266,7 +271,7 @@ class Message(Struct): type Type = Primitive | Number | Bitfield | Struct | Message -T = TypeVar("T", Number, Bitfield, Struct, Message) +T = typing.TypeVar("T", Number, Bitfield, Struct, Message) # Parse ######################################################################## @@ -294,7 +299,7 @@ def parse_numspec(ver: str, n: Number, spec: str) -> None: name = m.group("name") val = m.group("val") if name in n.vals: - raise ValueError(f"{n.name}: name {repr(name)} already assigned") + raise ValueError(f"{n.typname}: name {repr(name)} already assigned") n.vals[name] = val else: raise SyntaxError(f"invalid num spec {repr(spec)}") @@ -310,39 +315,39 @@ def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None: name = m.group("name") val = BitfieldVal() - val.name = name + 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.name}: bit {bit} is out-of-bounds") + raise ValueError(f"{bf.typname}: bit {bit} is out-of-bounds") if bf.bits[bit]: - raise ValueError(f"{bf.name}: bit {bit} already assigned") - bf.bits[bit] = val.name + raise ValueError(f"{bf.typname}: bit {bit} already assigned") + bf.bits[bit] = val.bitname elif m := re.fullmatch(re_bitspec_alias, spec): name = m.group("name") valstr = m.group("val") val = BitfieldVal() - val.name = name + val.bitname = name val.val = valstr val.in_versions.add(ver) else: raise SyntaxError(f"invalid bitfield spec {repr(spec)}") - if val.name in bf.names: - raise ValueError(f"{bf.name}: name {val.name} already assigned") - bf.names[val.name] = val + if val.bitname in bf.names: + raise ValueError(f"{bf.typname}: name {val.bitname} already assigned") + bf.names[val.bitname] = val def parse_expr(expr: str) -> Expr: assert re.fullmatch(re_expr, expr) ret = Expr() for tok in re.split("([-+])", expr): - if tok == "-" or tok == "+": + if tok in ("-", "+"): # I, for the life of me, do not understand why I need this - # cast() to keep mypy happy. - ret.tokens += [ExprOp(cast(Literal["-", "+"], tok))] + # typing.cast() to keep mypy happy. + ret.tokens += [ExprOp(typing.cast(typing.Literal["-", "+"], tok))] elif re.fullmatch("[0-9]+", tok): ret.tokens += [ExprLit(int(tok))] else: @@ -359,16 +364,16 @@ def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> member = StructMember() member.in_versions = {ver} - member.name = m.group("name") - if any(x.name == member.name for x in struct.members): - raise ValueError(f"duplicate member name {repr(member.name)}") + member.membname = m.group("name") + if any(x.membname == member.membname for x in struct.members): + raise ValueError(f"duplicate member name {member.membname!r}") if m.group("typ") not in env: raise NameError(f"Unknown type {repr(m.group('typ'))}") member.typ = env[m.group("typ")] if cnt := m.group("cnt"): - if len(struct.members) == 0 or struct.members[-1].name != cnt: + if len(struct.members) == 0 or struct.members[-1].membname != cnt: raise ValueError(f"list count must be previous item: {repr(cnt)}") cnt_mem = struct.members[-1] member.cnt = cnt_mem @@ -412,7 +417,7 @@ re_line_cont = f"\\s+{re_string('specs')}" # could be bitfield/struct/msg def parse_file( - filename: str, get_include: Callable[[str], tuple[str, list[Type]]] + filename: str, get_include: typing.Callable[[str], tuple[str, list[Type]]] ) -> tuple[str, list[Type]]: version: str | None = None env: dict[str, Type] = { @@ -428,10 +433,10 @@ def parse_file( raise NameError(f"Unknown type {repr(name)}") ret = env[name] if (not isinstance(ret, tc)) or (ret.__class__.__name__ != tc.__name__): - raise NameError(f"Type {repr(ret.name)} is not a {tc.__name__}") + raise NameError(f"Type {repr(ret.typname)} is not a {tc.__name__}") return ret - with open(filename, "r") as fh: + with open(filename, "r", encoding="utf-8") as fh: prev: Type | None = None for lineno, line in enumerate(fh): try: @@ -452,7 +457,7 @@ def parse_file( symname = symname.strip() found = False for typ in other_typs: - if typ.name == symname or symname == "*": + if symname in (typ.typname, "*"): found = True match typ: case Primitive(): @@ -469,31 +474,31 @@ def parse_file( for member in typ.members: if other_version in member.in_versions: member.in_versions.add(version) - if typ.name in env and env[typ.name] != typ: + if typ.typname in env and env[typ.typname] != typ: raise ValueError( - f"duplicate type name {repr(typ.name)}" + f"duplicate type name {typ.typname!r}" ) - env[typ.name] = typ + env[typ.typname] = typ if symname != "*" and not found: raise ValueError( f"import: {m.group('file')}: no symbol {repr(symname)}" ) elif m := re.fullmatch(re_line_num, line): num = Number() - num.name = m.group("name") + num.typname = m.group("name") num.in_versions.add(version) prim = env[m.group("prim")] assert isinstance(prim, Primitive) num.prim = prim - if num.name in env: - raise ValueError(f"duplicate type name {repr(num.name)}") - env[num.name] = num + if num.typname in env: + raise ValueError(f"duplicate type name {num.typname!r}") + env[num.typname] = num prev = num elif m := re.fullmatch(re_line_bitfield, line): bf = Bitfield() - bf.name = m.group("name") + bf.typname = m.group("name") bf.in_versions.add(version) prim = env[m.group("prim")] @@ -502,9 +507,9 @@ def parse_file( bf.bits = (prim.static_size * 8) * [""] - if bf.name in env: - raise ValueError(f"duplicate type name {repr(bf.name)}") - env[bf.name] = bf + if bf.typname in env: + raise ValueError(f"duplicate type name {bf.typname!r}") + env[bf.typname] = bf prev = bf elif m := re.fullmatch(re_line_bitfield_, line): bf = get_type(m.group("name"), Bitfield) @@ -515,16 +520,16 @@ def parse_file( match m.group("op"): case "=": struct = Struct() - struct.name = m.group("name") + struct.typname = m.group("name") struct.in_versions.add(version) struct.members = [] parse_members(version, env, struct, m.group("members")) - if struct.name in env: + if struct.typname in env: raise ValueError( - f"duplicate type name {repr(struct.name)}" + f"duplicate type name {struct.typname!r}" ) - env[struct.name] = struct + env[struct.typname] = struct prev = struct case "+=": struct = get_type(m.group("name"), Struct) @@ -535,16 +540,14 @@ def parse_file( match m.group("op"): case "=": msg = Message() - msg.name = m.group("name") + msg.typname = m.group("name") msg.in_versions.add(version) msg.members = [] parse_members(version, env, msg, m.group("members")) - if msg.name in env: - raise ValueError( - f"duplicate type name {repr(msg.name)}" - ) - env[msg.name] = msg + if msg.typname in env: + raise ValueError(f"duplicate type name {msg.typname!r}") + env[msg.typname] = msg prev = msg case "+=": msg = get_type(m.group("name"), Message) @@ -577,19 +580,24 @@ def parse_file( typs: list[Type] = [x for x in env.values() if not isinstance(x, Primitive)] for typ in [typ for typ in typs if isinstance(typ, Struct)]: - valid_syms = ["end", "s32_max", "s64_max", *["&" + m.name for m in typ.members]] + valid_syms = [ + "end", + "s32_max", + "s64_max", + *["&" + m.membname for m in typ.members], + ] for member in typ.members: if ( not isinstance(member.typ, Primitive) and member.typ.in_versions < member.in_versions ): raise ValueError( - f"{typ.name}.{member.name}: type {member.typ.name} does not exist in {member.in_versions.difference(member.typ.in_versions)}" + f"{typ.typname}.{member.membname}: type {member.typ.typname} does not exist in {member.in_versions.difference(member.typ.in_versions)}" ) for tok in [*member.max.tokens, *member.val.tokens]: - if isinstance(tok, ExprSym) and tok.name not in valid_syms: + if isinstance(tok, ExprSym) and tok.symname not in valid_syms: raise ValueError( - f"{typ.name}.{member.name}: invalid sym: {tok.name}" + f"{typ.typname}.{member.membname}: invalid sym: {tok.symname}" ) return version, typs @@ -619,11 +627,11 @@ class Parser: raise ValueError(f"duplicate protocol version {repr(version)}") ret_versions.add(version) for typ in typs: - if typ.name in ret_typs: - if typ != ret_typs[typ.name]: - raise ValueError(f"duplicate type name {repr(typ.name)}") + if typ.typname in ret_typs: + if typ != ret_typs[typ.typname]: + raise ValueError(f"duplicate type name {repr(typ.typname)}") else: - ret_typs[typ.name] = typ + ret_typs[typ.typname] = typ msgids: set[int] = set() for typ in ret_typs.values(): if isinstance(typ, Message): |