Skip to content

Commit

Permalink
[DEV] Escapable curly braces (#955)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmbannon authored Apr 2, 2024
1 parent 598c574 commit c622cf6
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/source/config_reference/scripting/scripting_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ triple-quotes can be used to avoid *closing* the String.
%string("""This has both " and ' in it.""")
}
If you want a plain string that contains literal curly braces, you can escape them like so:

.. code-block:: yaml
string_variable: "This contains \\{ literal curly braces \\}"
Integer
~~~~~~~

Expand Down
12 changes: 9 additions & 3 deletions src/ytdl_sub/script/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class ParsedArgType(Enum):

FUNCTION_INVALID_CHAR = InvalidSyntaxException("Invalid value when parsing a function")

BRACKET_INVALID_CHAR = InvalidSyntaxException("Invalid value within brackets")


def _UNEXPECTED_CHAR_ARGUMENT(arg_type: ParsedArgType):
return InvalidSyntaxException(f"Unexpected character when parsing {arg_type.value} arguments")
Expand Down Expand Up @@ -505,6 +507,10 @@ def _parse_map(self) -> UnresolvedMap:
raise UNREACHABLE

def _parse_main_loop(self, ch: str) -> bool:
if ch == "\\" and self._read(increment_pos=False) in {"{", "}"}:
# Escape brackets are \{ and \}, only add the second char
self._literal_str += self._read()
return True
if ch == "}":
if self._bracket_counter == 0:
raise BRACKET_NOT_CLOSED
Expand Down Expand Up @@ -558,9 +564,9 @@ def _parse_main_loop(self, ch: str) -> bool:
elif self._bracket_counter == 0:
# Only accumulate literal str if not in brackets
self._literal_str += ch
else:
# Should only be possible to get here if it's a space
assert ch.isspace()
elif not ch.isspace():
self._set_highlight_position(pos=self._pos - 1)
raise BRACKET_INVALID_CHAR

return True

Expand Down
15 changes: 15 additions & 0 deletions tests/unit/script/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest

from ytdl_sub.script.parser import _UNEXPECTED_CHAR_ARGUMENT
from ytdl_sub.script.parser import BRACKET_INVALID_CHAR
from ytdl_sub.script.parser import BRACKET_NOT_CLOSED
from ytdl_sub.script.parser import ParsedArgType
from ytdl_sub.script.parser import parse
Expand Down Expand Up @@ -192,6 +193,15 @@ def test_lambda_function(self):
]
)

def test_escaped_bracket(self):
assert parse("\\{ This is escape {%string('{}')} and here \\}") == SyntaxTree(
[
String(value="{ This is escape "),
BuiltInFunction(name="string", args=[String(value="{}")]),
String(value=" and here }"),
]
)


class TestParserBracketFailures:
def test_bracket_open(self):
Expand All @@ -208,3 +218,8 @@ def test_bracket_in_function(self):
match=re.escape(str(_UNEXPECTED_CHAR_ARGUMENT(ParsedArgType.MAP_KEY))),
):
parse("hello {%capitalize({as_arg)}")

@pytest.mark.parametrize("char", [")", ",", "*", "]"])
def test_extra_char_in_brackets(self, char: str):
with pytest.raises(InvalidSyntaxException, match=re.escape(str(BRACKET_INVALID_CHAR))):
parse(f"{{ %string('hi') {char} }}")

0 comments on commit c622cf6

Please sign in to comment.