diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-09 11:05:26 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-10-09 11:05:26 -0600 |
commit | cb8893dd08b7b359f45ef225acd2f6e103d38bba (patch) | |
tree | 1ae67dac842ee044ec1bd838ac9457f06c66b804 /lib9p/idl.gen | |
parent | bed78039f9bf086d35a3ae5efc8e6701c50ed006 (diff) |
wip
Diffstat (limited to 'lib9p/idl.gen')
-rwxr-xr-x | lib9p/idl.gen | 224 |
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 += """ |