at master 134 lines 4.9 kB view raw
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 VisitError 12 13from generators.source_top import XdrSourceTopGenerator 14from generators.enum import XdrEnumGenerator 15from generators.passthru import XdrPassthruGenerator 16from generators.pointer import XdrPointerGenerator 17from generators.program import XdrProgramGenerator 18from generators.typedef import XdrTypedefGenerator 19from generators.struct import XdrStructGenerator 20from generators.union import XdrUnionGenerator 21 22from xdr_ast import transform_parse_tree, _RpcProgram, Specification 23from xdr_ast import _XdrAst, _XdrEnum, _XdrPassthru, _XdrPointer 24from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion 25 26from xdr_parse import xdr_parser, set_xdr_annotate, set_xdr_enum_validation 27from xdr_parse import make_error_handler, XdrParseError 28from xdr_parse import handle_transform_error 29 30logger.setLevel(logging.INFO) 31 32 33def emit_source_decoder(node: _XdrAst, language: str, peer: str) -> None: 34 """Emit one XDR decoder function for a source file""" 35 if isinstance(node, _XdrEnum): 36 gen = XdrEnumGenerator(language, peer) 37 elif isinstance(node, _XdrPointer): 38 gen = XdrPointerGenerator(language, peer) 39 elif isinstance(node, _XdrTypedef): 40 gen = XdrTypedefGenerator(language, peer) 41 elif isinstance(node, _XdrStruct): 42 gen = XdrStructGenerator(language, peer) 43 elif isinstance(node, _XdrUnion): 44 gen = XdrUnionGenerator(language, peer) 45 elif isinstance(node, _RpcProgram): 46 gen = XdrProgramGenerator(language, peer) 47 else: 48 return 49 gen.emit_decoder(node) 50 51 52def emit_source_encoder(node: _XdrAst, language: str, peer: str) -> None: 53 """Emit one XDR encoder function for a source file""" 54 if isinstance(node, _XdrEnum): 55 gen = XdrEnumGenerator(language, peer) 56 elif isinstance(node, _XdrPointer): 57 gen = XdrPointerGenerator(language, peer) 58 elif isinstance(node, _XdrTypedef): 59 gen = XdrTypedefGenerator(language, peer) 60 elif isinstance(node, _XdrStruct): 61 gen = XdrStructGenerator(language, peer) 62 elif isinstance(node, _XdrUnion): 63 gen = XdrUnionGenerator(language, peer) 64 elif isinstance(node, _RpcProgram): 65 gen = XdrProgramGenerator(language, peer) 66 else: 67 return 68 gen.emit_encoder(node) 69 70 71def generate_server_source(filename: str, root: Specification, language: str) -> None: 72 """Generate server-side source code""" 73 74 gen = XdrSourceTopGenerator(language, "server") 75 gen.emit_source(filename, root) 76 77 for definition in root.definitions: 78 if isinstance(definition.value, _XdrPassthru): 79 passthru_gen = XdrPassthruGenerator(language, "server") 80 passthru_gen.emit_decoder(definition.value) 81 else: 82 emit_source_decoder(definition.value, language, "server") 83 for definition in root.definitions: 84 if not isinstance(definition.value, _XdrPassthru): 85 emit_source_encoder(definition.value, language, "server") 86 87 88def generate_client_source(filename: str, root: Specification, language: str) -> None: 89 """Generate client-side source code""" 90 91 gen = XdrSourceTopGenerator(language, "client") 92 gen.emit_source(filename, root) 93 94 for definition in root.definitions: 95 if isinstance(definition.value, _XdrPassthru): 96 passthru_gen = XdrPassthruGenerator(language, "client") 97 passthru_gen.emit_decoder(definition.value) 98 else: 99 emit_source_encoder(definition.value, language, "client") 100 for definition in root.definitions: 101 if not isinstance(definition.value, _XdrPassthru): 102 emit_source_decoder(definition.value, language, "client") 103 104 # cel: todo: client needs PROC macros 105 106 107def subcmd(args: Namespace) -> int: 108 """Generate encoder and decoder functions""" 109 110 set_xdr_annotate(args.annotate) 111 set_xdr_enum_validation(not args.no_enum_validation) 112 parser = xdr_parser() 113 with open(args.filename, encoding="utf-8") as f: 114 source = f.read() 115 try: 116 parse_tree = parser.parse( 117 source, on_error=make_error_handler(source, args.filename) 118 ) 119 except XdrParseError: 120 return 1 121 try: 122 ast = transform_parse_tree(parse_tree) 123 except VisitError as e: 124 handle_transform_error(e, source, args.filename) 125 return 1 126 match args.peer: 127 case "server": 128 generate_server_source(args.filename, ast, args.language) 129 case "client": 130 generate_client_source(args.filename, ast, args.language) 131 case _: 132 print("Code generation for", args.peer, "is not yet supported") 133 134 return 0