| #!/usr/bin/env python3 |
| # ex: set filetype=python: |
| |
| """Generate code to handle XDR unions""" |
| |
| from jinja2 import Environment |
| |
| from generators import SourceGenerator |
| from generators import create_jinja2_environment, get_jinja2_template |
| |
| from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid |
| from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis |
| |
| |
| def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None: |
| """Emit one declaration pair for an XDR union type""" |
| if node.name in public_apis: |
| template = get_jinja2_template(environment, "declaration", "close") |
| print(template.render(name=node.name)) |
| |
| |
| def emit_union_switch_spec_definition( |
| environment: Environment, node: _XdrDeclaration |
| ) -> None: |
| """Emit a definition for an XDR union's discriminant""" |
| assert isinstance(node, _XdrBasic) |
| template = get_jinja2_template(environment, "definition", "switch_spec") |
| print( |
| template.render( |
| name=node.name, |
| type=node.spec.type_name, |
| classifier=node.spec.c_classifier, |
| ) |
| ) |
| |
| |
| def emit_union_case_spec_definition( |
| environment: Environment, node: _XdrDeclaration |
| ) -> None: |
| """Emit a definition for an XDR union's case arm""" |
| if isinstance(node.arm, _XdrVoid): |
| return |
| assert isinstance(node.arm, _XdrBasic) |
| template = get_jinja2_template(environment, "definition", "case_spec") |
| print( |
| template.render( |
| name=node.arm.name, |
| type=node.arm.spec.type_name, |
| classifier=node.arm.spec.c_classifier, |
| ) |
| ) |
| |
| |
| def emit_union_definition(environment: Environment, node: _XdrUnion) -> None: |
| """Emit one XDR union definition""" |
| template = get_jinja2_template(environment, "definition", "open") |
| print(template.render(name=node.name)) |
| |
| emit_union_switch_spec_definition(environment, node.discriminant) |
| |
| for case in node.cases: |
| emit_union_case_spec_definition(environment, case) |
| |
| if node.default is not None: |
| emit_union_case_spec_definition(environment, node.default) |
| |
| template = get_jinja2_template(environment, "definition", "close") |
| print(template.render(name=node.name)) |
| |
| |
| def emit_union_switch_spec_decoder( |
| environment: Environment, node: _XdrDeclaration |
| ) -> None: |
| """Emit a decoder for an XDR union's discriminant""" |
| assert isinstance(node, _XdrBasic) |
| template = get_jinja2_template(environment, "decoder", "switch_spec") |
| print(template.render(name=node.name, type=node.spec.type_name)) |
| |
| |
| def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None: |
| """Emit decoder functions for an XDR union's case arm""" |
| |
| if isinstance(node.arm, _XdrVoid): |
| return |
| |
| template = get_jinja2_template(environment, "decoder", "case_spec") |
| for case in node.values: |
| print(template.render(case=case)) |
| |
| assert isinstance(node.arm, _XdrBasic) |
| template = get_jinja2_template(environment, "decoder", node.arm.template) |
| print( |
| template.render( |
| name=node.arm.name, |
| type=node.arm.spec.type_name, |
| classifier=node.arm.spec.c_classifier, |
| ) |
| ) |
| |
| template = get_jinja2_template(environment, "decoder", "break") |
| print(template.render()) |
| |
| |
| def emit_union_default_spec_decoder(environment: Environment, node: _XdrUnion) -> None: |
| """Emit a decoder function for an XDR union's default arm""" |
| default_case = node.default |
| |
| # Avoid a gcc warning about a default case with boolean discriminant |
| if default_case is None and node.discriminant.spec.type_name == "bool": |
| return |
| |
| template = get_jinja2_template(environment, "decoder", "default_spec") |
| print(template.render()) |
| |
| if default_case is None or isinstance(default_case.arm, _XdrVoid): |
| template = get_jinja2_template(environment, "decoder", "break") |
| print(template.render()) |
| return |
| |
| assert isinstance(default_case.arm, _XdrBasic) |
| template = get_jinja2_template(environment, "decoder", default_case.arm.template) |
| print( |
| template.render( |
| name=default_case.arm.name, |
| type=default_case.arm.spec.type_name, |
| classifier=default_case.arm.spec.c_classifier, |
| ) |
| ) |
| |
| |
| def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None: |
| """Emit one XDR union decoder""" |
| template = get_jinja2_template(environment, "decoder", "open") |
| print(template.render(name=node.name)) |
| |
| emit_union_switch_spec_decoder(environment, node.discriminant) |
| |
| for case in node.cases: |
| emit_union_case_spec_decoder(environment, case) |
| |
| emit_union_default_spec_decoder(environment, node) |
| |
| template = get_jinja2_template(environment, "decoder", "close") |
| print(template.render()) |
| |
| |
| def emit_union_switch_spec_encoder( |
| environment: Environment, node: _XdrDeclaration |
| ) -> None: |
| """Emit an encoder for an XDR union's discriminant""" |
| assert isinstance(node, _XdrBasic) |
| template = get_jinja2_template(environment, "encoder", "switch_spec") |
| print(template.render(name=node.name, type=node.spec.type_name)) |
| |
| |
| def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None: |
| """Emit encoder functions for an XDR union's case arm""" |
| |
| if isinstance(node.arm, _XdrVoid): |
| return |
| |
| template = get_jinja2_template(environment, "encoder", "case_spec") |
| for case in node.values: |
| print(template.render(case=case)) |
| |
| assert isinstance(node.arm, _XdrBasic) |
| template = get_jinja2_template(environment, "encoder", node.arm.template) |
| print( |
| template.render( |
| name=node.arm.name, |
| type=node.arm.spec.type_name, |
| ) |
| ) |
| |
| template = get_jinja2_template(environment, "encoder", "break") |
| print(template.render()) |
| |
| |
| def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None: |
| """Emit an encoder function for an XDR union's default arm""" |
| default_case = node.default |
| |
| # Avoid a gcc warning about a default case with boolean discriminant |
| if default_case is None and node.discriminant.spec.type_name == "bool": |
| return |
| |
| template = get_jinja2_template(environment, "encoder", "default_spec") |
| print(template.render()) |
| |
| if default_case is None or isinstance(default_case.arm, _XdrVoid): |
| template = get_jinja2_template(environment, "encoder", "break") |
| print(template.render()) |
| return |
| |
| assert isinstance(default_case.arm, _XdrBasic) |
| template = get_jinja2_template(environment, "encoder", default_case.arm.template) |
| print( |
| template.render( |
| name=default_case.arm.name, |
| type=default_case.arm.spec.type_name, |
| ) |
| ) |
| |
| |
| def emit_union_encoder(environment, node: _XdrUnion) -> None: |
| """Emit one XDR union encoder""" |
| template = get_jinja2_template(environment, "encoder", "open") |
| print(template.render(name=node.name)) |
| |
| emit_union_switch_spec_encoder(environment, node.discriminant) |
| |
| for case in node.cases: |
| emit_union_case_spec_encoder(environment, case) |
| |
| emit_union_default_spec_encoder(environment, node) |
| |
| template = get_jinja2_template(environment, "encoder", "close") |
| print(template.render()) |
| |
| |
| class XdrUnionGenerator(SourceGenerator): |
| """Generate source code for XDR unions""" |
| |
| def __init__(self, language: str, peer: str): |
| """Initialize an instance of this class""" |
| self.environment = create_jinja2_environment(language, "union") |
| self.peer = peer |
| |
| def emit_declaration(self, node: _XdrUnion) -> None: |
| """Emit one declaration pair for an XDR union""" |
| emit_union_declaration(self.environment, node) |
| |
| def emit_definition(self, node: _XdrUnion) -> None: |
| """Emit one definition for an XDR union""" |
| emit_union_definition(self.environment, node) |
| |
| def emit_decoder(self, node: _XdrUnion) -> None: |
| """Emit one decoder function for an XDR union""" |
| emit_union_decoder(self.environment, node) |
| |
| def emit_encoder(self, node: _XdrUnion) -> None: |
| """Emit one encoder function for an XDR union""" |
| emit_union_encoder(self.environment, node) |