diff options
Diffstat (limited to 'lib9p/idl/__init__.py')
-rw-r--r-- | lib9p/idl/__init__.py | 31 |
1 files changed, 25 insertions, 6 deletions
diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py index 920d02d..ab45ed0 100644 --- a/lib9p/idl/__init__.py +++ b/lib9p/idl/__init__.py @@ -1,12 +1,12 @@ # lib9p/idl/__init__.py - A parser for .9p specification files. # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later import enum import os.path import re -from typing import Callable, Literal, TypeAlias, TypeVar, cast +from typing import Callable, Literal, TypeVar, cast __all__ = [ # entrypoint @@ -50,8 +50,11 @@ class Number: prim: Primitive + vals: dict[str, str] + def __init__(self) -> None: self.in_versions = set() + self.vals = {} @property def static_size(self) -> int: @@ -181,8 +184,7 @@ class Message(Struct): return self.members[1].val.tokens[0].val -Type: TypeAlias = Primitive | Number | Bitfield | Struct | Message -# type Type = Primitive | Number | Bitfield | Struct | Message # Change to this once we have Python 3.13 +type Type = Primitive | Number | Bitfield | Struct | Message T = TypeVar("T", Number, Bitfield, Struct, Message) # Parse ######################################################################## @@ -196,12 +198,27 @@ re_memtype = f"(?:{re_symname}|{re_priname})" # typenames that a struct member 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_memberspec = f"(?:(?P<cnt>{re_symname})\\*\\()?(?P<name>{re_symname})\\[(?P<typ>{re_memtype})(?:,max=(?P<max>{re_expr})|,val=(?P<val>{re_expr}))*\\]\\)?" +def parse_numspec(ver: str, n: Number, spec: str) -> None: + spec = spec.strip() + + if m := re.fullmatch(re_numspec, spec): + name = m.group("name") + val = m.group("val") + if name in n.vals: + raise ValueError(f"{n.name}: name {repr(name)} already assigned") + n.vals[name] = val + else: + raise SyntaxError(f"invalid num spec {repr(spec)}") + + def parse_bitspec(ver: str, bf: Bitfield, spec: str) -> None: spec = spec.strip() @@ -266,7 +283,7 @@ def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> raise ValueError(f"duplicate member name {repr(member.name)}") if m.group("typ") not in env: - raise NameError(f"Unknown type {repr(m.group(2))}") + raise NameError(f"Unknown type {repr(m.group('typ'))}") member.typ = env[m.group("typ")] if cnt := m.group("cnt"): @@ -434,6 +451,8 @@ def parse_file( match prev: case Bitfield(): parse_bitspec(version, prev, m.group("specs")) + case Number(): + parse_numspec(version, prev, m.group("specs")) case Struct(): # and Message() parse_members(version, env, prev, m.group("specs")) case _: @@ -448,7 +467,7 @@ 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", *["&" + m.name for m in typ.members]] + valid_syms = ["end", "s32_max", "s64_max", *["&" + m.name for m in typ.members]] for member in typ.members: for tok in [*member.max.tokens, *member.val.tokens]: if isinstance(tok, ExprSym) and tok.name not in valid_syms: |