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, KW_ONLY 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 "short": ["XDR_short"], 38 "unsigned_short": ["XDR_unsigned_short"], 39 "int": ["XDR_int"], 40 "unsigned_int": ["XDR_unsigned_int"], 41 "long": ["XDR_long"], 42 "unsigned_long": ["XDR_unsigned_long"], 43 "hyper": ["XDR_hyper"], 44 "unsigned_hyper": ["XDR_unsigned_hyper"], 45} 46 47# Numeric XDR widths are tracked in a dictionary that is keyed 48# by type_name because sometimes a caller has nothing more than 49# the type_name to use to figure out the numeric width. 50max_widths = { 51 "void": 0, 52 "bool": 1, 53 "short": 1, 54 "unsigned_short": 1, 55 "int": 1, 56 "unsigned_int": 1, 57 "long": 1, 58 "unsigned_long": 1, 59 "hyper": 2, 60 "unsigned_hyper": 2, 61} 62 63 64@dataclass 65class _XdrAst(ast_utils.Ast): 66 """Base class for the XDR abstract syntax tree""" 67 68 # Source position of the construct's declared identifier, when 69 # the transformer records one, so semantic diagnostics can point 70 # at the exact declaration. The KW_ONLY marker makes the fields 71 # keyword-only, so they never disturb the positional child 72 # ordering lark uses to build each node; 0 means the position was 73 # not recorded. 74 _: KW_ONLY 75 line: int = 0 76 column: int = 0 77 78 79@dataclass 80class _XdrIdentifier(_XdrAst): 81 """Corresponds to 'identifier' in the XDR language grammar""" 82 83 symbol: str 84 85 86@dataclass 87class _XdrValue(_XdrAst): 88 """Corresponds to 'value' in the XDR language grammar""" 89 90 value: str 91 92 93@dataclass 94class _XdrConstantValue(_XdrAst): 95 """Corresponds to 'constant' in the XDR language grammar""" 96 97 value: int 98 99 100@dataclass 101class _XdrTypeSpecifier(_XdrAst): 102 """Corresponds to 'type_specifier' in the XDR language grammar""" 103 104 type_name: str 105 c_classifier: str = "" 106 107 108@dataclass 109class _XdrDefinedType(_XdrTypeSpecifier): 110 """Corresponds to a type defined by the input specification""" 111 112 def symbolic_width(self) -> List: 113 """Return list containing XDR width of type's components""" 114 return [get_header_name().upper() + "_" + self.type_name + "_sz"] 115 116 def __post_init__(self): 117 if self.type_name in structs: 118 self.c_classifier = "struct " 119 symbolic_widths[self.type_name] = self.symbolic_width() 120 121 122@dataclass 123class _XdrBuiltInType(_XdrTypeSpecifier): 124 """Corresponds to a built-in XDR type""" 125 126 def symbolic_width(self) -> List: 127 """Return list containing XDR width of type's components""" 128 return symbolic_widths[self.type_name] 129 130 131@dataclass 132class _XdrDeclaration(_XdrAst): 133 """Base class of XDR type declarations""" 134 135 136@dataclass 137class _XdrFixedLengthOpaque(_XdrDeclaration): 138 """A fixed-length opaque declaration""" 139 140 name: str 141 size: str 142 template: str = "fixed_length_opaque" 143 144 def max_width(self) -> int: 145 """Return width of type in XDR_UNITS""" 146 return xdr_quadlen(self.size) 147 148 def symbolic_width(self) -> List: 149 """Return list containing XDR width of type's components""" 150 return ["XDR_QUADLEN(" + self.size + ")"] 151 152 def __post_init__(self): 153 max_widths[self.name] = self.max_width() 154 symbolic_widths[self.name] = self.symbolic_width() 155 156 157@dataclass 158class _XdrVariableLengthOpaque(_XdrDeclaration): 159 """A variable-length opaque declaration""" 160 161 name: str 162 maxsize: str 163 template: str = "variable_length_opaque" 164 165 def max_width(self) -> int: 166 """Return width of type in XDR_UNITS""" 167 return 1 + xdr_quadlen(self.maxsize) 168 169 def symbolic_width(self) -> List: 170 """Return list containing XDR width of type's components""" 171 widths = ["XDR_unsigned_int"] 172 if self.maxsize != "0": 173 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 174 return widths 175 176 def __post_init__(self): 177 max_widths[self.name] = self.max_width() 178 symbolic_widths[self.name] = self.symbolic_width() 179 180 181@dataclass 182class _XdrString(_XdrDeclaration): 183 """A (NUL-terminated) variable-length string declaration""" 184 185 name: str 186 maxsize: str 187 template: str = "string" 188 189 def max_width(self) -> int: 190 """Return width of type in XDR_UNITS""" 191 return 1 + xdr_quadlen(self.maxsize) 192 193 def symbolic_width(self) -> List: 194 """Return list containing XDR width of type's components""" 195 widths = ["XDR_unsigned_int"] 196 if self.maxsize != "0": 197 widths.append("XDR_QUADLEN(" + self.maxsize + ")") 198 return widths 199 200 def __post_init__(self): 201 max_widths[self.name] = self.max_width() 202 symbolic_widths[self.name] = self.symbolic_width() 203 204 205@dataclass 206class _XdrFixedLengthArray(_XdrDeclaration): 207 """A fixed-length array declaration""" 208 209 name: str 210 spec: _XdrTypeSpecifier 211 size: str 212 template: str = "fixed_length_array" 213 214 def max_width(self) -> int: 215 """Return width of type in XDR_UNITS""" 216 return xdr_quadlen(self.size) * max_widths[self.spec.type_name] 217 218 def symbolic_width(self) -> List: 219 """Return list containing XDR width of type's components""" 220 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 221 return ["(" + self.size + " * (" + item_width + "))"] 222 223 def __post_init__(self): 224 max_widths[self.name] = self.max_width() 225 symbolic_widths[self.name] = self.symbolic_width() 226 227 228@dataclass 229class _XdrVariableLengthArray(_XdrDeclaration): 230 """A variable-length array declaration""" 231 232 name: str 233 spec: _XdrTypeSpecifier 234 maxsize: str 235 template: str = "variable_length_array" 236 237 def max_width(self) -> int: 238 """Return width of type in XDR_UNITS""" 239 return 1 + (xdr_quadlen(self.maxsize) * max_widths[self.spec.type_name]) 240 241 def symbolic_width(self) -> List: 242 """Return list containing XDR width of type's components""" 243 widths = ["XDR_unsigned_int"] 244 if self.maxsize != "0": 245 item_width = " + ".join(symbolic_widths[self.spec.type_name]) 246 widths.append("(" + self.maxsize + " * (" + item_width + "))") 247 return widths 248 249 def __post_init__(self): 250 max_widths[self.name] = self.max_width() 251 symbolic_widths[self.name] = self.symbolic_width() 252 253 254@dataclass 255class _XdrOptionalData(_XdrDeclaration): 256 """An 'optional_data' declaration""" 257 258 name: str 259 spec: _XdrTypeSpecifier 260 template: str = "optional_data" 261 262 def max_width(self) -> int: 263 """Return width of type in XDR_UNITS""" 264 return 1 265 266 def symbolic_width(self) -> List: 267 """Return list containing XDR width of type's components""" 268 return ["XDR_bool"] 269 270 def __post_init__(self): 271 structs.add(self.name) 272 pass_by_reference.add(self.name) 273 max_widths[self.name] = self.max_width() 274 symbolic_widths[self.name] = self.symbolic_width() 275 276 277@dataclass 278class _XdrBasic(_XdrDeclaration): 279 """A 'basic' declaration""" 280 281 name: str 282 spec: _XdrTypeSpecifier 283 template: str = "basic" 284 285 def max_width(self) -> int: 286 """Return width of type in XDR_UNITS""" 287 return max_widths[self.spec.type_name] 288 289 def symbolic_width(self) -> List: 290 """Return list containing XDR width of type's components""" 291 return symbolic_widths[self.spec.type_name] 292 293 def __post_init__(self): 294 max_widths[self.name] = self.max_width() 295 symbolic_widths[self.name] = self.symbolic_width() 296 297 298@dataclass 299class _XdrVoid(_XdrDeclaration): 300 """A void declaration""" 301 302 name: str = "void" 303 template: str = "void" 304 305 def max_width(self) -> int: 306 """Return width of type in XDR_UNITS""" 307 return 0 308 309 def symbolic_width(self) -> List: 310 """Return list containing XDR width of type's components""" 311 return [] 312 313 314@dataclass 315class _XdrConstant(_XdrAst): 316 """Corresponds to 'constant_def' in the grammar""" 317 318 name: str 319 value: str 320 321 def __post_init__(self): 322 if self.value not in constants: 323 constants[self.name] = int(self.value, 0) 324 325 326@dataclass 327class _XdrEnumerator(_XdrAst): 328 """An 'identifier = value' enumerator""" 329 330 name: str 331 value: str 332 333 def __post_init__(self): 334 if self.value not in constants: 335 constants[self.name] = int(self.value, 0) 336 337 338@dataclass 339class _XdrEnum(_XdrAst): 340 """An XDR enum definition""" 341 342 name: str 343 enumerators: List[_XdrEnumerator] 344 345 def max_width(self) -> int: 346 """Return width of type in XDR_UNITS""" 347 return 1 348 349 def symbolic_width(self) -> List: 350 """Return list containing XDR width of type's components""" 351 return ["XDR_int"] 352 353 def __post_init__(self): 354 max_widths[self.name] = self.max_width() 355 symbolic_widths[self.name] = self.symbolic_width() 356 357 358@dataclass 359class _XdrStruct(_XdrAst): 360 """An XDR struct definition""" 361 362 name: str 363 fields: List[_XdrDeclaration] 364 365 def max_width(self) -> int: 366 """Return width of type in XDR_UNITS""" 367 width = 0 368 for field in self.fields: 369 width += field.max_width() 370 return width 371 372 def symbolic_width(self) -> List: 373 """Return list containing XDR width of type's components""" 374 widths = [] 375 for field in self.fields: 376 widths += field.symbolic_width() 377 return widths 378 379 def __post_init__(self): 380 structs.add(self.name) 381 pass_by_reference.add(self.name) 382 max_widths[self.name] = self.max_width() 383 symbolic_widths[self.name] = self.symbolic_width() 384 385 386@dataclass 387class _XdrPointer(_XdrAst): 388 """An XDR pointer definition""" 389 390 name: str 391 fields: List[_XdrDeclaration] 392 393 def max_width(self) -> int: 394 """Return width of type in XDR_UNITS""" 395 width = 1 396 for field in self.fields[0:-1]: 397 width += field.max_width() 398 return width 399 400 def symbolic_width(self) -> List: 401 """Return list containing XDR width of type's components""" 402 widths = [] 403 widths += ["XDR_bool"] 404 for field in self.fields[0:-1]: 405 widths += field.symbolic_width() 406 return widths 407 408 def __post_init__(self): 409 structs.add(self.name) 410 pass_by_reference.add(self.name) 411 max_widths[self.name] = self.max_width() 412 symbolic_widths[self.name] = self.symbolic_width() 413 414 415@dataclass 416class _XdrTypedef(_XdrAst): 417 """An XDR typedef""" 418 419 declaration: _XdrDeclaration 420 421 def max_width(self) -> int: 422 """Return width of type in XDR_UNITS""" 423 return self.declaration.max_width() 424 425 def symbolic_width(self) -> List: 426 """Return list containing XDR width of type's components""" 427 return self.declaration.symbolic_width() 428 429 def __post_init__(self): 430 if isinstance(self.declaration, _XdrBasic): 431 new_type = self.declaration 432 if isinstance(new_type.spec, _XdrDefinedType): 433 if new_type.spec.type_name in pass_by_reference: 434 pass_by_reference.add(new_type.name) 435 max_widths[new_type.name] = self.max_width() 436 symbolic_widths[new_type.name] = self.symbolic_width() 437 438 439@dataclass 440class _XdrCaseSpec(_XdrAst): 441 """One case in an XDR union""" 442 443 values: List[str] 444 arm: _XdrDeclaration 445 template: str = "case_spec" 446 447 448@dataclass 449class _XdrDefaultSpec(_XdrAst): 450 """Default case in an XDR union""" 451 452 arm: _XdrDeclaration 453 template: str = "default_spec" 454 455 456@dataclass 457class _XdrUnion(_XdrAst): 458 """An XDR union""" 459 460 name: str 461 discriminant: _XdrDeclaration 462 cases: List[_XdrCaseSpec] 463 default: _XdrDeclaration 464 465 def max_width(self) -> int: 466 """Return width of type in XDR_UNITS""" 467 max_width = 0 468 for case in self.cases: 469 if case.arm.max_width() > max_width: 470 max_width = case.arm.max_width() 471 if self.default: 472 if self.default.arm.max_width() > max_width: 473 max_width = self.default.arm.max_width() 474 return 1 + max_width 475 476 def symbolic_width(self) -> List: 477 """Return list containing XDR width of type's components""" 478 max_width = 0 479 for case in self.cases: 480 if case.arm.max_width() > max_width: 481 max_width = case.arm.max_width() 482 width = case.arm.symbolic_width() 483 if self.default: 484 if self.default.arm.max_width() > max_width: 485 max_width = self.default.arm.max_width() 486 width = self.default.arm.symbolic_width() 487 return symbolic_widths[self.discriminant.name] + width 488 489 def __post_init__(self): 490 structs.add(self.name) 491 pass_by_reference.add(self.name) 492 max_widths[self.name] = self.max_width() 493 symbolic_widths[self.name] = self.symbolic_width() 494 495 496@dataclass 497class _RpcProcedure(_XdrAst): 498 """RPC procedure definition""" 499 500 name: str 501 number: int 502 argument: _XdrTypeSpecifier 503 result: _XdrTypeSpecifier 504 505 506@dataclass 507class _RpcVersion(_XdrAst): 508 """RPC version definition""" 509 510 name: str 511 number: int 512 procedures: List[_RpcProcedure] 513 514 515@dataclass 516class _RpcProgram(_XdrAst): 517 """RPC program definition""" 518 519 name: str 520 number: int 521 versions: List[_RpcVersion] 522 523 524@dataclass 525class _Pragma(_XdrAst): 526 """Empty class for pragma directives""" 527 528 529@dataclass 530class _XdrPassthru(_XdrAst): 531 """Passthrough line to emit verbatim in output""" 532 533 content: str 534 535 536@dataclass 537class Definition(_XdrAst, ast_utils.WithMeta): 538 """Corresponds to 'definition' in the grammar""" 539 540 meta: Meta 541 value: _XdrAst 542 543 544@dataclass 545class Specification(_XdrAst, ast_utils.AsList): 546 """Corresponds to 'specification' in the grammar""" 547 548 definitions: List[Definition] 549 550 551class ParseToAst(Transformer): 552 """Functions that transform productions into AST nodes""" 553 554 def identifier(self, children): 555 """Instantiate one _XdrIdentifier object""" 556 token = children[0] 557 return _XdrIdentifier(token.value, line=token.line, column=token.column) 558 559 def value(self, children): 560 """Instantiate one _XdrValue object""" 561 if isinstance(children[0], _XdrIdentifier): 562 return _XdrValue(children[0].symbol) 563 return _XdrValue(children[0].children[0].value) 564 565 def constant(self, children): 566 """Instantiate one _XdrConstantValue object""" 567 match children[0].data: 568 case "decimal_constant": 569 value = int(children[0].children[0].value, base=10) 570 case "hexadecimal_constant": 571 value = int(children[0].children[0].value, base=16) 572 case "octal_constant": 573 value = int(children[0].children[0].value, base=8) 574 return _XdrConstantValue(value) 575 576 def type_specifier(self, children): 577 """Instantiate one _XdrTypeSpecifier object""" 578 if isinstance(children[0], _XdrIdentifier): 579 name = children[0].symbol 580 return _XdrDefinedType(type_name=name) 581 582 name = children[0].data.value 583 return _XdrBuiltInType(type_name=name) 584 585 def constant_def(self, children): 586 """Instantiate one _XdrConstant object""" 587 ident = children[0] 588 value = children[1].value 589 return _XdrConstant(ident.symbol, value, line=ident.line, column=ident.column) 590 591 def enum(self, children): 592 """Instantiate one _XdrEnum object""" 593 name_ident = children[0] 594 595 i = 0 596 enumerators = [] 597 body = children[1] 598 while i < len(body.children): 599 ident = body.children[i] 600 value = body.children[i + 1].value 601 enumerators.append( 602 _XdrEnumerator( 603 ident.symbol, value, line=ident.line, column=ident.column 604 ) 605 ) 606 i = i + 2 607 608 return _XdrEnum( 609 name_ident.symbol, 610 enumerators, 611 line=name_ident.line, 612 column=name_ident.column, 613 ) 614 615 def fixed_length_opaque(self, children): 616 """Instantiate one _XdrFixedLengthOpaque declaration object""" 617 ident = children[0] 618 size = children[1].value 619 620 return _XdrFixedLengthOpaque( 621 ident.symbol, size, line=ident.line, column=ident.column 622 ) 623 624 def variable_length_opaque(self, children): 625 """Instantiate one _XdrVariableLengthOpaque declaration object""" 626 ident = children[0] 627 if children[1] is not None: 628 maxsize = children[1].value 629 else: 630 maxsize = "0" 631 632 return _XdrVariableLengthOpaque( 633 ident.symbol, maxsize, line=ident.line, column=ident.column 634 ) 635 636 def string(self, children): 637 """Instantiate one _XdrString declaration object""" 638 ident = children[0] 639 if children[1] is not None: 640 maxsize = children[1].value 641 else: 642 maxsize = "0" 643 644 return _XdrString(ident.symbol, maxsize, line=ident.line, column=ident.column) 645 646 def fixed_length_array(self, children): 647 """Instantiate one _XdrFixedLengthArray declaration object""" 648 spec = children[0] 649 ident = children[1] 650 size = children[2].value 651 652 return _XdrFixedLengthArray( 653 ident.symbol, spec, size, line=ident.line, column=ident.column 654 ) 655 656 def variable_length_array(self, children): 657 """Instantiate one _XdrVariableLengthArray declaration object""" 658 spec = children[0] 659 ident = children[1] 660 if children[2] is not None: 661 maxsize = children[2].value 662 else: 663 maxsize = "0" 664 665 return _XdrVariableLengthArray( 666 ident.symbol, spec, maxsize, line=ident.line, column=ident.column 667 ) 668 669 def optional_data(self, children): 670 """Instantiate one _XdrOptionalData declaration object""" 671 spec = children[0] 672 ident = children[1] 673 674 return _XdrOptionalData( 675 ident.symbol, spec, line=ident.line, column=ident.column 676 ) 677 678 def basic(self, children): 679 """Instantiate one _XdrBasic object""" 680 spec = children[0] 681 ident = children[1] 682 683 return _XdrBasic(ident.symbol, spec, line=ident.line, column=ident.column) 684 685 def void(self, children): 686 """Instantiate one _XdrVoid declaration object""" 687 688 return _XdrVoid() 689 690 def struct(self, children): 691 """Instantiate one _XdrStruct object""" 692 ident = children[0] 693 name = ident.symbol 694 fields = children[1].children 695 pos = {"line": ident.line, "column": ident.column} 696 697 last_field = fields[-1] 698 if ( 699 isinstance(last_field, _XdrOptionalData) 700 and name == last_field.spec.type_name 701 ): 702 return _XdrPointer(name, fields, **pos) 703 704 return _XdrStruct(name, fields, **pos) 705 706 def typedef(self, children): 707 """Instantiate one _XdrTypedef object""" 708 new_type = children[0] 709 710 return _XdrTypedef(new_type) 711 712 def case_spec(self, children): 713 """Instantiate one _XdrCaseSpec object""" 714 values = [] 715 for item in children[0:-1]: 716 values.append(item.value) 717 arm = children[-1] 718 719 return _XdrCaseSpec(values, arm) 720 721 def default_spec(self, children): 722 """Instantiate one _XdrDefaultSpec object""" 723 arm = children[0] 724 725 return _XdrDefaultSpec(arm) 726 727 def union(self, children): 728 """Instantiate one _XdrUnion object""" 729 ident = children[0] 730 731 body = children[1] 732 discriminant = body.children[0].children[0] 733 cases = body.children[1:-1] 734 default = body.children[-1] 735 736 return _XdrUnion( 737 ident.symbol, 738 discriminant, 739 cases, 740 default, 741 line=ident.line, 742 column=ident.column, 743 ) 744 745 def procedure_def(self, children): 746 """Instantiate one _RpcProcedure object""" 747 result = children[0] 748 ident = children[1] 749 argument = children[2] 750 number = children[3].value 751 752 return _RpcProcedure( 753 ident.symbol, 754 number, 755 argument, 756 result, 757 line=ident.line, 758 column=ident.column, 759 ) 760 761 def version_def(self, children): 762 """Instantiate one _RpcVersion object""" 763 ident = children[0] 764 number = children[-1].value 765 procedures = children[1:-1] 766 767 return _RpcVersion( 768 ident.symbol, number, procedures, line=ident.line, column=ident.column 769 ) 770 771 def program_def(self, children): 772 """Instantiate one _RpcProgram object""" 773 ident = children[0] 774 number = children[-1].value 775 versions = children[1:-1] 776 777 return _RpcProgram( 778 ident.symbol, number, versions, line=ident.line, column=ident.column 779 ) 780 781 def pragma_def(self, children): 782 """Instantiate one _Pragma object""" 783 directive = children[0].children[0].data 784 match directive: 785 case "big_endian_directive": 786 big_endian.append(children[1].symbol) 787 case "exclude_directive": 788 excluded_apis.append(children[1].symbol) 789 case "header_directive": 790 global header_name 791 header_name = children[1].symbol 792 case "public_directive": 793 public_apis.append(children[1].symbol) 794 case _: 795 raise NotImplementedError("Directive not supported") 796 return _Pragma() 797 798 def passthru_def(self, children): 799 """Instantiate one _XdrPassthru object""" 800 token = children[0] 801 content = token.value[1:] 802 return _XdrPassthru(content) 803 804 805transformer = ast_utils.create_transformer(this_module, ParseToAst()) 806 807 808def _merge_consecutive_passthru(definitions: List[Definition]) -> List[Definition]: 809 """Merge consecutive passthru definitions into single nodes""" 810 result = [] 811 i = 0 812 while i < len(definitions): 813 if isinstance(definitions[i].value, _XdrPassthru): 814 lines = [definitions[i].value.content] 815 meta = definitions[i].meta 816 j = i + 1 817 while j < len(definitions) and isinstance( 818 definitions[j].value, _XdrPassthru 819 ): 820 lines.append(definitions[j].value.content) 821 j += 1 822 merged = _XdrPassthru("\n".join(lines)) 823 result.append(Definition(meta, merged)) 824 i = j 825 else: 826 result.append(definitions[i]) 827 i += 1 828 return result 829 830 831def _meta_line(meta) -> int: 832 """Return the 1-based source line for a node's meta, or 0 if unknown""" 833 try: 834 return meta.line 835 except AttributeError: 836 return 0 837 838 839class XdrSemanticError(Exception): 840 """A specification that parses but violates an XDR semantic rule. 841 842 Detection lives in the language-independent front end because a 843 duplicate name is malformed XDR regardless of the output language. 844 """ 845 846 def __init__(self, message: str, meta): 847 super().__init__(message) 848 self.message = message 849 self.line = _meta_line(meta) 850 self.column = getattr(meta, "column", 0) 851 852 853def _introduced_names(value): 854 """Yield (name, node) for each identifier a definition introduces.""" 855 if isinstance(value, (_XdrStruct, _XdrUnion, _XdrPointer)): 856 yield value.name, value 857 elif isinstance(value, _XdrEnum): 858 yield value.name, value 859 for enumerator in value.enumerators: 860 yield enumerator.name, enumerator 861 elif isinstance(value, _XdrTypedef): 862 yield value.declaration.name, value.declaration 863 elif isinstance(value, _XdrConstant): 864 yield value.name, value 865 elif isinstance(value, _RpcProgram): 866 yield value.name, value 867 868 869def _check_rpc_scope_names(program: "_RpcProgram") -> None: 870 """Enforce RFC 5531 Section 12.3 scoping within an RPC program. 871 872 A version name and number are unique within the program and a 873 procedure name and number are unique within its version. 874 """ 875 version_names = set() 876 version_numbers = set() 877 for version in program.versions: 878 if version.name in version_names: 879 raise XdrSemanticError( 880 f"duplicate version name '{version.name}'" 881 f" in program '{program.name}'", 882 version, 883 ) 884 version_names.add(version.name) 885 if version.number in version_numbers: 886 raise XdrSemanticError( 887 f"duplicate version number {version.number}" 888 f" in program '{program.name}'", 889 version, 890 ) 891 version_numbers.add(version.number) 892 procedure_names = set() 893 procedure_numbers = set() 894 for procedure in version.procedures: 895 if procedure.name in procedure_names: 896 raise XdrSemanticError( 897 f"duplicate procedure name '{procedure.name}'" 898 f" in version '{version.name}'", 899 procedure, 900 ) 901 procedure_names.add(procedure.name) 902 if procedure.number in procedure_numbers: 903 raise XdrSemanticError( 904 f"duplicate procedure number {procedure.number}" 905 f" in version '{version.name}'", 906 procedure, 907 ) 908 procedure_numbers.add(procedure.number) 909 910 911def check_duplicate_definitions(root: "Specification") -> None: 912 """Reject a spec that declares an identifier more than once. 913 914 RFC 4506 Section 6.4 places constant and type identifiers in a 915 single name space that must be unique within a specification. 916 RFC 5531 Section 12.3 adds RPC program names to that name space 917 and scopes version names and numbers to their program and 918 procedure names and numbers to their version. 919 """ 920 seen = {} 921 for definition in root.definitions: 922 for name, node in _introduced_names(definition.value): 923 where = node if node.line else definition.meta 924 first = seen.get(name) 925 if first is not None: 926 raise XdrSemanticError( 927 f"duplicate identifier '{name}'" 928 f" (first declared at line {_meta_line(first)})", 929 where, 930 ) 931 seen[name] = where 932 if isinstance(definition.value, _RpcProgram): 933 _check_rpc_scope_names(definition.value) 934 935 936# RFC 5531 (Section 9) encodes program, version, and procedure numbers 937# as unsigned 32-bit integers, so each must fall within [0, 2**32 - 1]. 938_RPC_NUMBER_MAX = 2**32 - 1 939 940 941def _check_rpc_number(kind: str, number: int, scope: str, meta) -> None: 942 """Reject one RPC number that is negative or wider than 32 bits.""" 943 if number < 0: 944 raise XdrSemanticError( 945 f"negative {kind} number {number} {scope}", 946 meta, 947 ) 948 if number > _RPC_NUMBER_MAX: 949 raise XdrSemanticError( 950 f"{kind} number {number} {scope} exceeds {_RPC_NUMBER_MAX}", 951 meta, 952 ) 953 954 955def check_rpc_number_range(root: "Specification") -> None: 956 """Reject an out-of-range program, version, or procedure number. 957 958 RFC 5531 assigns only unsigned constants to program, version, and 959 procedure numbers (Section 12.3) and encodes each as an unsigned 960 32-bit integer (Section 9). RFC 4506 Section 6.2 permits a signed 961 decimal constant for XDR constants in general and sets no ceiling on 962 magnitude, so the grammar accepts an out-of-range value; the range 963 is enforced here instead. The parser retains no per-version or 964 per-procedure source location, so a violation is reported against the 965 program definition. 966 """ 967 for definition in root.definitions: 968 program = definition.value 969 if not isinstance(program, _RpcProgram): 970 continue 971 _check_rpc_number( 972 "program", 973 program.number, 974 f"in program '{program.name}'", 975 definition.meta, 976 ) 977 for version in program.versions: 978 _check_rpc_number( 979 "version", 980 version.number, 981 f"in program '{program.name}'", 982 definition.meta, 983 ) 984 for procedure in version.procedures: 985 _check_rpc_number( 986 "procedure", 987 procedure.number, 988 f"in version '{version.name}'", 989 definition.meta, 990 ) 991 992 993def transform_parse_tree(parse_tree): 994 """Transform productions into an abstract syntax tree""" 995 ast = transformer.transform(parse_tree) 996 ast.definitions = _merge_consecutive_passthru(ast.definitions) 997 check_duplicate_definitions(ast) 998 check_rpc_number_range(ast) 999 return ast 1000 1001 1002def get_header_name() -> str: 1003 """Return header name set by pragma header directive""" 1004 return header_name 1005