summaryrefslogtreecommitdiff
path: root/lib9p/core_gen/c_fmt_print.py
blob: eaacddbe771be8f7f8af9e0f7ba1fe36cff33045 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# lib9p/core_gen/c_fmt_print.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, idlutil

# 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_fmt_print"]


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_fmt_print(versions: set[str], typs: list[idl.UserType]) -> str:
    ret = """
/* fmt_print_* ****************************************************************/
"""
    for typ in idlutil.topo_sorted(typs):
        ret += "\n"
        ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
        storage = "" if typ.typname == "stat" else "static "  # SPECIAL (stat is public)
        ret += f"[[maybe_unused]] {storage}void fmt_print_{typ.typname}(lo_interface fmt_dest w, [[maybe_unused]] struct lib9p_ctx *ctx, {c9util.typename(typ)} *self) {{\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_print_str(w, "{name}");\n'
                        ret += "\t\tbreak;\n"
                    ret += "\tdefault:\n"
                    ret += "\t\tfmt_print_base10(w, *self);\n"
                    ret += "\t}\n"
                else:
                    ret += "\t\tfmt_print_base10(w, *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_print_byte(w, '(');\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_print_byte(w, '|');\n"
                            ret += f'\t\tfmt_print_str(w, "{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_print_byte(w, '|');\n"
                                ret += f'\t\tfmt_print_str(w, "{bitname}");\n'
                                ret += "\t\tempty = false;\n"
                                ret += "\t\tbreak;\n"
                            ret += "\tdefault:\n"
                            ret += "\t\tif (!empty)\n"
                            ret += "\t\t\tfmt_print_byte(w, '|');\n"
                            ret += f"\t\tfmt_print_base10(w, {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_print_byte(w, '|');\n"
                    ret += "\tfmt_print(w, (rjust, 4, '0', (base8, *self & 0777)));\n"
                else:
                    ret += "\tif (empty)\n"
                    ret += "\t\tfmt_print_byte(w, '0');\n"
                ret += "\tfmt_print_byte(w, ')');\n"
            case idl.Struct(typname="s"):  # SPECIAL (string)
                ret += "\tfmt_print_qmem(w, self->utf8, self->len);\n"
            case idl.Struct():  # and idl.Message():
                if isinstance(typ, idl.Message):
                    ret += f'\tfmt_print_str(w, "{typ.typname} {{");\n'
                else:
                    ret += "\tfmt_print_byte(w, '{');\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 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)
                        if member.typ.static_size == 1:  # SPECIAL (data)
                            ret += f"\tif (is_valid_utf8_without_nul((uint8_t *)self->{member.membname}, (size_t){cnt_str})) {{\n"
                            ret += f'\t\tfmt_print_str(w, " {member.membname}=");\n'
                            ret += f"\t\tfmt_print_qmem(w, self->{member.membname}, {cnt_str} < 50 ? {cnt_str} : 50);\n"
                            ret += f"\t\tif ({cnt_str} > 50)\n"
                            ret += '\t\t\tfmt_print_str(w, "...");\n'
                            ret += "\t} else {\n"
                            ret += f'\t\tfmt_print_str(w, " {member.membname}=<bytedata>");\n'
                            ret += "\t}\n"
                            continue
                        ret += f'\tfmt_print_str(w, " {member.membname}=[");\n'
                        ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n"
                        ret += "\t\tif (i)\n"
                        ret += "\t\t\tfmt_print_byte(w, ',');\n"
                        ret += "\t\tfmt_print_byte(w, ' ');\n"
                        if isinstance(member.typ, idl.Primitive):
                            ret += f"\t\tfmt_print_base10(w, self->{member.membname}[i]);\n"
                        else:
                            ret += f"\t\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname}[i]);\n"
                        ret += "\t}\n"
                        ret += '\tfmt_print_str(w, " ]");\n'
                    else:
                        ret += f'\tfmt_print_str(w, " {member.membname}=");\n'
                        if isinstance(member.typ, idl.Primitive):
                            ret += f"\tfmt_print_base10(w, self->{member.membname});\n"
                        else:
                            ret += f"\tfmt_print_{member.typ.typname}(w, ctx, &self->{member.membname});\n"
                ret += cutil.ifdef_pop(1)
                ret += '\tfmt_print_str(w, " }");\n'
        ret += "}\n"
    ret += cutil.ifdef_pop(0)

    return ret