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 # cel: todo: client needs XDR size macros 87 88 for definition in root.definitions: 89 emit_source_encoder(definition.value, language, "client") 90 for definition in root.definitions: 91 emit_source_decoder(definition.value, language, "client") 92 93 # cel: todo: client needs PROC macros 94 95 96def handle_parse_error(e: UnexpectedInput) -> bool: 97 """Simple parse error reporting, no recovery attempted""" 98 print(e) 99 return True 100 101 102def subcmd(args: Namespace) -> int: 103 """Generate encoder and decoder functions""" 104 105 set_xdr_annotate(args.annotate) 106 parser = xdr_parser() 107 with open(args.filename, encoding="utf-8") as f: 108 parse_tree = parser.parse(f.read(), on_error=handle_parse_error) 109 ast = transform_parse_tree(parse_tree) 110 match args.peer: 111 case "server": 112 generate_server_source(args.filename, ast, args.language) 113 case "client": 114 generate_client_source(args.filename, ast, args.language) 115 case _: 116 print("Code generation for", args.peer, "is not yet supported") 117 118 return 0 119