summaryrefslogtreecommitdiff
path: root/lib9p/protogen/c9util.py
blob: e7ad9990a816c401857dfc447c3835be4fe8aa65 (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
# lib9p/protogen/c9util.py - Utilities for generating lib9p-specific C
#
# Copyright (C) 2024-2025  Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later

import typing

import idl

# 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__ = [
    "add_prefix",
    "ident",
    "Ident",
    "IDENT",
    "ver_enum",
    "ver_ifdef",
    "ver_cond",
    "typename",
    "idl_expr",
]

# idents #######################################################################


def add_prefix(p: str, s: str) -> str:
    if s.startswith("_"):
        return "_" + p + s[1:]
    return p + s


def _ident(p: str, s: str) -> str:
    return add_prefix(p, s.replace(".", "_"))


def ident(s: str) -> str:
    return _ident("lib9p_", s)


def Ident(s: str) -> str:
    return _ident("lib9p_".upper(), s)


def IDENT(s: str) -> str:
    return _ident("lib9p_", s).upper()


# versions #####################################################################


def ver_enum(ver: str) -> str:
    return Ident("VER_" + ver)


def ver_ifdef(versions: typing.Collection[str]) -> str:
    return " || ".join(
        f"CONFIG_9P_ENABLE_{v.replace('.', '_')}" for v in sorted(versions)
    )


def ver_cond(versions: typing.Collection[str]) -> str:
    if len(versions) == 1:
        v = next(v for v in versions)
        return f"is_ver(ctx, {v.replace('.', '_')})"
    return "( " + (" || ".join(ver_cond({v}) for v in sorted(versions))) + " )"


# misc #########################################################################


def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str:
    match typ:
        case idl.Primitive():
            if typ.value == 1 and parent and parent.cnt:  # SPECIAL (string)
                return "[[gnu::nonstring]] char"
            return f"uint{typ.value*8}_t"
        case idl.Number():
            return ident(f"{typ.typname}_t")
        case idl.Bitfield():
            return ident(f"{typ.typname}_t")
        case idl.Message():
            return f"struct {ident(f'msg_{typ.typname}')}"
        case idl.Struct():
            return f"struct {ident(typ.typname)}"
        case _:
            raise ValueError(f"not a type: {typ.__class__.__name__}")


def idl_expr(expr: idl.Expr, lookup_sym: typing.Callable[[str], str]) -> str:
    ret: list[str] = []
    for tok in expr.tokens:
        match tok:
            case idl.ExprOp():
                ret.append(tok.op)
            case idl.ExprLit():
                ret.append(str(tok.val))
            case idl.ExprSym(symname="s32_max"):
                ret.append("INT32_MAX")
            case idl.ExprSym(symname="s64_max"):
                ret.append("INT64_MAX")
            case idl.ExprSym():
                ret.append(lookup_sym(tok.symname))
            case _:
                assert False
    return " ".join(ret)