From fee64ccb2dfad79a3a5940fa495e51d5ed6d9acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= <hugo@lysator.liu.se> Date: Tue, 19 Sep 2023 15:24:25 +0200 Subject: [PATCH] Fix index access in interpolated strings. --- muppet/puppet/format/parser.py | 38 ++++++++++++++++ tests/test_parse_elsif.py | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/muppet/puppet/format/parser.py b/muppet/puppet/format/parser.py index 046823c..b5406a9 100644 --- a/muppet/puppet/format/parser.py +++ b/muppet/puppet/format/parser.py @@ -289,6 +289,39 @@ class ParserFormatter(Serializer[ParseDirective]): case _: return nop + def concat_access(self, item: PuppetAccess) -> ParseDirective: + """ + Parse an access inside an interpolated string. + + The following string + + .. code-block:: puppet + + "Hello ${people['name']['first']}!" + + serializes more or less ass + + .. code-block:: lisp + + (concat + (str "Hello ") + (access (access (var people) 'name') 'first')) + + And the regular PuppetAccess procedure can't be used, since + the leading '$' is optional here. + + Note that the delimiting "${" and "}" should be handled + outside this procedure. + """ + parser = ws + match item: + case PuppetAccess(PuppetVar(name), _): + parser &= optional(s('$')) & name + case PuppetAccess(PuppetAccess() as next, _): + parser &= self.concat_access(next) + parser &= ws & self.known_array("[]", item.args) + return parser + # -------------------------------------------------- @override @@ -363,6 +396,11 @@ class ParserFormatter(Serializer[ParseDirective]): except ParseError: for c in st: parser &= rich_char(c) + case PuppetAccess(): + # Needs to be separate from the "regular" + # PuppetAccess rule, since these variables + parser &= ws & "${" & self.concat_access(fragment) + parser &= ws & "}" case _: # TODO "${x[10][20]}" parser &= ws & "${" & ws & self.s(fragment) & ws & "}" diff --git a/tests/test_parse_elsif.py b/tests/test_parse_elsif.py index 9230420..88eb69e 100644 --- a/tests/test_parse_elsif.py +++ b/tests/test_parse_elsif.py @@ -1,5 +1,6 @@ """Tests for Parser combinator re-parsers.""" +import pytest from muppet.puppet.format.parser import ParserFormatter from muppet.puppet.ast import build_ast from muppet.puppet.parser import puppet_parser @@ -151,3 +152,81 @@ def test_funcall_with_block_inner(): print("parser:\n" + str(parser)) match_objects = ParserCombinator(s, "s").get(parser) pprint(match_objects) + + +def test_string_interpolation_1(): + # This one is parsed as 'qn' + s = """ + "${x}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) + + +def test_string_interpolation_2(): + # This one is parsed as 'var', but also looks like one + s = """ + "${$x}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) + + +def test_string_interpolation_3(): + # qn + s = """ + "${x + 1}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) + + +def test_string_interpolation_4(): + # var + s = """ + "${x + 1}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) + + +def test_string_interpolation_access(): + # var, but looks like qn + s = """ + "${x['y']}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) + + +def test_string_interpolation_deep_access(): + # var, but looks like qn + s = """ + "${x['y']['z']}" + """ + ast = build_ast(puppet_parser(s)) + pprint(ast) + parser = ParserFormatter(s, "s").serialize(ast) + print("parser:\n" + str(parser)) + match_objects = ParserCombinator(s, "s").get(parser) + pprint(match_objects) -- GitLab