diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-23 00:53:44 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-23 03:05:06 -0600 |
commit | c1a1f287ed883bed049627da0fd8395197ebf876 (patch) | |
tree | 8bc42e9dd28bb7203639bd7bce75b70374a514f7 | |
parent | 057f17b3f22b8b0112f87b6c3128df6925b8f27a (diff) |
lib9p: protogen: pull cutil.py out of __init__.py
-rw-r--r-- | lib9p/protogen/__init__.py | 187 | ||||
-rw-r--r-- | lib9p/protogen/cutil.py | 84 |
2 files changed, 148 insertions, 123 deletions
diff --git a/lib9p/protogen/__init__.py b/lib9p/protogen/__init__.py index 21b5161..8a9a371 100644 --- a/lib9p/protogen/__init__.py +++ b/lib9p/protogen/__init__.py @@ -1,6 +1,5 @@ # lib9p/protogen/__init__.py - Generate C marshalers/unmarshalers for -# .9p files defining 9P protocol -# variants. +# .9p files defining 9P protocol variants # # Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later @@ -13,6 +12,8 @@ import typing import idl +from . import cutil + # 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". @@ -25,16 +26,6 @@ __all__ = ["main"] idprefix = "lib9p_" -u32max = (1 << 32) - 1 -u64max = (1 << 64) - 1 - - -def tab_ljust(s: str, width: int) -> str: - cur = len(s.expandtabs(tabsize=8)) - if cur >= width: - return s - return s + " " * (width - cur) - def add_prefix(p: str, s: str) -> str: if s.startswith("_"): @@ -42,15 +33,6 @@ def add_prefix(p: str, s: str) -> str: return p + s -def c_macro(full: str) -> str: - full = full.rstrip() - assert "\n" in full - lines = [l.rstrip() for l in full.split("\n")] - width = max(len(l.expandtabs(tabsize=8)) for l in lines[:-1]) - lines = [tab_ljust(l, width) for l in lines] - return " \\\n".join(lines).rstrip() + "\n" - - def c_ver_enum(ver: str) -> str: return f"{idprefix.upper()}VER_{ver.replace('.', '_')}" @@ -105,47 +87,6 @@ def c_expr(expr: idl.Expr, lookup_sym: typing.Callable[[str], str]) -> str: return " ".join(ret) -_ifdef_stack: list[str | None] = [] - - -def ifdef_push(n: int, _newval: str) -> str: - # Grow the stack as needed - while len(_ifdef_stack) < n: - _ifdef_stack.append(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 - - # topo_sorted() ################################################################ @@ -378,8 +319,7 @@ def get_buffer_size(typ: idl.Type, version: str) -> BufferSize: def gen_h(versions: set[str], typs: list[idl.UserType]) -> str: - global _ifdef_stack - _ifdef_stack = [] + cutil.ifdef_init() ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ @@ -424,10 +364,10 @@ enum {idprefix}version {{ verwidth = max(len(v) for v in fullversions) for ver in fullversions: if ver in versions: - ret += ifdef_push(1, c_ver_ifdef({ver})) + ret += cutil.ifdef_push(1, c_ver_ifdef({ver})) ret += f"\t{c_ver_enum(ver)}," ret += (" " * (verwidth - len(ver))) + ' /* "' + ver.split()[0] + '" */\n' - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += f"\t{c_ver_enum('NUM')},\n" ret += "};\n" @@ -441,9 +381,9 @@ enum {idprefix}version {{ if n not in id2typ: continue msg = id2typ[n] - ret += ifdef_push(1, c_ver_ifdef(msg.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(msg.in_versions)) ret += f"\t{idprefix.upper()}TYP_{msg.typname:<{namewidth}} = {msg.msgid},\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += "};\n" ret += """ @@ -469,21 +409,21 @@ enum {idprefix}version {{ for typ in topo_sorted(typs): ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(typ.in_versions)) def sum_size(typ: idl.UserType, version: str) -> str: sz = get_buffer_size(typ, version) assert ( sz.min_size <= sz.exp_size and sz.exp_size <= sz.max_size - and sz.max_size < u64max + and sz.max_size < cutil.UINT64_MAX ) ret = "" if sz.min_size == sz.max_size: ret += f"size = {sz.min_size:,}" else: ret += f"min_size = {sz.min_size:,} ; exp_size = {sz.exp_size:,} ; max_size = {sz.max_size:,}" - if sz.max_size > u32max: + if sz.max_size > cutil.UINT32_MAX: ret += " (warning: >UINT32_MAX)" ret += f" ; max_iov = {sz.max_iov:,}{sz.max_iov_extra} ; max_copy = {sz.max_copy:,}{sz.max_copy_extra}" return ret @@ -527,7 +467,7 @@ enum {idprefix}version {{ vers = bit.in_versions if bit.cat == idl.BitCat.UNUSED: vers = typ.in_versions - ret += ifdef_push(2, c_ver_ifdef(vers)) + ret += cutil.ifdef_push(2, c_ver_ifdef(vers)) # It is important all of the `beg` strings have # the same length. @@ -536,10 +476,10 @@ enum {idprefix}version {{ case ( idl.BitCat.USED | idl.BitCat.RESERVED | idl.BitCat.SUBFIELD ): - if _ifdef_stack[-1]: - beg = "# define" - else: + if cutil.ifdef_leaf_is_noop(): beg = "#define " + else: + beg = "# define" case idl.BitCat.UNUSED: beg = "/* unused" end = " */" @@ -555,18 +495,18 @@ enum {idprefix}version {{ ret += "\n" for alias in aliases: - ret += ifdef_push(2, c_ver_ifdef(alias.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(alias.in_versions)) end = "" - if _ifdef_stack[-1]: - beg = "# define" - else: + if cutil.ifdef_leaf_is_noop(): beg = "#define " + else: + beg = "# define" c_name = bitname(alias) c_val = alias.val ret += f"{beg} {c_name:<{namewidth}} (({c_typename(typ)})({c_val})){end}\n" - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) del bitname case idl.Struct(): # and idl.Message(): ret += c_typename(typ) + " {" @@ -580,12 +520,12 @@ enum {idprefix}version {{ for member in typ.members: if member.val: continue - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) ret += f"\t{c_typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n" - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) ret += "};\n" del typ - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += """ /* containers *****************************************************************/ @@ -675,8 +615,7 @@ enum {idprefix}version {{ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: - global _ifdef_stack - _ifdef_stack = [] + cutil.ifdef_init() ret = f"""/* Generated by `{' '.join(sys.argv)}`. DO NOT EDIT! */ @@ -711,7 +650,7 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: ret = f"const {tentry} _{idprefix}table_{grp}_{meth}[{c_ver_enum('NUM')}][{hex(len(range(*rng)))}] = {{\n" for ver in ["unknown", *sorted(versions)]: if ver != "unknown": - ret += ifdef_push(1, c_ver_ifdef({ver})) + ret += cutil.ifdef_push(1, c_ver_ifdef({ver})) ret += f"\t[{c_ver_enum(ver)}] = {{\n" for n in range(*rng): xmsg: idl.Message | None = id2typ.get(n, None) @@ -725,7 +664,7 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: if xmsg: ret += f"\t\t_MSG_{meth.upper()}({xmsg.typname}),\n" ret += "\t},\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += "};\n" return ret @@ -752,9 +691,9 @@ const char *const _{idprefix}table_ver_name[{c_ver_enum('NUM')}] = {{ """ for ver in ["unknown", *sorted(versions)]: if ver in versions: - ret += ifdef_push(1, c_ver_ifdef({ver})) + ret += cutil.ifdef_push(1, c_ver_ifdef({ver})) ret += f'\t[{c_ver_enum(ver)}] = "{ver}",\n' - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += "};\n" ret += "\n" @@ -769,11 +708,11 @@ const char *const _{idprefix}table_ver_name[{c_ver_enum('NUM')}] = {{ if not isinstance(typ, idl.Bitfield): continue ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"static const {c_typename(typ)} {typ.typname}_masks[{c_ver_enum('NUM')}] = {{\n" verwidth = max(len(ver) for ver in versions) for ver in sorted(versions): - ret += ifdef_push(2, c_ver_ifdef({ver})) + ret += cutil.ifdef_push(2, c_ver_ifdef({ver})) ret += ( f"\t[{c_ver_enum(ver)}]{' '*(verwidth-len(ver))} = 0b" + "".join( @@ -787,9 +726,9 @@ const char *const _{idprefix}table_ver_name[{c_ver_enum('NUM')}] = {{ ) + ",\n" ) - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) ret += "};\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) # validate_* ############################################################### ret += """ @@ -839,7 +778,7 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE" argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"{inline} static bool validate_{typ.typname}(struct _validate_ctx *{argfn('ctx')}) {{\n" match typ: @@ -865,9 +804,9 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val # Pass 1 - declare value variables for member in typ.members: if should_save_value(typ, member): - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) ret += f"\t{c_typename(member.typ)} {member.membname};\n" - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) # Pass 2 - declare offset variables mark_offset: set[str] = set() @@ -881,7 +820,7 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val # Pass 3 - main pass ret += "\treturn false\n" for member in typ.members: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) ret += "\t || " if member.in_versions != typ.in_versions: ret += "( " + c_ver_cond(member.in_versions) + " && " @@ -925,20 +864,20 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val if member.max: assert member.static_size nbits = member.static_size * 8 - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) ret += f"\t || ({{ uint{nbits}_t max = {c_expr(member.max, lookup_sym)}; (((uint{nbits}_t){member.membname}) > max) &&\n" ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.membname} value is too large (%"PRIu{nbits}" > %"PRIu{nbits}")", {member.membname}, max); }})\n' if member.val: assert member.static_size nbits = member.static_size * 8 - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) ret += f"\t || ({{ uint{nbits}_t exp = {c_expr(member.val, lookup_sym)}; (((uint{nbits}_t){member.membname}) != exp) &&\n" ret += f'\t lib9p_errorf(ctx->ctx, LINUX_EBADMSG, "{member.membname} value is wrong (actual:%"PRIu{nbits}" != correct:%"PRIu{nbits}")", (uint{nbits}_t){member.membname}, exp); }})\n' - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) ret += "\t ;\n" ret += "}\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) # unmarshal_* ############################################################## ret += """ @@ -968,7 +907,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE" argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"{inline} static void unmarshal_{typ.typname}(struct _unmarshal_ctx *{argfn('ctx')}, {c_typename(typ)} *out) {{\n" match typ: case idl.Number(): @@ -979,7 +918,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o ret += "\tmemset(out, 0, sizeof(*out));\n" for member in typ.members: - ret += ifdef_push(2, c_ver_ifdef(member.in_versions)) + ret += cutil.ifdef_push(2, c_ver_ifdef(member.in_versions)) if member.val: ret += f"\tctx->net_offset += {member.static_size};\n" continue @@ -1005,16 +944,16 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o ret += "\t}\n" else: ret += f"unmarshal_{member.typ.typname}(ctx, &out->{member.membname});\n" - ret += ifdef_pop(1) + ret += cutil.ifdef_pop(1) ret += "}\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) # marshal_* ################################################################ ret += """ /* marshal_* ******************************************************************/ """ - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len)\n" "\tif (ctx->net_iov[ctx->net_iov_cnt-1].iov_len)\n" "\t\tctx->net_iov_cnt++;\n" @@ -1022,7 +961,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len = len;\n" "\tctx->net_iov_cnt++;\n" ) - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_BYTES(ctx, data, len)\n" "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n" "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n" @@ -1030,7 +969,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o "\tctx->net_copied_size += len;\n" "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += len;\n" ) - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_U8LE(ctx, val)\n" "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n" "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n" @@ -1038,7 +977,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o "\tctx->net_copied_size += 1;\n" "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 1;\n" ) - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_U16LE(ctx, val)\n" "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n" "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n" @@ -1046,7 +985,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o "\tctx->net_copied_size += 2;\n" "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 2;\n" ) - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_U32LE(ctx, val)\n" "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n" "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n" @@ -1054,7 +993,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o "\tctx->net_copied_size += 4;\n" "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 4;\n" ) - ret += c_macro( + ret += cutil.macro( "#define MARSHAL_U64LE(ctx, val)\n" "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n" "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n" @@ -1108,11 +1047,11 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o ) multiline += f"{'\t'*indent_depth}}}\n" for vers, sub in self.cond.items(): - multiline += ifdef_push(indent_depth + 1, c_ver_ifdef(vers)) + multiline += cutil.ifdef_push(indent_depth + 1, c_ver_ifdef(vers)) multiline += f"{'\t'*indent_depth}if {c_ver_cond(vers)} {{\n" multiline += sub.gen_c("", dstvar, root, indent_depth + 1, loop_depth) multiline += f"{'\t'*indent_depth}}}\n" - multiline += ifdef_pop(indent_depth) + multiline += cutil.ifdef_pop(indent_depth) if dsttyp: if not oneline: oneline.append("0") @@ -1204,13 +1143,13 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o continue assert isinstance(typ, idl.Struct) ret += "\n" - ret += ifdef_push(1, c_ver_ifdef(typ.in_versions)) + ret += cutil.ifdef_push(1, c_ver_ifdef(typ.in_versions)) ret += f"static bool marshal_{typ.typname}(struct _marshal_ctx *ctx, {c_typename(typ)} *val) {{\n" # Pass 1 - check size max_size = max(typ.max_size(v) for v in typ.in_versions) - if max_size > u32max: # SPECIAL (9P2000.e) + if max_size > cutil.UINT32_MAX: # SPECIAL (9P2000.e) ret += get_offset_expr(typ, go_to_end).gen_c( "uint64_t", "needed_size", "val->", 1, 0 ) @@ -1247,7 +1186,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o ret += f"{'\t'*(len(stack)-1)}}}\n" if stack[-1][1]: ifdef_depth -= 1 - ret += ifdef_pop(ifdef_depth) + ret += cutil.ifdef_pop(ifdef_depth) stack = stack[:-1] loopdepth = sum(1 for elem in path.elems if elem.cnt) @@ -1270,7 +1209,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o if name == "end": if not path.elems: nonlocal max_size - if max_size > u32max: + if max_size > cutil.UINT32_MAX: ret += f"{'\t'*len(stack)}uint32_t {name_prefix}end = (uint32_t)needed_size;\n" else: ret += f"{'\t'*len(stack)}uint32_t {name_prefix}end = needed_size;\n" @@ -1295,7 +1234,9 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o child = path.elems[-1] parent = path.elems[-2].typ if len(path.elems) > 1 else path.root if child.in_versions < parent.in_versions: - ret += ifdef_push(ifdef_depth + 1, c_ver_ifdef(child.in_versions)) + ret += cutil.ifdef_push( + ifdef_depth + 1, c_ver_ifdef(child.in_versions) + ) ifdef_depth += 1 ret += f"{'\t'*len(stack)}if ({c_ver_cond(child.in_versions)}) {{\n" stack.append((path, True)) @@ -1338,7 +1279,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o ret += "\treturn false;\n" ret += "}\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) # function tables ########################################################## ret += """ @@ -1350,20 +1291,20 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o rerror = next(typ for typ in typs if typ.typname == "Rerror") ret += f"\t[{c_ver_enum('unknown')}] = {rerror.min_size('9P2000')},\n" # SPECIAL (initialization) for ver in sorted(versions): - ret += ifdef_push(1, c_ver_ifdef({ver})) + ret += cutil.ifdef_push(1, c_ver_ifdef({ver})) ret += f"\t[{c_ver_enum(ver)}] = {rerror.min_size(ver)},\n" - ret += ifdef_pop(0) + ret += cutil.ifdef_pop(0) ret += "};\n" ret += "\n" - ret += c_macro( + ret += cutil.macro( f"#define _MSG_RECV(typ) [{idprefix.upper()}TYP_##typ/2] = {{\n" f"\t\t.basesize = sizeof(struct {idprefix}msg_##typ),\n" f"\t\t.validate = validate_##typ,\n" f"\t\t.unmarshal = (_unmarshal_fn_t)unmarshal_##typ,\n" f"\t}}\n" ) - ret += c_macro( + ret += cutil.macro( f"#define _MSG_SEND(typ) [{idprefix.upper()}TYP_##typ/2] = {{\n" f"\t\t.marshal = (_marshal_fn_t)marshal_##typ,\n" f"\t}}\n" diff --git a/lib9p/protogen/cutil.py b/lib9p/protogen/cutil.py new file mode 100644 index 0000000..a78cd17 --- /dev/null +++ b/lib9p/protogen/cutil.py @@ -0,0 +1,84 @@ +# lib9p/protogen/cutil.py - Utilities for generating C code +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +# pylint: disable=unused-variable +__all__ = [ + "UINT32_MAX", + "UINT64_MAX", + "macro", + "ifdef_init", + "ifdef_push", + "ifdef_pop", + "ifdef_leaf_is_noop", +] + +UINT32_MAX = (1 << 32) - 1 +UINT64_MAX = (1 << 64) - 1 + + +def tab_ljust(s: str, width: int) -> str: + cur = len(s.expandtabs(tabsize=8)) + if cur >= width: + return s + return s + " " * (width - cur) + + +def macro(full: str) -> str: + full = full.rstrip() + assert "\n" in full + lines = [l.rstrip() for l in full.split("\n")] + width = max(len(l.expandtabs(tabsize=8)) for l in lines[:-1]) + lines = [tab_ljust(l, width) for l in lines] + return " \\\n".join(lines).rstrip() + "\n" + + +_ifdef_stack: list[str | None] = [] + + +def ifdef_init() -> None: + global _ifdef_stack + _ifdef_stack = [] + + +def ifdef_push(n: int, _newval: str) -> str: + # Grow the stack as needed + while len(_ifdef_stack) < n: + _ifdef_stack.append(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 ifdef_leaf_is_noop() -> bool: + return not _ifdef_stack[-1] |