1#!/usr/bin/env python3 2# ex: set filetype=python: 3 4"""Define and implement the Abstract Syntax Tree for the XDR language.""" 5 6import sys 7from typing import List 8from dataclasses import dataclass 9 10from lark import ast_utils, Transformer 11from lark.tree import Meta 12 13this_module = sys.modules[__name__] 14 15big_endian = [] 16excluded_apis = [] 17header_name = "none" 18public_apis = [] 19structs = set() 20pass_by_reference = set() 21 22constants = {} 23 24 25def xdr_quadlen(val: str) -> int: 26 """Return integer XDR width of an XDR type""" 27 if val in constants: 28 octets = constants[val] 29 else: 30 octets = int(val) 31 return int((octets + 3) / 4) 32 33 34symbolic_widths = { 35 "void": ["XDR_void"], 36 "bool": ["XDR_bool"], 37 "int": ["XDR_int"], 38 "unsigned_int": ["XDR_unsigned_int"], 39 "long": ["XDR_long"], 40 "unsigned_long": ["XDR_unsigned_long"], 41 "hyper": ["XDR_hyper"], 42 "unsigned_hyper": ["XDR_unsigned_hyper"], 43} 44 45# Numeric XDR widths are tracked in a dictionary that is keyed 46# by type_name because sometimes a caller has nothing more than 47# the type_name to use to figure out the numeric width. 48max_widths = { 49 "void": 0, 50 "bool": 1, 51 "int": 1, 52 "unsigned_int": 1, 53 "long": 1, 54 "unsigned_long": 1, 55 "hyper": 2, 56 "unsigned_hyper": 2, 57} 58 59 60@dataclass 61class _XdrAst(ast_utils.Ast): 62 """Base class for the XDR abstract syntax tree""" 63 64 65@dataclass 66class _XdrIdentifier(_XdrAst): 67 """Corresponds to 'identifier' in the XDR language grammar""" 68 69 symbol: str 70 71 72@dataclass 73class _XdrValue(_XdrAst): 74 """Corresponds to 'value' in the XDR language grammar""" 75 76 value: str 77 78 79@dataclass 80class _XdrConstantValue(_XdrAst): 81 """Corresponds to 'constant' in the XDR language grammar""" 82 83 value: int 84 85 86@dataclass 87class _XdrTypeSpecifier(_XdrAst): 88 """Corresponds to 'type_specifier' in the XDR language grammar""" 89 90 type_name: str 91 c_classifier: str = "" 92 93 94@dataclass 95class _XdrDefinedType(_XdrTypeSpecifier): 96 """Corresponds to a type defined by the input specification""" 97 98 def symbolic_width(self) -> List: 99 """Return list containing XDR width of type's components""" 100 return [get_header_name().upper() + "_" + self.type_name + "_sz"] 101 102 def __post_init__(self): 103 if self.type_name in structs: 104 self.c_classifier = "struct " 105 symbolic_widths[self.type_name] = self.symbolic_width() 106 107 108@dataclass 109class _XdrBuiltInType(_XdrTypeSpecifier): 110 """Corresponds to a built-in XDR type""" 111 112 def symbolic_width(self) -> List: 113 """Return list containing XDR width of type's components""" 114 return symbolic_widths[self.type_name] 115 116 117@dataclass 118class _XdrDeclaration(_XdrAst): 119 """Base class of XDR type declarations""" 120 121 122@dataclass 123class _XdrFixedLengthOpaque(_XdrDeclaration): 124 """A fixed-length opaque declaration""" 125 126 name: str 127 size: str 128 template: str = "fixed_length_opaque" 129 130 def max_width(self) -> int: 131 """Return width of type in XDR_UNITS""" 132 return xdr_quadlen(self.size) 133 134 def symbolic_width(self) -> List: 135 """Return list containing XDR width of type's components""" 136 return ["XDR_QUADLEN(" + self.size + ")"] 137 138 def __post_init__(self): 139 max_widths[self.name] = self.max_width() 140 symbolic_widths[self.name] = self.symbolic_width() 141 142 143@dataclass 144class _XdrVariableLengthOpaque(_XdrDeclaration): 145 """A variable-length opaque declaration""" 146 147 name: str 148 maxsize: str 149 template: str = "variable_length_opaque" 150 151 def max_width(self) -> int: 152 """Return width of type in XDR_UNITS""" 153 return 1 + xdr_quadlen(self.maxsize) 154 155 def symbolic_width(self) -> List: 156 """Return list containing XDR width of type's components""" 157 widths = ["XDR_unsigned_int"] 158 if self.maxsize != "0": 159 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 160 return widths 161 162 def __post_init__(self): 163 max_widths[self.name] = self.max_width() 164 symbolic_widths[self.name] = self.symbolic_width() 165 166 167@dataclass 168class _XdrString(_XdrDeclaration): 169 """A (NUL-terminated) variable-length string declaration""" 170 171 name: str 172 maxsize: str 173 template: str = "string" 174 175 def max_width(self) -> int: 176 """Return width of type in XDR_UNITS""" 177 return 1 + xdr_quadlen(self.maxsize) 178 179 def symbolic_width(self) -> List: 180 """Return list containing XDR width of type's components""" 181 widths = ["XDR_unsigned_int"] 182 if self.maxsize != "0": 183 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 184 return widths 185 186 def __post_init__(self): 187 max_widths[self.name] = self.max_width() 188 symbolic_widths[self.name] = self.symbolic_width() 189 190 191@dataclass 192class _XdrFixedLengthArray(_XdrDeclaration): 193 """A fixed-length array declaration""" 194 195 name: str 196 spec: _XdrTypeSpecifier 197 size: str 198 template: str = "fixed_length_array" 199 200 def max_width(self) -> int: 201 """Return width of type in XDR_UNITS""" 202 return xdr_quadlen(self.size) * max_widths[self.spec.type_name] 203 204 def symbolic_width(self) -> List: 205 """Return list containing XDR width of type's components""" 206 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 207 return ["(" + self.size + " * (" + item_width + "))"] 208 209 def __post_init__(self): 210 max_widths[self.name] = self.max_width() 211 symbolic_widths[self.name] = self.symbolic_width() 212 213 214@dataclass 215class _XdrVariableLengthArray(_XdrDeclaration): 216 """A variable-length array declaration""" 217 218 name: str 219 spec: _XdrTypeSpecifier 220 maxsize: str 221 template: str = "variable_length_array" 222 223 def max_width(self) -> int: 224 """Return width of type in XDR_UNITS""" 225 return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name]) 226 227 def symbolic_width(self) -> List: 228 """Return list containing XDR width of type's components""" 229 widths = ["XDR_unsigned_int"] 230 if self.maxsize != "0": 231 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 232 widths.append("(" + self.maxsize + " * (" + item_width + "))") 233 return widths 234 235 def __post_init__(self): 236 max_widths[self.name] = self.max_width() 237 symbolic_widths[self.name] = self.symbolic_width() 238 239 240@dataclass 241class _XdrOptionalData(_XdrDeclaration): 242 """An 'optional_data' declaration""" 243 244 name: str 245 spec: _XdrTypeSpecifier 246 template: str = "optional_data" 247 248 def max_width(self) -> int: 249 """Return width of type in XDR_UNITS""" 250 return 1 251 252 def symbolic_width(self) -> List: 253 """Return list containing XDR width of type's components""" 254 return ["XDR_bool"] 255 256 def __post_init__(self): 257 structs.add(self.name) 258 pass_by_reference.add(self.name) 259 max_widths[self.name] = self.max_width() 260 symbolic_widths[self.name] = self.symbolic_width() 261 262 263@dataclass 264class _XdrBasic(_XdrDeclaration): 265 """A 'basic' declaration""" 266 267 name: str 268 spec: _XdrTypeSpecifier 269 template: str = "basic" 270 271 def max_width(self) -> int: 272 """Return width of type in XDR_UNITS""" 273 return max_widths[self.spec.type_name] 274 275 def symbolic_width(self) -> List: 276 """Return list containing XDR width of type's components""" 277 return symbolic_widths[self.spec.type_name] 278 279 def __post_init__(self): 280 max_widths[self.name] = self.max_width() 281 symbolic_widths[self.name] = self.symbolic_width() 282 283 284@dataclass 285class _XdrVoid(_XdrDeclaration): 286 """A void declaration""" 287 288 name: str = "void" 289 template: str = "void" 290 291 def max_width(self) -> int: 292 """Return width of type in XDR_UNITS""" 293 return 0 294 295 def symbolic_width(self) -> List: 296 """Return list containing XDR width of type's components""" 297 return [] 298 299 300@dataclass 301class _XdrConstant(_XdrAst): 302 """Corresponds to 'constant_def' in the grammar""" 303 304 name: str 305 value: str 306 307 def __post_init__(self): 308 if self.value not in constants: 309 constants[self.name] = int(self.value, 0) 310 311 312@dataclass 313class _XdrEnumerator(_XdrAst): 314 """An 'identifier = value' enumerator""" 315 316 name: str 317 value: str 318 319 def __post_init__(self): 320 if self.value not in constants: 321 constants[self.name] = int(self.value, 0) 322 323 324@dataclass 325class _XdrEnum(_XdrAst): 326 """An XDR enum definition""" 327 328 name: str 329 minimum: int 330 maximum: int 331 enumerators: List[_XdrEnumerator] 332 333 def max_width(self) -> int: 334 """Return width of type in XDR_UNITS""" 335 return 1 336 337 def symbolic_width(self) -> List: 338 """Return list containing XDR width of type's components""" 339 return ["XDR_int"] 340 341 def __post_init__(self): 342 max_widths[self.name] = self.max_width() 343 symbolic_widths[self.name] = self.symbolic_width() 344 345 346@dataclass 347class _XdrStruct(_XdrAst): 348 """An XDR struct definition""" 349 350 name: str 351 fields: List[_XdrDeclaration] 352 353 def max_width(self) -> int: 354 """Return width of type in XDR_UNITS""" 355 width = 0 356 for field in self.fields: 357 width += field.max_width() 358 return width 359 360 def symbolic_width(self) -> List: 361 """Return list containing XDR width of type's components""" 362 widths = [] 363 for field in self.fields: 364 widths += field.symbolic_width() 365 return widths 366 367 def __post_init__(self): 368 structs.add(self.name) 369 pass_by_reference.add(self.name) 370 max_widths[self.name] = self.max_width() 371 symbolic_widths[self.name] = self.symbolic_width() 372 373 374@dataclass 375class _XdrPointer(_XdrAst): 376 """An XDR pointer definition""" 377 378 name: str 379 fields: List[_XdrDeclaration] 380 381 def max_width(self) -> int: 382 """Return width of type in XDR_UNITS""" 383 width = 1 384 for field in self.fields[0:-1]: 385 width += field.max_width() 386 return width 387 388 def symbolic_width(self) -> List: 389 """Return list containing XDR width of type's components""" 390 widths = [] 391 widths += ["XDR_bool"] 392 for field in self.fields[0:-1]: 393 widths += field.symbolic_width() 394 return widths 395 396 def __post_init__(self): 397 structs.add(self.name) 398 pass_by_reference.add(self.name) 399 max_widths[self.name] = self.max_width() 400 symbolic_widths[self.name] = self.symbolic_width() 401 402 403@dataclass 404class _XdrTypedef(_XdrAst): 405 """An XDR typedef""" 406 407 declaration: _XdrDeclaration 408 409 def max_width(self) -> int: 410 """Return width of type in XDR_UNITS""" 411 return self.declaration.max_width() 412 413 def symbolic_width(self) -> List: 414 """Return list containing XDR width of type's components""" 415 return self.declaration.symbolic_width() 416 417 def __post_init__(self): 418 if isinstance(self.declaration, _XdrBasic): 419 new_type = self.declaration 420 if isinstance(new_type.spec, _XdrDefinedType): 421 if new_type.spec.type_name in pass_by_reference: 422 pass_by_reference.add(new_type.name) 423 max_widths[new_type.name] = self.max_width() 424 symbolic_widths[new_type.name] = self.symbolic_width() 425 426 427@dataclass 428class _XdrCaseSpec(_XdrAst): 429 """One case in an XDR union""" 430 431 values: List[str] 432 arm: _XdrDeclaration 433 template: str = "case_spec" 434 435 436@dataclass 437class _XdrDefaultSpec(_XdrAst): 438 """Default case in an XDR union""" 439 440 arm: _XdrDeclaration 441 template: str = "default_spec" 442 443 444@dataclass 445class _XdrUnion(_XdrAst): 446 """An XDR union""" 447 448 name: str 449 discriminant: _XdrDeclaration 450 cases: List[_XdrCaseSpec] 451 default: _XdrDeclaration 452 453 def max_width(self) -> int: 454 """Return width of type in XDR_UNITS""" 455 max_width = 0 456 for case in self.cases: 457 if case.arm.max_width() > max_width: 458 max_width = case.arm.max_width() 459 if self.default: 460 if self.default.arm.max_width() > max_width: 461 max_width = self.default.arm.max_width() 462 return 1 + max_width 463 464 def symbolic_width(self) -> List: 465 """Return list containing XDR width of type's components""" 466 max_width = 0 467 for case in self.cases: 468 if case.arm.max_width() > max_width: 469 max_width = case.arm.max_width() 470 width = case.arm.symbolic_width() 471 if self.default: 472 if self.default.arm.max_width() > max_width: 473 max_width = self.default.arm.max_width() 474 width = self.default.arm.symbolic_width() 475 return symbolic_widths[self.discriminant.name] + width 476 477 def __post_init__(self): 478 structs.add(self.name) 479 pass_by_reference.add(self.name) 480 max_widths[self.name] = self.max_width() 481 symbolic_widths[self.name] = self.symbolic_width() 482 483 484@dataclass 485class _RpcProcedure(_XdrAst): 486 """RPC procedure definition""" 487 488 name: str 489 number: str 490 argument: _XdrTypeSpecifier 491 result: _XdrTypeSpecifier 492 493 494@dataclass 495class _RpcVersion(_XdrAst): 496 """RPC version definition""" 497 498 name: str 499 number: str 500 procedures: List[_RpcProcedure] 501 502 503@dataclass 504class _RpcProgram(_XdrAst): 505 """RPC program definition""" 506 507 name: str 508 number: str 509 versions: List[_RpcVersion] 510 511 512@dataclass 513class _Pragma(_XdrAst): 514 """Empty class for pragma directives""" 515 516 517@dataclass 518class Definition(_XdrAst, ast_utils.WithMeta): 519 """Corresponds to 'definition' in the grammar""" 520 521 meta: Meta 522 value: _XdrAst 523 524 525@dataclass 526class Specification(_XdrAst, ast_utils.AsList): 527 """Corresponds to 'specification' in the grammar""" 528 529 definitions: List[Definition] 530 531 532class ParseToAst(Transformer): 533 """Functions that transform productions into AST nodes""" 534 535 def identifier(self, children): 536 """Instantiate one _XdrIdentifier object""" 537 return _XdrIdentifier(children[0].value) 538 539 def value(self, children): 540 """Instantiate one _XdrValue object""" 541 if isinstance(children[0], _XdrIdentifier): 542 return _XdrValue(children[0].symbol) 543 return _XdrValue(children[0].children[0].value) 544 545 def constant(self, children): 546 """Instantiate one _XdrConstantValue object""" 547 match children[0].data: 548 case "decimal_constant": 549 value = int(children[0].children[0].value, base=10) 550 case "hexadecimal_constant": 551 value = int(children[0].children[0].value, base=16) 552 case "octal_constant": 553 value = int(children[0].children[0].value, base=8) 554 return _XdrConstantValue(value) 555 556 def type_specifier(self, children): 557 """Instantiate one _XdrTypeSpecifier object""" 558 if isinstance(children[0], _XdrIdentifier): 559 name = children[0].symbol 560 return _XdrDefinedType(type_name=name) 561 562 name = children[0].data.value 563 return _XdrBuiltInType(type_name=name) 564 565 def constant_def(self, children): 566 """Instantiate one _XdrConstant object""" 567 name = children[0].symbol 568 value = children[1].value 569 return _XdrConstant(name, value) 570 571 # cel: Python can compute a min() and max() for the enumerator values 572 # so that the generated code can perform proper range checking. 573 def enum(self, children): 574 """Instantiate one _XdrEnum object""" 575 enum_name = children[0].symbol 576 577 i = 0 578 enumerators = [] 579 body = children[1] 580 while i < len(body.children): 581 name = body.children[i].symbol 582 value = body.children[i + 1].value 583 enumerators.append(_XdrEnumerator(name, value)) 584 i = i + 2 585 586 return _XdrEnum(enum_name, 0, 0, enumerators) 587 588 def fixed_length_opaque(self, children): 589 """Instantiate one _XdrFixedLengthOpaque declaration object""" 590 name = children[0].symbol 591 size = children[1].value 592 593 return _XdrFixedLengthOpaque(name, size) 594 595 def variable_length_opaque(self, children): 596 """Instantiate one _XdrVariableLengthOpaque declaration object""" 597 name = children[0].symbol 598 if children[1] is not None: 599 maxsize = children[1].value 600 else: 601 maxsize = "0" 602 603 return _XdrVariableLengthOpaque(name, maxsize) 604 605 def string(self, children): 606 """Instantiate one _XdrString declaration object""" 607 name = children[0].symbol 608 if children[1] is not None: 609 maxsize = children[1].value 610 else: 611 maxsize = "0" 612 613 return _XdrString(name, maxsize) 614 615 def fixed_length_array(self, children): 616 """Instantiate one _XdrFixedLengthArray declaration object""" 617 spec = children[0] 618 name = children[1].symbol 619 size = children[2].value 620 621 return _XdrFixedLengthArray(name, spec, size) 622 623 def variable_length_array(self, children): 624 """Instantiate one _XdrVariableLengthArray declaration object""" 625 spec = children[0] 626 name = children[1].symbol 627 if children[2] is not None: 628 maxsize = children[2].value 629 else: 630 maxsize = "0" 631 632 return _XdrVariableLengthArray(name, spec, maxsize) 633 634 def optional_data(self, children): 635 """Instantiate one _XdrOptionalData declaration object""" 636 spec = children[0] 637 name = children[1].symbol 638 639 return _XdrOptionalData(name, spec) 640 641 def basic(self, children): 642 """Instantiate one _XdrBasic object""" 643 spec = children[0] 644 name = children[1].symbol 645 646 return _XdrBasic(name, spec) 647 648 def void(self, children): 649 """Instantiate one _XdrVoid declaration object""" 650 651 return _XdrVoid() 652 653 def struct(self, children): 654 """Instantiate one _XdrStruct object""" 655 name = children[0].symbol 656 fields = children[1].children 657 658 last_field = fields[-1] 659 if ( 660 isinstance(last_field, _XdrOptionalData) 661 and name == last_field.spec.type_name 662 ): 663 return _XdrPointer(name, fields) 664 665 return _XdrStruct(name, fields) 666 667 def typedef(self, children): 668 """Instantiate one _XdrTypedef object""" 669 new_type = children[0] 670 671 return _XdrTypedef(new_type) 672 673 def case_spec(self, children): 674 """Instantiate one _XdrCaseSpec object""" 675 values = [] 676 for item in children[0:-1]: 677 values.append(item.value) 678 arm = children[-1] 679 680 return _XdrCaseSpec(values, arm) 681 682 def default_spec(self, children): 683 """Instantiate one _XdrDefaultSpec object""" 684 arm = children[0] 685 686 return _XdrDefaultSpec(arm) 687 688 def union(self, children): 689 """Instantiate one _XdrUnion object""" 690 name = children[0].symbol 691 692 body = children[1] 693 discriminant = body.children[0].children[0] 694 cases = body.children[1:-1] 695 default = body.children[-1] 696 697 return _XdrUnion(name, discriminant, cases, default) 698 699 def procedure_def(self, children): 700 """Instantiate one _RpcProcedure object""" 701 result = children[0] 702 name = children[1].symbol 703 argument = children[2] 704 number = children[3].value 705 706 return _RpcProcedure(name, number, argument, result) 707 708 def version_def(self, children): 709 """Instantiate one _RpcVersion object""" 710 name = children[0].symbol 711 number = children[-1].value 712 procedures = children[1:-1] 713 714 return _RpcVersion(name, number, procedures) 715 716 def program_def(self, children): 717 """Instantiate one _RpcProgram object""" 718 name = children[0].symbol 719 number = children[-1].value 720 versions = children[1:-1] 721 722 return _RpcProgram(name, number, versions) 723 724 def pragma_def(self, children): 725 """Instantiate one _Pragma object""" 726 directive = children[0].children[0].data 727 match directive: 728 case "big_endian_directive": 729 big_endian.append(children[1].symbol) 730 case "exclude_directive": 731 excluded_apis.append(children[1].symbol) 732 case "header_directive": 733 global header_name 734 header_name = children[1].symbol 735 case "public_directive": 736 public_apis.append(children[1].symbol) 737 case _: 738 raise NotImplementedError("Directive not supported") 739 return _Pragma() 740 741 742transformer = ast_utils.create_transformer(this_module, ParseToAst()) 743 744 745def transform_parse_tree(parse_tree): 746 """Transform productions into an abstract syntax tree""" 747 748 return transformer.transform(parse_tree) 749 750 751def get_header_name() -> str: 752 """Return header name set by pragma header directive""" 753 return header_name 754