xref: /linux/tools/net/sunrpc/xdrgen/subcmds/source.py (revision c34e9ab9a612ee8b18273398ef75c207b01f516d)
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
8
9from argparse import Namespace
10from lark import logger
11from lark.exceptions import UnexpectedInput
12
13from generators.source_top import XdrSourceTopGenerator
14from generators.enum import XdrEnumGenerator
15from generators.pointer import XdrPointerGenerator
16from generators.program import XdrProgramGenerator
17from generators.typedef import XdrTypedefGenerator
18from generators.struct import XdrStructGenerator
19from generators.union import XdrUnionGenerator
20
21from xdr_ast import transform_parse_tree, _RpcProgram, Specification
22from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer
23from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
24
25from xdr_parse import xdr_parser, set_xdr_annotate
26
27logger.setLevel(logging.INFO)
28
29
30def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None:
31    """Emit one XDR decoder function for a source file"""
32    if isinstance(node, _XdrEnum):
33        gen = XdrEnumGenerator(language, peer)
34    elif isinstance(node, _XdrPointer):
35        gen = XdrPointerGenerator(language, peer)
36    elif isinstance(node, _XdrTypedef):
37        gen = XdrTypedefGenerator(language, peer)
38    elif isinstance(node, _XdrStruct):
39        gen = XdrStructGenerator(language, peer)
40    elif isinstance(node, _XdrUnion):
41        gen = XdrUnionGenerator(language, peer)
42    elif isinstance(node, _RpcProgram):
43        gen = XdrProgramGenerator(language, peer)
44    else:
45        return
46    gen.emit_decoder(node)
47
48
49def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None:
50    """Emit one XDR encoder function for a source file"""
51    if isinstance(node, _XdrEnum):
52        gen = XdrEnumGenerator(language, peer)
53    elif isinstance(node, _XdrPointer):
54        gen = XdrPointerGenerator(language, peer)
55    elif isinstance(node, _XdrTypedef):
56        gen = XdrTypedefGenerator(language, peer)
57    elif isinstance(node, _XdrStruct):
58        gen = XdrStructGenerator(language, peer)
59    elif isinstance(node, _XdrUnion):
60        gen = XdrUnionGenerator(language, peer)
61    elif isinstance(node, _RpcProgram):
62        gen = XdrProgramGenerator(language, peer)
63    else:
64        return
65    gen.emit_encoder(node)
66
67
68def generate_server_source(filename: str, root: Specification, language: str) -> None:
69    """Generate server-side source code"""
70
71    gen = XdrSourceTopGenerator(language, "server")
72    gen.emit_source(filename, root)
73
74    for definition in root.definitions:
75        emit_source_decoder(definition.value, language, "server")
76    for definition in root.definitions:
77        emit_source_encoder(definition.value, language, "server")
78
79
80def generate_client_source(filename: str, root: Specification, language: str) -> None:
81    """Generate server-side source code"""
82
83    gen = XdrSourceTopGenerator(language, "client")
84    gen.emit_source(filename, root)
85
86    print("")
87    for definition in root.definitions:
88        emit_source_encoder(definition.value, language, "client")
89    for definition in root.definitions:
90        emit_source_decoder(definition.value, language, "client")
91
92    # cel: todo: client needs PROC macros
93
94
95def handle_parse_error(e: UnexpectedInput) -> bool:
96    """Simple parse error reporting, no recovery attempted"""
97    print(e)
98    return True
99
100
101def subcmd(args: Namespace) -> int:
102    """Generate encoder and decoder functions"""
103
104    set_xdr_annotate(args.annotate)
105    parser = xdr_parser()
106    with open(args.filename, encoding="utf-8") as f:
107        parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
108        ast = transform_parse_tree(parse_tree)
109        match args.peer:
110            case "server":
111                generate_server_source(args.filename, ast, args.language)
112            case "client":
113                generate_client_source(args.filename, ast, args.language)
114            case _:
115                print("Code generation for", args.peer, "is not yet supported")
116
117    return 0
118