xref: /linux/tools/net/sunrpc/xdrgen/xdr_ast.py (revision c34e9ab9a612ee8b18273398ef75c207b01f516d)
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