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