def visit_placeholder_node( self, node: nodes.PlaceholderNode ) -> Optional[nodes.Node]: """ Optimizations: - If placeholder is defined in bound context, and placeholder key has a single value, replaces the placeholder by a LiteralNode having that value. - If placeholder is defined in bound context and placeholder key has multiple values, replaces the placeholder by an AnyNode of LiteralNodes corresponding to the context values. Args: node (nodes.PlaceholderNode): The placeholder to replace. Returns: The replaced node. """ if not self._ctx: return node # Not the most elegant solution, could be improved # Have to manually call the visit literal method since new nodes will never be visited as tree is traversed # depth-first try: values = self._ctx.get(node.key) except KeyError: return node if len(values) == 1: if values[0] is None: return None return self.visit_literal_node(nodes.LiteralNode(values[0])) return nodes.AnyNode( [self.visit_literal_node(nodes.LiteralNode(val)) for val in values] )
def test_optimizer_visit_entity_node_does_nothing_without_macro() -> None: o = Optimizer({}, {}) entity_1 = nodes.EntityNode( "some_entity", [nodes.LiteralNode("a"), nodes.LiteralNode("b")]) entity_2 = nodes.EntityNode( "some_entity", [nodes.LiteralNode("a"), nodes.LiteralNode("b")]) assert entity_2 == o.visit_entity_node(entity_1)
def test_grammar_node_generate(): grammar = nodes.Grammar( { "some_entity": nodes.EntityNode( "some_entity", [nodes.LiteralNode("a"), nodes.PlaceholderNode("a")]) }, {}, ) assert grammar.generate("some_entity", {"a": "sdf"}) == "a sdf"
def test_optional_node_generate(): expected_value = "a" node = nodes.OptionalNode(nodes.LiteralNode(expected_value)) for i in range(1000): with mock.patch.object( node.expression, "generate", return_value=node.expression.generate()) as mock_gen: val = node.generate() if val == "": mock_gen.assert_not_called() else: assert expected_value == val
def test_optimizer_visit_entity_node_with_macro() -> None: parameters = [nodes.ParameterNode("a"), nodes.ParameterNode("b")] o = Optimizer( {}, {"macro_a": nodes.MacroNode("macro_a", parameters, parameters[::-1])}) entity_1 = nodes.EntityNode( "some_entity", [nodes.LiteralNode("a"), nodes.LiteralNode("b")], macro=nodes.MacroReferenceNode("macro_a"), ) expected_entity = nodes.EntityNode( "some_entity", [ nodes.ParameterNode("b", nodes.LiteralNode("b")), nodes.ParameterNode("a", nodes.LiteralNode("a")), ], ) assert expected_entity == o.visit_entity_node(entity_1)
def expression(self) -> Expression: if self._accept(TokenType.Literal): assert self.current_token is not None return nodes.LiteralNode(value=self.current_token.value) if self._accept(TokenType.Placeholder): assert self.current_token is not None return nodes.PlaceholderNode(key=self.current_token.value) if self._accept(TokenType.Symbol): assert self.current_token is not None return nodes.ReferenceNode(key=self.current_token.value) if self._accept(TokenType.BracketOpen): optional_expr = self.expression() self._expect(TokenType.BracketClose) return nodes.OptionalNode(optional_expr) self._expect(TokenType.ParenOpen) if self._accept(TokenType.Function): assert self.current_token is not None fn_type = self.current_token.value if fn_type == Function.If: condition = self.condition() return condition if fn_type == Function.Repeat: repeat = self.repeat() return repeat children = [] while not self._accept(TokenType.ParenClose): children.append(self.expression()) if fn_type == Function.Any: return nodes.AnyNode(children) children = [] while not self._accept(TokenType.ParenClose): children.append(self.expression()) return nodes.ListNode(children)
from txtgen.parser import Expression, DescentParser from txtgen import nodes from typing import Optional import pytest @pytest.mark.parametrize( "text,expected_expression", [ ('"hello world"', nodes.LiteralNode(value="hello world")), ("$hello.world", nodes.PlaceholderNode(key="hello.world")), ("hello_world", nodes.ReferenceNode(key="hello_world")), ("[hello_world]", nodes.OptionalNode(nodes.ReferenceNode("hello_world"))), ( "(a b c)", nodes.ListNode( [ nodes.ReferenceNode("a"), nodes.ReferenceNode("b"), nodes.ReferenceNode("c"), ] ), ), ( "(any a b)", nodes.AnyNode([nodes.ReferenceNode("a"), nodes.ReferenceNode("b")]), ), ( "(if $a=$b a)",
def test_literal_node_generate(): for val in ["asdf", "hello", "world", "hey_there", "1234", ","]: n = nodes.LiteralNode(val) assert n.generate() == val
from txtgen import nodes from txtgen.constants import PUNCTUATION from txtgen.context import Context from typing import Optional, Set from unittest import mock import pytest @pytest.mark.parametrize( "input_node,expected_output", [ ( nodes.LiteralNode("hello"), nodes.ListNode( [nodes.LiteralNode(" "), nodes.LiteralNode("hello")]), ), *[(nodes.LiteralNode(x), nodes.LiteralNode(x)) for x in PUNCTUATION], ], ) def test_sub_punctuation(input_node: nodes.LiteralNode, expected_output: nodes.Node) -> None: assert nodes.sub_punctuation(input_node) == expected_output @pytest.mark.parametrize( "input_node,input_ctx,expected_node", [
from txtgen import nodes from txtgen.context import Context from txtgen.optimizer import Optimizer from typing import Dict, Optional import pytest @pytest.mark.parametrize( "node,expected", [ (nodes.AnyNode([nodes.LiteralNode("a")]), nodes.LiteralNode("a")), ( nodes.AnyNode([nodes.LiteralNode("a"), nodes.LiteralNode("b")]), nodes.AnyNode([nodes.LiteralNode("a"), nodes.LiteralNode("b")]), ), ], ) def test_optimizer_visit_any_node(node: nodes.Node, expected: nodes.Node) -> None: o = Optimizer({}, {}) assert expected == o.visit_any_node(node) @pytest.mark.parametrize( "node,ctx,expected", [ (