#!/usr/bin/env python3 # ex: set filetype=python: """Translate an XDR specification into executable code that can be compiled for the Linux kernel.""" import logging from argparse import Namespace from lark import logger from lark.exceptions import UnexpectedInput from generators.source_top import XdrSourceTopGenerator from generators.enum import XdrEnumGenerator from generators.pointer import XdrPointerGenerator from generators.program import XdrProgramGenerator from generators.typedef import XdrTypedefGenerator from generators.struct import XdrStructGenerator from generators.union import XdrUnionGenerator from xdr_ast import transform_parse_tree, _RpcProgram, Specification from xdr_ast import _XdrAst, _XdrEnum, _XdrPointer from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion from xdr_parse import xdr_parser, set_xdr_annotate logger.setLevel(logging.INFO) def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None: """Emit one XDR decoder function for a source file""" if isinstance(node, _XdrEnum): gen = XdrEnumGenerator(language, peer) elif isinstance(node, _XdrPointer): gen = XdrPointerGenerator(language, peer) elif isinstance(node, _XdrTypedef): gen = XdrTypedefGenerator(language, peer) elif isinstance(node, _XdrStruct): gen = XdrStructGenerator(language, peer) elif isinstance(node, _XdrUnion): gen = XdrUnionGenerator(language, peer) elif isinstance(node, _RpcProgram): gen = XdrProgramGenerator(language, peer) else: return gen.emit_decoder(node) def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None: """Emit one XDR encoder function for a source file""" if isinstance(node, _XdrEnum): gen = XdrEnumGenerator(language, peer) elif isinstance(node, _XdrPointer): gen = XdrPointerGenerator(language, peer) elif isinstance(node, _XdrTypedef): gen = XdrTypedefGenerator(language, peer) elif isinstance(node, _XdrStruct): gen = XdrStructGenerator(language, peer) elif isinstance(node, _XdrUnion): gen = XdrUnionGenerator(language, peer) elif isinstance(node, _RpcProgram): gen = XdrProgramGenerator(language, peer) else: return gen.emit_encoder(node) def generate_server_source(filename: str, root: Specification, language: str) -> None: """Generate server-side source code""" gen = XdrSourceTopGenerator(language, "server") gen.emit_source(filename, root) for definition in root.definitions: emit_source_decoder(definition.value, language, "server") for definition in root.definitions: emit_source_encoder(definition.value, language, "server") def generate_client_source(filename: str, root: Specification, language: str) -> None: """Generate server-side source code""" gen = XdrSourceTopGenerator(language, "client") gen.emit_source(filename, root) # cel: todo: client needs XDR size macros for definition in root.definitions: emit_source_encoder(definition.value, language, "client") for definition in root.definitions: emit_source_decoder(definition.value, language, "client") # cel: todo: client needs PROC macros def handle_parse_error(e: UnexpectedInput) -> bool: """Simple parse error reporting, no recovery attempted""" print(e) return True def subcmd(args: Namespace) -> int: """Generate encoder and decoder functions""" set_xdr_annotate(args.annotate) parser = xdr_parser() with open(args.filename, encoding="utf-8") as f: parse_tree = parser.parse(f.read(), on_error=handle_parse_error) ast = transform_parse_tree(parse_tree) match args.peer: case "server": generate_server_source(args.filename, ast, args.language) case "client": generate_client_source(args.filename, ast, args.language) case _: print("Code generation for", args.peer, "is not yet supported") return 0