diff options
Diffstat (limited to 'lib9p/protogen')
-rw-r--r-- | lib9p/protogen/c.py | 13 | ||||
-rw-r--r-- | lib9p/protogen/c9util.py | 22 | ||||
-rw-r--r-- | lib9p/protogen/c_format.py | 142 | ||||
-rw-r--r-- | lib9p/protogen/h.py | 6 |
4 files changed, 178 insertions, 5 deletions
diff --git a/lib9p/protogen/c.py b/lib9p/protogen/c.py index b36e817..722db7f 100644 --- a/lib9p/protogen/c.py +++ b/lib9p/protogen/c.py @@ -7,7 +7,7 @@ import sys import idl -from . import c9util, c_marshal, c_unmarshal, c_validate, cutil +from . import c9util, c_format, c_marshal, c_unmarshal, c_validate, 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 @@ -36,6 +36,14 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: #include "tables.h" #include "utf8.h" """ + # libobj vtables ########################################################### + ret += """ +/* libobj vtables *************************************************************/ +""" + for typ in typs: + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"LO_IMPLEMENTATION_C(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)}, static);\n" + ret += cutil.ifdef_pop(0) # utilities ################################################################ ret += """ @@ -102,6 +110,9 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str: # marshal_* ################################################################ ret += c_marshal.gen_c_marshal(versions, typs) + # *_format ################################################################# + ret += c_format.gen_c_format(versions, typs) + # tables.h ################################################################# ret += """ /* tables.h *******************************************************************/ diff --git a/lib9p/protogen/c9util.py b/lib9p/protogen/c9util.py index 85fd47b..cf91951 100644 --- a/lib9p/protogen/c9util.py +++ b/lib9p/protogen/c9util.py @@ -73,6 +73,20 @@ def ver_cond(versions: typing.Collection[str]) -> str: # misc ######################################################################### +def basename(typ: idl.UserType) -> str: + match typ: + case idl.Number(): + return ident(typ.typname) + case idl.Bitfield(): + return ident(typ.typname) + case idl.Message(): + return ident(f"msg_{typ.typname}") + case idl.Struct(): + return ident(typ.typname) + case _: + raise ValueError(f"not a defined type: {typ.__class__.__name__}") + + def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str: match typ: case idl.Primitive(): @@ -80,13 +94,13 @@ def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str: return "[[gnu::nonstring]] char" return f"uint{typ.value*8}_t" case idl.Number(): - return ident(f"{typ.typname}_t") + return f"{basename(typ)}_t" case idl.Bitfield(): - return ident(f"{typ.typname}_t") + return f"{basename(typ)}_t" case idl.Message(): - return f"struct {ident(f'msg_{typ.typname}')}" + return f"struct {basename(typ)}" case idl.Struct(): - return f"struct {ident(typ.typname)}" + return f"struct {basename(typ)}" case _: raise ValueError(f"not a type: {typ.__class__.__name__}") diff --git a/lib9p/protogen/c_format.py b/lib9p/protogen/c_format.py new file mode 100644 index 0000000..a1bcbf3 --- /dev/null +++ b/lib9p/protogen/c_format.py @@ -0,0 +1,142 @@ +# lib9p/protogen/c_format.py - Generate C pretty-print functions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + + +import idl + +from . import c9util, 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". + +# pylint: disable=unused-variable +__all__ = ["gen_c_format"] + + +def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str: + prefix = f"{typ.typname}_{num.numname}_".upper() + return c9util.Ident(c9util.add_prefix(prefix, base)) + + +def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str: + ret = """ +/* *_format *******************************************************************/ +""" + for typ in typs: + ret += "\n" + ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions)) + ret += f"static void {c9util.basename(typ)}_format({c9util.typename(typ)} *self, struct fmt_state *state) {{\n" + match typ: + case idl.Number(): + if typ.vals: + ret += "\tswitch (*self) {\n" + for name in typ.vals: + ret += f"\tcase {c9util.Ident(c9util.add_prefix(f'{typ.typname}_'.upper(), name))}:\n" + ret += f'\t\tfmt_state_puts(state, "{name}");\n' + ret += "\t\tbreak;\n" + ret += "\tdefault:\n" + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n' + ret += "\t}\n" + else: + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n' + case idl.Bitfield(): + val = "*self" + if typ.typname == "dm": # SPECIAL (pretty file permissions) + val = f"(*self & ~(({c9util.typename(typ)})0777))" + ret += "\tbool empty = true;\n" + ret += "\tfmt_state_putchar(state, '(');\n" + nums: set[str] = set() + + for bit in reversed(typ.bits): + match bit.cat: + case "UNUSED" | "USED" | "RESERVED": + if bit.cat == "UNUSED": + bitname = f"1<<{bit.num}" + else: + bitname = bit.bitname + ret += f"\tif ({val} & (UINT{typ.static_size*8}_C(1)<<{bit.num})) {{\n" + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_puts(state, "{bitname}");\n' + ret += "\t\tempty = false;\n" + ret += "\t}\n" + case idl.BitNum(): + if bit.cat.numname in nums: + continue + ret += f"\tswitch ({val} & {bf_numname(typ, bit.cat, 'MASK')}) {{\n" + for name in bit.cat.vals: + ret += f"\tcase {bf_numname(typ, bit.cat, name)}:\n" + bitname = c9util.add_prefix( + f"{bit.cat.numname}_".upper(), name + ) + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_puts(state, "{bitname}");\n' + ret += "\t\tempty = false;\n" + ret += "\t\tbreak;\n" + ret += "\tdefault:\n" + ret += "\t\tif (!empty)\n" + ret += "\t\t\tfmt_state_putchar(state, '|');\n" + ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n' + ret += "\t\tempty = false;\n" + ret += "\t}\n" + nums.add(bit.cat.numname) + if typ.typname == "dm": # SPECIAL (pretty file permissions) + ret += "\tif (!empty)\n" + ret += "\t\tfmt_state_putchar(state, '|');\n" + ret += f'\tfmt_state_printf(state, "%#04"PRIo{typ.static_size*8}, *self & 0777);\n' + else: + ret += "\tif (empty)\n" + ret += "\t\tfmt_state_putchar(state, '0');\n" + ret += "\tfmt_state_putchar(state, ')');\n" + case idl.Struct(typname="s"): # SPECIAL(string) + ret += "\t/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */\n" + ret += "#pragma GCC diagnostic push\n" + ret += '#pragma GCC diagnostic ignored "-Wformat"\n' + ret += '#pragma GCC diagnostic ignored "-Wformat-extra-args"\n' + ret += '\tfmt_state_printf(state, "%.*q", self->len, self->utf8);\n' + ret += "#pragma GCC diagnostic pop\n" + case idl.Struct(): # and idl.Message(): + if isinstance(typ, idl.Message): + ret += f'\tfmt_state_puts(state, "{typ.typname} {{");\n' + else: + ret += "\tfmt_state_putchar(state, '{');\n" + for member in typ.members: + if member.val: + continue + ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions)) + if member.cnt: + if member.typ.static_size == 1: # SPECIAL (data) + ret += f'\tfmt_state_puts(state, " {member.membname}=<bytedata>");\n' + continue + if isinstance(member.cnt, int): + cnt_str = str(member.cnt) + cnt_typ = "size_t" + else: + cnt_str = f"self->{member.cnt.membname}" + cnt_typ = c9util.typename(member.cnt.typ) + ret += f'\tfmt_state_puts(state, " {member.membname}=[");\n' + ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n" + ret += "\t\tif (i)\n" + ret += '\t\t\tfmt_state_puts(state, ", ");\n' + if isinstance(member.typ, idl.Primitive): + ret += f'\t\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname}[i]);\n' + else: + ret += f"\t\t{c9util.basename(member.typ)}_format(&self->{member.membname}[i], state);\n" + ret += "\t}\n" + ret += '\tfmt_state_puts(state, " ]");\n' + else: + ret += f'\tfmt_state_puts(state, " {member.membname}=");\n' + if isinstance(member.typ, idl.Primitive): + ret += f'\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname});\n' + else: + ret += f"\t{c9util.basename(member.typ)}_format(&self->{member.membname}, state);\n" + ret += cutil.ifdef_pop(1) + ret += '\tfmt_state_puts(state, " }");\n' + ret += "}\n" + ret += cutil.ifdef_pop(0) + + return ret diff --git a/lib9p/protogen/h.py b/lib9p/protogen/h.py index 13c3f89..3b33419 100644 --- a/lib9p/protogen/h.py +++ b/lib9p/protogen/h.py @@ -165,6 +165,7 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str: #include <stdint.h> /* for uint{{n}}_t types */ +#include <libfmt/fmt.h> /* for fmt_formatter */ #include <libhw/generic/net.h> /* for struct iovec */ """ @@ -206,6 +207,7 @@ enum {c9util.ident('version')} {{ ret += cutil.ifdef_pop(0) ret += f"\t{c9util.ver_enum('NUM')},\n" ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('version')}, {c9util.ident('version')});\n" ret += """ /* enum msg_type **************************************************************/ @@ -221,6 +223,7 @@ enum {c9util.ident('version')} {{ ret += f"\t{c9util.Ident(f'TYP_{msg.typname:<{namewidth}}')} = {msg.msgid},\n" ret += cutil.ifdef_pop(0) ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('msg_type')}, {c9util.ident('msg_type')});\n" ret += """ /* payload types **************************************************************/ @@ -361,6 +364,7 @@ enum {c9util.ident('version')} {{ def gen_number(typ: idl.Number) -> str: ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" def lookup_sym(sym: str) -> str: assert False @@ -379,6 +383,7 @@ def gen_number(typ: idl.Number) -> str: def gen_bitfield(typ: idl.Bitfield) -> str: ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" def lookup_sym(sym: str) -> str: assert False @@ -526,4 +531,5 @@ def gen_struct(typ: idl.Struct) -> str: # and idl.Message ret += f"\t{c9util.typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n" ret += cutil.ifdef_pop(1) ret += "};\n" + ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n" return ret |