xref: /linux/tools/net/sunrpc/xdrgen/subcmds/source.py (revision fcc202d324e5d2a21557ddd20fc3f96e08c4f72d)
1#!/usr/bin/env python3
2# ex: set filetype=python:
3
4"""Translate an XDR specification into executable code that
5can be compiled for the Linux kernel."""
6
7import logging
8import sys
9
10from argparse import Namespace
11from lark import logger
12from lark.exceptions import VisitError
13
14from generators.source_top import XdrSourceTopGenerator
15from generators.enum import XdrEnumGenerator
16from generators.passthru import XdrPassthruGenerator
17from generators.pointer import XdrPointerGenerator
18from generators.program import XdrProgramGenerator
19from generators.typedef import XdrTypedefGenerator
20from generators.struct import XdrStructGenerator
21from generators.union import XdrUnionGenerator
22
23from xdr_ast import transform_parse_tree, _RpcProgram, Specification
24from xdr_ast import _XdrAst, _XdrEnum, _XdrPassthru, _XdrPointer
25from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion, XdrSemanticError
26
27from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation
28from xdr_parse import make_error_handler, XdrParseError
29from xdr_parse import handle_transform_error, handle_semantic_error
30
31logger.setLevel(logging.INFO)
32
33
34def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
35    """Emit one XDR decoder function for a source file"""
36    if isinstance(node, _XdrEnum):
37        gen = XdrEnumGenerator(language, peer)
38    elif isinstance(node, _XdrPointer):
39        gen = XdrPointerGenerator(language, peer)
40    elif isinstance(node, _XdrTypedef):
41        gen = XdrTypedefGenerator(language, peer)
42    elif isinstance(node, _XdrStruct):
43        gen = XdrStructGenerator(language, peer)
44    elif isinstance(node, _XdrUnion):
45        gen = XdrUnionGenerator(language, peer)
46    elif isinstance(node, _RpcProgram):
47        gen = XdrProgramGenerator(language, peer)
48    else:
49        return
50    gen.emit_decoder(node)
51
52
53def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
54    """Emit one XDR encoder function for a source file"""
55    if isinstance(node, _XdrEnum):
56        gen = XdrEnumGenerator(language, peer)
57    elif isinstance(node, _XdrPointer):
58        gen = XdrPointerGenerator(language, peer)
59    elif isinstance(node, _XdrTypedef):
60        gen = XdrTypedefGenerator(language, peer)
61    elif isinstance(node, _XdrStruct):
62        gen = XdrStructGenerator(language, peer)
63    elif isinstance(node, _XdrUnion):
64        gen = XdrUnionGenerator(language, peer)
65    elif isinstance(node, _RpcProgram):
66        gen = XdrProgramGenerator(language, peer)
67    else:
68        return
69    gen.emit_encoder(node)
70
71
72def generate_server_source(filename: str, root: Specification, language: str) -> None:
73    """Generate server-side source code"""
74
75    gen = XdrSourceTopGenerator(language, "server")
76    gen.emit_source(filename, root)
77
78    for definition in root.definitions:
79        if isinstance(definition.value, _XdrPassthru):
80            passthru_gen = XdrPassthruGenerator(language, "server")
81            passthru_gen.emit_decoder(definition.value)
82        else:
83            emit_source_decoder(definition.value, language, "server")
84    for definition in root.definitions:
85        if not isinstance(definition.value, _XdrPassthru):
86            emit_source_encoder(definition.value, language, "server")
87
88
89def generate_client_source(filename: str, root: Specification, language: str) -> None:
90    """Generate client-side source code"""
91
92    gen = XdrSourceTopGenerator(language, "client")
93    gen.emit_source(filename, root)
94
95    for definition in root.definitions:
96        if isinstance(definition.value, _XdrPassthru):
97            passthru_gen = XdrPassthruGenerator(language, "client")
98            passthru_gen.emit_decoder(definition.value)
99        else:
100            emit_source_encoder(definition.value, language, "client")
101    for definition in root.definitions:
102        if not isinstance(definition.value, _XdrPassthru):
103            emit_source_decoder(definition.value, language, "client")
104
105    # cel: todo: client needs PROC macros
106
107
108def subcmd(args: Namespace) -> int:
109    """Generate encoder and decoder functions"""
110
111    set_xdr_annotate(args.annotate)
112    set_xdr_enum_validation(not args.no_enum_validation)
113    parser = xdr_parser()
114    with open(args.filename, encoding="utf-8") as f:
115        source = f.read()
116        try:
117            parse_tree = parser.parse(
118                source, on_error=make_error_handler(source, args.filename)
119            )
120        except XdrParseError:
121            return 1
122        try:
123            ast = transform_parse_tree(parse_tree)
124        except VisitError as e:
125            handle_transform_error(e, source, args.filename)
126            return 1
127        except XdrSemanticError as e:
128            handle_semantic_error(e, source, args.filename)
129            return 1
130        try:
131            match args.peer:
132                case "server":
133                    generate_server_source(args.filename, ast, args.language)
134                case "client":
135                    generate_client_source(args.filename, ast, args.language)
136                case _:
137                    print("Code generation for", args.peer, "is not yet supported")
138        except NotImplementedError as e:
139            sys.stderr.write(f"{args.filename}: {e}\n")
140            return 1
141
142    return 0
143