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