225 lines
7.7 KiB
Python
225 lines
7.7 KiB
Python
import re
|
|
from typing import Optional
|
|
from typing import Union
|
|
|
|
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
|
|
from ytdl_sub.script.types.array import UnresolvedArray
|
|
from ytdl_sub.script.types.function import BuiltInFunction
|
|
from ytdl_sub.script.types.resolvable import Boolean
|
|
from ytdl_sub.script.types.resolvable import Float
|
|
from ytdl_sub.script.types.resolvable import Integer
|
|
from ytdl_sub.script.types.resolvable import Lambda
|
|
from ytdl_sub.script.types.resolvable import String
|
|
from ytdl_sub.script.types.syntax_tree import SyntaxTree
|
|
from ytdl_sub.script.types.variable import Variable
|
|
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
|
|
|
|
|
class TestParser:
|
|
def test_simple(self):
|
|
parsed = parse("hello world")
|
|
assert parsed == SyntaxTree([String(value="hello world")])
|
|
assert parsed.variables == set()
|
|
|
|
def test_single_function_one_arg(self):
|
|
parsed = parse("hello {%capitalize('hi mom')}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(name="capitalize", args=[String(value="hi mom")]),
|
|
]
|
|
)
|
|
|
|
def test_conditional(self):
|
|
parsed = parse("hello {%if(True, 'hi', 3.4)}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(
|
|
name="if", args=[Boolean(value=True), String(value="hi"), Float(value=3.4)]
|
|
),
|
|
]
|
|
)
|
|
assert parsed.ast[1].output_type() == Union[String, Float]
|
|
|
|
def test_conditional_as_input_same_outputs(self):
|
|
parsed = parse("hello {%concat(%if(True, 'hi', 'mom'), 'and dad')}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(
|
|
name="concat",
|
|
args=[
|
|
BuiltInFunction(
|
|
name="if", args=[Boolean(value=True), String("hi"), String("mom")]
|
|
),
|
|
String(value="and dad"),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
|
|
def test_conditional_as_input_different_outputs(self):
|
|
parsed = parse("hello {%string(%if(True, 'hi', 4))}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(
|
|
name="string",
|
|
args=[
|
|
BuiltInFunction(name="if", args=[Boolean(True), String("hi"), Integer(4)]),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
|
|
def test_nested_if_output_type(self):
|
|
parsed = parse(
|
|
"""{
|
|
%if(
|
|
True,
|
|
%if(
|
|
True,
|
|
%if(
|
|
True,
|
|
"winner",
|
|
True
|
|
),
|
|
True
|
|
),
|
|
True
|
|
)
|
|
}"""
|
|
)
|
|
assert len(parsed.ast) == 1
|
|
token = parsed.ast[0]
|
|
assert isinstance(token, BuiltInFunction)
|
|
assert token.output_type() == Union[String, Boolean]
|
|
|
|
def test_single_function_one_vararg(self):
|
|
parsed = parse("hello {%concat('hi mom')}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(name="concat", args=[String(value="hi mom")]),
|
|
]
|
|
)
|
|
|
|
def test_single_function_many_vararg(self):
|
|
parsed = parse("hello {%concat('hi', 'mom')}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(name="concat", args=[String(value="hi"), String(value="mom")]),
|
|
]
|
|
)
|
|
|
|
def test_single_function_many_args_with_optional_none(self):
|
|
parsed = parse("hello {%replace('hi mom', 'hi', '')}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(
|
|
name="replace",
|
|
args=[String(value="hi mom"), String(value="hi"), String(value="")],
|
|
),
|
|
]
|
|
)
|
|
|
|
def test_single_function_many_args_with_optional_provided(self):
|
|
parsed = parse("hello {%replace('hi mom', 'hi', '', 1)}")
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String("hello "),
|
|
BuiltInFunction(
|
|
name="replace",
|
|
args=[
|
|
String(value="hi mom"),
|
|
String(value="hi"),
|
|
String(value=""),
|
|
Integer(value=1),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
|
|
@pytest.mark.parametrize("whitespace", [None, " ", " ", "\n", " \n "])
|
|
def test_single_function_multiple_args(self, whitespace: Optional[str]):
|
|
s = whitespace
|
|
if s is None:
|
|
s = ""
|
|
|
|
input_str = (
|
|
f"hello{s}{{{s}%concat({s}'string'{s},{s}%string(1){s},{s}%string(2.4){s},"
|
|
f"{s}%string(True){s},{s}%string(variable_name){s},{s}%capitalize({s}'hi'{s}){s})}}"
|
|
f"{s}"
|
|
)
|
|
parsed = parse(input_str)
|
|
assert parsed == SyntaxTree(
|
|
[
|
|
String(value=f"hello{s}"),
|
|
BuiltInFunction(
|
|
name="concat",
|
|
args=[
|
|
String(value="string"),
|
|
BuiltInFunction(name="string", args=[Integer(value=1)]),
|
|
BuiltInFunction(name="string", args=[Float(value=2.4)]),
|
|
BuiltInFunction(name="string", args=[Boolean(value=True)]),
|
|
BuiltInFunction(name="string", args=[Variable(name="variable_name")]),
|
|
BuiltInFunction(name="capitalize", args=[String(value="hi")]),
|
|
],
|
|
),
|
|
]
|
|
+ ([String(value=s)] if s else [])
|
|
)
|
|
assert parsed.variables == {Variable(name="variable_name")}
|
|
|
|
def test_lambda_function(self):
|
|
assert parse("{%array_apply([1], %times_two)}") == SyntaxTree(
|
|
[
|
|
BuiltInFunction(
|
|
name="array_apply",
|
|
args=[
|
|
UnresolvedArray(value=[Integer(1)]),
|
|
Lambda(value="times_two"),
|
|
],
|
|
)
|
|
]
|
|
)
|
|
|
|
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):
|
|
with pytest.raises(InvalidSyntaxException, match=re.escape(str(BRACKET_NOT_CLOSED))):
|
|
parse("{")
|
|
|
|
def test_bracket_close(self):
|
|
with pytest.raises(InvalidSyntaxException, match=re.escape(str(BRACKET_NOT_CLOSED))):
|
|
parse("}")
|
|
|
|
def test_bracket_in_function(self):
|
|
with pytest.raises(
|
|
InvalidSyntaxException,
|
|
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} }}")
|