summaryrefslogtreecommitdiff
path: root/lib9p/idl.gen
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/idl.gen')
-rwxr-xr-xlib9p/idl.gen224
1 files changed, 107 insertions, 117 deletions
diff --git a/lib9p/idl.gen b/lib9p/idl.gen
index 1dafef9..1f5e48c 100755
--- a/lib9p/idl.gen
+++ b/lib9p/idl.gen
@@ -9,19 +9,14 @@ import enum
import os.path
import re
from abc import ABC, abstractmethod
-from typing import Callable, Literal, TypeAlias, TypeVar
+from typing import Callable, Final, Literal, TypeAlias, TypeVar, cast
# This strives to be "general-purpose" in that it just acts on the
# *.9p inputs; but (unfortunately?) there are a few special-cases in
# this script, marked with "SPECIAL".
-T = TypeVar("T")
-
# Types ########################################################################
-Type: TypeAlias = "Primitive | Number | Bitfield | Struct | Message"
-
-
class Primitive(enum.Enum):
u8 = 1
u16 = 2
@@ -133,7 +128,7 @@ class StructMember:
# from left-to-right when parsing
cnt: str | None = None
name: str
- typ: Type
+ typ: 'Type'
max: Expr
val: Expr
@@ -178,6 +173,10 @@ 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
+T = TypeVar("T", Number, Bitfield, Struct, Message)
+
# Parse *.9p ###################################################################
re_priname = "(?:1|2|4|8)" # primitive names
@@ -235,7 +234,7 @@ def parse_expr(expr: str) -> Expr:
ret = Expr()
for tok in re.split("([-+])", expr):
if tok == "-" or tok == "+":
- ret.tokens += [ExprOp(tok)]
+ ret.tokens += [ExprOp(cast(Literal["-", "+"], tok))]
elif re.fullmatch("[0-9]+", tok):
ret.tokens += [ExprLit(int(tok))]
else:
@@ -292,7 +291,9 @@ re_line_version = f"version\\s+{re_string('version')}"
re_line_import = f"from\\s+(?P<file>\\S+)\\s+import\\s+(?P<syms>{re_impname}(?:\\s*,\\s*{re_impname})*)"
re_line_num = f"num\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})"
re_line_bitfield = f"bitfield\\s+(?P<name>{re_symname})\\s*=\\s*(?P<prim>{re_priname})"
-re_line_bitfield_ = f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}"
+re_line_bitfield_ = (
+ f"bitfield\\s+(?P<name>{re_symname})\\s*\\+=\\s*{re_string('member')}"
+)
re_line_struct = (
f"struct\\s+(?P<name>{re_symname})\\s*(?P<op>\\+?=)\\s*{re_string('members')}"
)
@@ -372,6 +373,7 @@ def parse_file(
elif m := re.fullmatch(re_line_bitfield, line):
bf = Bitfield()
bf.name = m.group("name")
+ bf.in_versions.add(version)
prim = env[m.group("prim")]
assert isinstance(prim, Primitive)
@@ -454,10 +456,9 @@ def c_ver_enum(idprefix: str, ver: str) -> str:
return f"{idprefix.upper()}VER_{ver.replace('.', '_')}"
-def c_ver_ifdef(idprefix: str, versions: set[str]) -> str:
+def c_ver_ifdef(versions: set[str]) -> str:
return " || ".join(
- f"defined(CONFIG_{idprefix.upper()}ENABLE_{c_ver_enum('', v)})"
- for v in sorted(versions)
+ f"defined(CONFIG_9P_ENABLE_{v.replace('.', '_')})" for v in sorted(versions)
)
@@ -485,7 +486,51 @@ def c_typename(idprefix: str, typ: Type) -> str:
raise ValueError(f"not a type: {typ.__class__.__name__}")
+_ifdef_stack: list[str | None] = []
+
+
+def ifdef_push(n: int, _newval: str) -> str:
+ # Grow the stack as needed
+ global _ifdef_stack
+ while len(_ifdef_stack) < n:
+ _ifdef_stack += [None]
+
+ # Set some variables
+ parentval: str | None = None
+ for x in _ifdef_stack[:-1]:
+ if x is not None:
+ parentval = x
+ oldval = _ifdef_stack[-1]
+ newval: str | None = _newval
+ if newval == parentval:
+ newval = None
+
+ # Put newval on the stack.
+ _ifdef_stack[-1] = newval
+
+ # Build output.
+ ret = ""
+ if newval != oldval:
+ if oldval is not None:
+ ret += f"#endif /* {oldval} */\n"
+ if newval is not None:
+ ret += f"#if {newval}\n"
+ return ret
+
+def ifdef_pop(n: int) -> str:
+ global _ifdef_stack
+ ret = ""
+ while len(_ifdef_stack) > n:
+ if _ifdef_stack[-1] is not None:
+ ret += f"#endif /* {_ifdef_stack[-1]} */\n"
+ _ifdef_stack = _ifdef_stack[:-1]
+ return ret
+
+
def gen_h(idprefix: str, versions: set[str], typs: list[Type]) -> str:
+ global _ifdef_stack
+ _ifdef_stack = []
+
ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
#ifndef _LIB9P_9P_H_
@@ -495,36 +540,6 @@ def gen_h(idprefix: str, versions: set[str], typs: list[Type]) -> str:
#include <stdint.h> /* for uint{{n}}_t types */
"""
- _ifdef: list[str] = []
-
- def push_ifdef(v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- ret += f"#if {v}\n"
- _ifdef += [v]
-
- def pop_ifdef(n: int) -> None:
- nonlocal _ifdef
- nonlocal ret
- while len(_ifdef) > n:
- ret += f"#endif /* {_ifdef[-1]}\n"
- _ifdef = _ifdef[:-1]
-
- def set_ifdef(v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- if v != _ifdef[-1]:
- ret += f"#elif {v}\n"
- _ifdef[-1] = v
-
- def pushorset_ifdef(n: int, v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- if len(_ifdef) < n:
- push_ifdef(v)
- else:
- set_ifdef(v)
-
ret += f"""
/* versions *******************************************************************/
@@ -534,10 +549,10 @@ enum {idprefix}version {{
verwidth = max(len(v) for v in fullversions)
for ver in fullversions:
if ver in versions:
- pushorset_ifdef(1, c_ver_ifdef(idprefix, {ver}))
+ ret += ifdef_push(1, c_ver_ifdef({ver}))
ret += f"\t{c_ver_enum(idprefix, ver)},"
ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n'
- pop_ifdef(0)
+ ret += ifdef_pop(0)
ret += f"\t{c_ver_enum(idprefix, 'NUM')},\n"
ret += "};\n"
ret += "\n"
@@ -548,7 +563,7 @@ enum {idprefix}version {{
"""
for typ in [typ for typ in typs if not isinstance(typ, Message)]:
ret += "\n"
- pushorset_ifdef(1, c_ver_ifdef(idprefix, typ.in_versions))
+ ret += ifdef_push(1, c_ver_ifdef(typ.in_versions))
match typ:
case Number():
ret += f"typedef {c_typename(idprefix, typ.prim)} {c_typename(idprefix, typ)};\n"
@@ -556,7 +571,7 @@ enum {idprefix}version {{
ret += f"typedef {c_typename(idprefix, typ.prim)} {c_typename(idprefix, typ)};\n"
names = [
*reversed(
- [typ.bits[n] or f"_UNUSED_{n}" for n in range(0, len(typ.bits))]
+ [typ.bits[n] or f" {n}" for n in range(0, len(typ.bits))]
),
"",
*[k for k in typ.names if k not in typ.bits],
@@ -567,21 +582,21 @@ enum {idprefix}version {{
for name in names:
if name == "":
ret += "\n"
- continue
- pushorset_ifdef(
- 2, c_ver_ifdef(idprefix, typ.names[name].in_versions)
- )
- if name.startswith("_"):
- c_name = f"_{idprefix.upper()}{typ.name.upper()}_{name[1:]}"
- else:
- c_name = f"{idprefix.upper()}{typ.name.upper()}_{name}"
- if name in typ.names:
- val = typ.names[name].val
+ elif name.startswith(" "):
+ ret += ifdef_push(2, c_ver_ifdef(typ.in_versions))
+ sp = ' '*(len('# define ')+len(idprefix)+len(typ.name)+1+namewidth+2-len("/* unused"))
+ ret += f"/* unused{sp}(({c_typename(idprefix, typ)})(1<<{name[1:]})) */\n"
else:
- assert name.startswith("_UNUSED_")
- val = f"1<<{name[len('_UNUSED_'):]}"
- ret += f"#define {c_name}{' '*(namewidth-len(name))} (({c_typename(idprefix, typ)})({val}))\n"
- pop_ifdef(1)
+ ret += ifdef_push(2, c_ver_ifdef(typ.names[name].in_versions))
+ if name.startswith("_"):
+ c_name = f"_{idprefix.upper()}{typ.name.upper()}_{name[1:]}"
+ else:
+ c_name = f"{idprefix.upper()}{typ.name.upper()}_{name}"
+ sp1 = ' ' if _ifdef_stack[-1] else ''
+ sp2 = ' ' if _ifdef_stack[-1] else ' '
+ sp3 = ' '*(2+namewidth-len(name))
+ ret += f"#{sp1}define{sp2}{c_name}{sp3}(({c_typename(idprefix, typ)})({typ.names[name].val}))\n"
+ ret += ifdef_pop(1)
case Struct():
typewidth = max(len(c_typename(idprefix, m.typ)) for m in typ.members)
@@ -589,14 +604,14 @@ enum {idprefix}version {{
for member in typ.members:
if member.val:
continue
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
c_type = c_typename(idprefix, member.typ)
if (typ.name in ["d", "s"]) and member.cnt: # SPECIAL
c_type = "char"
ret += f"\t{c_type.ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n"
- pop_ifdef(1)
+ ret += ifdef_pop(1)
ret += "};\n"
- pop_ifdef(0)
+ ret += ifdef_pop(0)
ret += """
/* messages *******************************************************************/
@@ -605,16 +620,16 @@ enum {idprefix}version {{
ret += f"enum {idprefix}msg_type {{ /* uint8_t */\n"
namewidth = max(len(msg.name) for msg in typs if isinstance(msg, Message))
for msg in [msg for msg in typs if isinstance(msg, Message)]:
- pushorset_ifdef(1, c_ver_ifdef(idprefix, msg.in_versions))
+ ret += ifdef_push(1, c_ver_ifdef(msg.in_versions))
ret += f"\t{idprefix.upper()}TYP_{msg.name.ljust(namewidth)} = {msg.msgid},\n"
- pop_ifdef(0)
+ ret += ifdef_pop(0)
ret += "};\n"
ret += "\n"
ret += f"const char *{idprefix}msg_type_str(enum {idprefix}msg_type);\n"
for msg in [msg for msg in typs if isinstance(msg, Message)]:
ret += "\n"
- pushorset_ifdef(1, c_ver_ifdef(idprefix, msg.in_versions))
+ ret += ifdef_push(1, c_ver_ifdef(msg.in_versions))
ret += c_typename(idprefix, msg) + " {"
if not msg.members:
ret += "};\n"
@@ -626,11 +641,11 @@ enum {idprefix}version {{
for member in msg.members:
if member.val:
continue
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
ret += f"\t{c_typename(idprefix, member.typ).ljust(typewidth)} {'*' if member.cnt else ' '}{member.name};\n"
- pop_ifdef(1)
+ ret += ifdef_pop(1)
ret += "};\n"
- pop_ifdef(0)
+ ret += ifdef_pop(0)
return ret
@@ -651,6 +666,9 @@ def c_expr(expr: Expr) -> str:
def gen_c(idprefix: str, versions: set[str], typs: list[Type]) -> str:
+ global _ifdef_stack
+ _ifdef_stack = []
+
ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */
#include <assert.h>
@@ -664,36 +682,6 @@ def gen_c(idprefix: str, versions: set[str], typs: list[Type]) -> str:
#include "internal.h"
"""
- _ifdef: list[str] = []
-
- def push_ifdef(v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- ret += f"#if {v}\n"
- _ifdef += [v]
-
- def pop_ifdef(n: int) -> None:
- nonlocal _ifdef
- nonlocal ret
- while len(_ifdef) > n:
- ret += f"#endif /* {_ifdef[-1]}\n"
- _ifdef = _ifdef[:-1]
-
- def set_ifdef(v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- if v != _ifdef[-1]:
- ret += f"#elif {v}\n"
- _ifdef[-1] = v
-
- def pushorset_ifdef(n: int, v: str) -> None:
- nonlocal _ifdef
- nonlocal ret
- if len(_ifdef) < n:
- push_ifdef(v)
- else:
- set_ifdef(v)
-
def used(arg: str) -> str:
return arg
@@ -707,9 +695,10 @@ def gen_c(idprefix: str, versions: set[str], typs: list[Type]) -> str:
static const char *version_strs[{c_ver_enum(idprefix, 'NUM')}] = {{
"""
for ver in ["unknown", *sorted(versions)]:
- pushorset_ifdef(1, c_ver_ifdef(idprefix, {ver}))
+ if ver in versions:
+ ret += ifdef_push(1, c_ver_ifdef({ver}))
ret += f'\t[{c_ver_enum(idprefix, ver)}] = "{ver}",\n'
- pop_ifdef(0)
+ ret += ifdef_pop(0)
ret += "};\n"
ret += f"""
const char *{idprefix}version_str(enum {idprefix}version ver) {{
@@ -774,7 +763,7 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
inline = "FLATTEN" if isinstance(typ, Message) else "ALWAYS_INLINE"
argfn = unused if (isinstance(typ, Struct) and not typ.members) else used
ret += "\n"
- pushorset_ifdef(1, c_ver_ifdef(idprefix, typ.in_versions))
+ ret += ifdef_push(1, c_ver_ifdef(typ.in_versions))
ret += f"static {inline} bool validate_{typ.name}(struct _validate_ctx *{argfn('ctx')}) {{\n"
if typ.name == "d": # SPECIAL
@@ -811,7 +800,7 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
ret += f"\tstatic const {c_typename(idprefix, typ)} masks[{c_ver_enum(idprefix, 'NUM')}] = {{\n"
verwidth = max(len(ver) for ver in versions)
for ver in sorted(versions):
- pushorset_ifdef(2, c_ver_ifdef(idprefix, {ver}))
+ ret += ifdef_push(2, c_ver_ifdef({ver}))
ret += (
f"\t\t[{c_ver_enum(idprefix, ver)}]{' '*(verwidth-len(ver))} = 0b"
+ "".join(
@@ -820,7 +809,7 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
)
+ ",\n"
)
- pop_ifdef(1)
+ ret += ifdef_pop(1)
ret += "\t};\n"
ret += (
f"\t{c_typename(idprefix, typ)} mask = masks[ctx->ctx->version];\n"
@@ -839,10 +828,10 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
# Pass 1
for member in typ.members:
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
if member.max or member.val:
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
ret += f"\t{c_typename(idprefix, member.typ)} {member.name};\n"
- pop_ifdef(1)
+ ret += ifdef_pop(1)
# Pass 2
mark_offset: set[str] = set()
@@ -857,8 +846,8 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
ret += "\treturn false\n"
prev_size: int | None = None
for member in typ.members:
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
- ret += f"\n\t|| "
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
+ ret += f"\t || "
if member.in_versions != typ.in_versions:
ret += "( " + c_ver_cond(idprefix, member.in_versions) + " && "
if member.cnt is not None:
@@ -879,23 +868,24 @@ static ALWAYS_INLINE bool _validate_list(struct _validate_ctx *ctx,
ret += f" || ({{ {member.name} = decode_u{bits}le(&ctx->net_bytes[ctx->net_offset-{bytes}]); false; }}))"
if member.in_versions != typ.in_versions:
ret += " )"
+ ret += "\n"
prev_size = member.static_size
# Pass 4
for member in typ.members:
if member.max:
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
- ret += f"\n\t|| ({{ uint32_t max = {c_expr(member.max)}; (((uint32_t){member.name}) > max) &&\n"
- ret += f'\n\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value too large (%"PRIu32" > %"PRIu32")", {member.name}, max); }})\n'
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
+ ret += f"\n\t || ({{ uint32_t max = {c_expr(member.max)}; (((uint32_t){member.name}) > max) &&\n"
+ ret += f'\n\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value too large (%"PRIu32" > %"PRIu32")", {member.name}, max); }})\n'
if member.val:
- pushorset_ifdef(2, c_ver_ifdef(idprefix, member.in_versions))
- ret += f"\n\t|| ({{ uint32_t exp = {c_expr(member.val)}; (((uint32_t){member.name}) != exp) &&\n"
- ret += f'\n\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t){member.name}, exp); }})\n'
+ ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
+ ret += f"\n\t || ({{ uint32_t exp = {c_expr(member.val)}; (((uint32_t){member.name}) != exp) &&\n"
+ ret += f'\n\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.name} value wrong (actual:%"PRIu32" != correct:%"PRIu32")", (uint32_t){member.name}, exp); }})\n'
- pop_ifdef(1)
- ret += "\t;\n"
+ ret += ifdef_pop(1)
+ ret += "\t ;\n"
ret += "}\n"
- pop_ifdef(0)
+ ret += ifdef_pop(0)
# # unmarshal_* ##############################################################
# ret += """