511 lines · python
1#!/usr/bin/env python32# ex: set filetype=python:3 4"""Define and implement the Abstract Syntax Tree for the XDR language."""5 6import sys7from typing import List8from dataclasses import dataclass9 10from lark import ast_utils, Transformer11from lark.tree import Meta12 13this_module = sys.modules[__name__]14 15excluded_apis = []16header_name = "none"17public_apis = []18enums = set()19structs = set()20pass_by_reference = set()21 22 23@dataclass24class _XdrAst(ast_utils.Ast):25 """Base class for the XDR abstract syntax tree"""26 27 28@dataclass29class _XdrIdentifier(_XdrAst):30 """Corresponds to 'identifier' in the XDR language grammar"""31 32 symbol: str33 34 35@dataclass36class _XdrValue(_XdrAst):37 """Corresponds to 'value' in the XDR language grammar"""38 39 value: str40 41 42@dataclass43class _XdrConstantValue(_XdrAst):44 """Corresponds to 'constant' in the XDR language grammar"""45 46 value: int47 48 49@dataclass50class _XdrTypeSpecifier(_XdrAst):51 """Corresponds to 'type_specifier' in the XDR language grammar"""52 53 type_name: str54 c_classifier: str55 56 57@dataclass58class _XdrDefinedType(_XdrTypeSpecifier):59 """Corresponds to a type defined by the input specification"""60 61 62@dataclass63class _XdrBuiltInType(_XdrTypeSpecifier):64 """Corresponds to a built-in XDR type"""65 66 67@dataclass68class _XdrDeclaration(_XdrAst):69 """Base class of XDR type declarations"""70 71 72@dataclass73class _XdrFixedLengthOpaque(_XdrDeclaration):74 """A fixed-length opaque declaration"""75 76 name: str77 size: str78 template: str = "fixed_length_opaque"79 80 81@dataclass82class _XdrVariableLengthOpaque(_XdrDeclaration):83 """A variable-length opaque declaration"""84 85 name: str86 maxsize: str87 template: str = "variable_length_opaque"88 89 90@dataclass91class _XdrVariableLengthString(_XdrDeclaration):92 """A (NUL-terminated) variable-length string declaration"""93 94 name: str95 maxsize: str96 template: str = "variable_length_string"97 98 99@dataclass100class _XdrFixedLengthArray(_XdrDeclaration):101 """A fixed-length array declaration"""102 103 name: str104 spec: _XdrTypeSpecifier105 size: str106 template: str = "fixed_length_array"107 108 109@dataclass110class _XdrVariableLengthArray(_XdrDeclaration):111 """A variable-length array declaration"""112 113 name: str114 spec: _XdrTypeSpecifier115 maxsize: str116 template: str = "variable_length_array"117 118 119@dataclass120class _XdrOptionalData(_XdrDeclaration):121 """An 'optional_data' declaration"""122 123 name: str124 spec: _XdrTypeSpecifier125 template: str = "optional_data"126 127 128@dataclass129class _XdrBasic(_XdrDeclaration):130 """A 'basic' declaration"""131 132 name: str133 spec: _XdrTypeSpecifier134 template: str = "basic"135 136 137@dataclass138class _XdrVoid(_XdrDeclaration):139 """A void declaration"""140 141 template: str = "void"142 143 144@dataclass145class _XdrConstant(_XdrAst):146 """Corresponds to 'constant_def' in the grammar"""147 148 name: str149 value: str150 151 152@dataclass153class _XdrEnumerator(_XdrAst):154 """An 'identifier = value' enumerator"""155 156 name: str157 value: str158 159 160@dataclass161class _XdrEnum(_XdrAst):162 """An XDR enum definition"""163 164 name: str165 minimum: int166 maximum: int167 enumerators: List[_XdrEnumerator]168 169 170@dataclass171class _XdrStruct(_XdrAst):172 """An XDR struct definition"""173 174 name: str175 fields: List[_XdrDeclaration]176 177 178@dataclass179class _XdrPointer(_XdrAst):180 """An XDR pointer definition"""181 182 name: str183 fields: List[_XdrDeclaration]184 185 186@dataclass187class _XdrTypedef(_XdrAst):188 """An XDR typedef"""189 190 declaration: _XdrDeclaration191 192 193@dataclass194class _XdrCaseSpec(_XdrAst):195 """One case in an XDR union"""196 197 values: List[str]198 arm: _XdrDeclaration199 template: str = "case_spec"200 201 202@dataclass203class _XdrDefaultSpec(_XdrAst):204 """Default case in an XDR union"""205 206 arm: _XdrDeclaration207 template: str = "default_spec"208 209 210@dataclass211class _XdrUnion(_XdrAst):212 """An XDR union"""213 214 name: str215 discriminant: _XdrDeclaration216 cases: List[_XdrCaseSpec]217 default: _XdrDeclaration218 219 220@dataclass221class _RpcProcedure(_XdrAst):222 """RPC procedure definition"""223 224 name: str225 number: str226 argument: _XdrTypeSpecifier227 result: _XdrTypeSpecifier228 229 230@dataclass231class _RpcVersion(_XdrAst):232 """RPC version definition"""233 234 name: str235 number: str236 procedures: List[_RpcProcedure]237 238 239@dataclass240class _RpcProgram(_XdrAst):241 """RPC program definition"""242 243 name: str244 number: str245 versions: List[_RpcVersion]246 247 248@dataclass249class _Pragma(_XdrAst):250 """Empty class for pragma directives"""251 252 253@dataclass254class Definition(_XdrAst, ast_utils.WithMeta):255 """Corresponds to 'definition' in the grammar"""256 257 meta: Meta258 value: _XdrAst259 260 261@dataclass262class Specification(_XdrAst, ast_utils.AsList):263 """Corresponds to 'specification' in the grammar"""264 265 definitions: List[Definition]266 267 268class ParseToAst(Transformer):269 """Functions that transform productions into AST nodes"""270 271 def identifier(self, children):272 """Instantiate one _XdrIdentifier object"""273 return _XdrIdentifier(children[0].value)274 275 def value(self, children):276 """Instantiate one _XdrValue object"""277 if isinstance(children[0], _XdrIdentifier):278 return _XdrValue(children[0].symbol)279 return _XdrValue(children[0].children[0].value)280 281 def constant(self, children):282 """Instantiate one _XdrConstantValue object"""283 match children[0].data:284 case "decimal_constant":285 value = int(children[0].children[0].value, base=10)286 case "hexadecimal_constant":287 value = int(children[0].children[0].value, base=16)288 case "octal_constant":289 value = int(children[0].children[0].value, base=8)290 return _XdrConstantValue(value)291 292 def type_specifier(self, children):293 """Instantiate one type_specifier object"""294 c_classifier = ""295 if isinstance(children[0], _XdrIdentifier):296 name = children[0].symbol297 if name in enums:298 c_classifier = "enum "299 if name in structs:300 c_classifier = "struct "301 return _XdrDefinedType(302 type_name=name,303 c_classifier=c_classifier,304 )305 306 token = children[0].data307 return _XdrBuiltInType(308 type_name=token.value,309 c_classifier=c_classifier,310 )311 312 def constant_def(self, children):313 """Instantiate one _XdrConstant object"""314 name = children[0].symbol315 value = children[1].value316 return _XdrConstant(name, value)317 318 # cel: Python can compute a min() and max() for the enumerator values319 # so that the generated code can perform proper range checking.320 def enum(self, children):321 """Instantiate one _XdrEnum object"""322 enum_name = children[0].symbol323 enums.add(enum_name)324 325 i = 0326 enumerators = []327 body = children[1]328 while i < len(body.children):329 name = body.children[i].symbol330 value = body.children[i + 1].value331 enumerators.append(_XdrEnumerator(name, value))332 i = i + 2333 334 return _XdrEnum(enum_name, 0, 0, enumerators)335 336 def fixed_length_opaque(self, children):337 """Instantiate one _XdrFixedLengthOpaque declaration object"""338 name = children[0].symbol339 size = children[1].value340 341 return _XdrFixedLengthOpaque(name, size)342 343 def variable_length_opaque(self, children):344 """Instantiate one _XdrVariableLengthOpaque declaration object"""345 name = children[0].symbol346 if children[1] is not None:347 maxsize = children[1].value348 else:349 maxsize = "0"350 351 return _XdrVariableLengthOpaque(name, maxsize)352 353 def variable_length_string(self, children):354 """Instantiate one _XdrVariableLengthString declaration object"""355 name = children[0].symbol356 if children[1] is not None:357 maxsize = children[1].value358 else:359 maxsize = "0"360 361 return _XdrVariableLengthString(name, maxsize)362 363 def fixed_length_array(self, children):364 """Instantiate one _XdrFixedLengthArray declaration object"""365 spec = children[0]366 name = children[1].symbol367 size = children[2].value368 369 return _XdrFixedLengthArray(name, spec, size)370 371 def variable_length_array(self, children):372 """Instantiate one _XdrVariableLengthArray declaration object"""373 spec = children[0]374 name = children[1].symbol375 if children[2] is not None:376 maxsize = children[2].value377 else:378 maxsize = "0"379 380 return _XdrVariableLengthArray(name, spec, maxsize)381 382 def optional_data(self, children):383 """Instantiate one _XdrOptionalData declaration object"""384 spec = children[0]385 name = children[1].symbol386 structs.add(name)387 pass_by_reference.add(name)388 389 return _XdrOptionalData(name, spec)390 391 def basic(self, children):392 """Instantiate one _XdrBasic object"""393 spec = children[0]394 name = children[1].symbol395 396 return _XdrBasic(name, spec)397 398 def void(self, children):399 """Instantiate one _XdrVoid declaration object"""400 401 return _XdrVoid()402 403 def struct(self, children):404 """Instantiate one _XdrStruct object"""405 name = children[0].symbol406 structs.add(name)407 pass_by_reference.add(name)408 fields = children[1].children409 410 last_field = fields[-1]411 if (412 isinstance(last_field, _XdrOptionalData)413 and name == last_field.spec.type_name414 ):415 return _XdrPointer(name, fields)416 417 return _XdrStruct(name, fields)418 419 def typedef(self, children):420 """Instantiate one _XdrTypedef object"""421 new_type = children[0]422 if isinstance(new_type, _XdrBasic) and isinstance(423 new_type.spec, _XdrDefinedType424 ):425 if new_type.spec.type_name in pass_by_reference:426 pass_by_reference.add(new_type.name)427 428 return _XdrTypedef(new_type)429 430 def case_spec(self, children):431 """Instantiate one _XdrCaseSpec object"""432 values = []433 for item in children[0:-1]:434 values.append(item.value)435 arm = children[-1]436 437 return _XdrCaseSpec(values, arm)438 439 def default_spec(self, children):440 """Instantiate one _XdrDefaultSpec object"""441 arm = children[0]442 443 return _XdrDefaultSpec(arm)444 445 def union(self, children):446 """Instantiate one _XdrUnion object"""447 name = children[0].symbol448 structs.add(name)449 pass_by_reference.add(name)450 451 body = children[1]452 discriminant = body.children[0].children[0]453 cases = body.children[1:-1]454 default = body.children[-1]455 456 return _XdrUnion(name, discriminant, cases, default)457 458 def procedure_def(self, children):459 """Instantiate one _RpcProcedure object"""460 result = children[0]461 name = children[1].symbol462 argument = children[2]463 number = children[3].value464 465 return _RpcProcedure(name, number, argument, result)466 467 def version_def(self, children):468 """Instantiate one _RpcVersion object"""469 name = children[0].symbol470 number = children[-1].value471 procedures = children[1:-1]472 473 return _RpcVersion(name, number, procedures)474 475 def program_def(self, children):476 """Instantiate one _RpcProgram object"""477 name = children[0].symbol478 number = children[-1].value479 versions = children[1:-1]480 481 return _RpcProgram(name, number, versions)482 483 def pragma_def(self, children):484 """Instantiate one _Pragma object"""485 directive = children[0].children[0].data486 match directive:487 case "exclude_directive":488 excluded_apis.append(children[1].symbol)489 case "header_directive":490 global header_name491 header_name = children[1].symbol492 case "public_directive":493 public_apis.append(children[1].symbol)494 case _:495 raise NotImplementedError("Directive not supported")496 return _Pragma()497 498 499transformer = ast_utils.create_transformer(this_module, ParseToAst())500 501 502def transform_parse_tree(parse_tree):503 """Transform productions into an abstract syntax tree"""504 505 return transformer.transform(parse_tree)506 507 508def get_header_name() -> str:509 """Return header name set by pragma header directive"""510 return header_name511