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, 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):