multiple lists work :o
This commit is contained in:
parent
870b3ee018
commit
ec62c7fe67
6 changed files with 54 additions and 14 deletions
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from ytdl_sub.script.functions.array_functions import ArrayFunctions
|
||||||
from ytdl_sub.script.functions.numeric_functions import NumericFunctions
|
from ytdl_sub.script.functions.numeric_functions import NumericFunctions
|
||||||
from ytdl_sub.script.functions.special_functions import SpecialFunctions
|
from ytdl_sub.script.functions.special_functions import SpecialFunctions
|
||||||
from ytdl_sub.script.functions.string_functions import StringFunctions
|
from ytdl_sub.script.functions.string_functions import StringFunctions
|
||||||
|
|
||||||
|
|
||||||
class Functions(StringFunctions, NumericFunctions, SpecialFunctions):
|
class Functions(StringFunctions, NumericFunctions, SpecialFunctions, ArrayFunctions):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
14
src/ytdl_sub/script/functions/array_functions.py
Normal file
14
src/ytdl_sub/script/functions/array_functions.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from ytdl_sub.script.types.resolvable import Array
|
||||||
|
from ytdl_sub.script.types.resolvable import Resolvable
|
||||||
|
|
||||||
|
|
||||||
|
class ArrayFunctions:
|
||||||
|
@staticmethod
|
||||||
|
def extend(*arrays: Array) -> Array:
|
||||||
|
output: List[Resolvable] = []
|
||||||
|
for array in arrays:
|
||||||
|
output.extend(array.value)
|
||||||
|
|
||||||
|
return Array(output)
|
||||||
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
||||||
from ytdl_sub.script.syntax_tree import SyntaxTree
|
from ytdl_sub.script.syntax_tree import SyntaxTree
|
||||||
from ytdl_sub.script.types.function import ArgumentType
|
from ytdl_sub.script.types.function import ArgumentType
|
||||||
from ytdl_sub.script.types.function import Function
|
from ytdl_sub.script.types.function import Function
|
||||||
|
from ytdl_sub.script.types.resolvable import Array
|
||||||
from ytdl_sub.script.types.resolvable import Boolean
|
from ytdl_sub.script.types.resolvable import Boolean
|
||||||
from ytdl_sub.script.types.resolvable import Float
|
from ytdl_sub.script.types.resolvable import Float
|
||||||
from ytdl_sub.script.types.resolvable import Integer
|
from ytdl_sub.script.types.resolvable import Integer
|
||||||
|
|
@ -48,7 +49,7 @@ class _Parser:
|
||||||
if ch.isspace() and not var_name:
|
if ch.isspace() and not var_name:
|
||||||
self._pos += 1
|
self._pos += 1
|
||||||
continue
|
continue
|
||||||
if ch in ["}", ",", ")"] or ch.isspace():
|
if ch in ["}", ",", ")", "]"] or ch.isspace():
|
||||||
break
|
break
|
||||||
|
|
||||||
is_lower = ch.isascii() and ch.islower()
|
is_lower = ch.isascii() and ch.islower()
|
||||||
|
|
@ -115,6 +116,9 @@ class _Parser:
|
||||||
return Boolean(value=False)
|
return Boolean(value=False)
|
||||||
if self._read(increment_pos=False) in ["'", '"']:
|
if self._read(increment_pos=False) in ["'", '"']:
|
||||||
return self._parse_string()
|
return self._parse_string()
|
||||||
|
if self._read(increment_pos=False) == "[":
|
||||||
|
self._pos += 1
|
||||||
|
return self._parse_array()
|
||||||
if self._read(increment_pos=False).isascii() and self._read(increment_pos=False).islower():
|
if self._read(increment_pos=False).isascii() and self._read(increment_pos=False).islower():
|
||||||
return self._parse_variable()
|
return self._parse_variable()
|
||||||
raise StringFormattingException(
|
raise StringFormattingException(
|
||||||
|
|
@ -122,7 +126,7 @@ class _Parser:
|
||||||
"string, boolean, or variable without brackets"
|
"string, boolean, or variable without brackets"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _parse_function_args(self) -> List[ArgumentType]:
|
def _parse_args(self, breaking_char: str = ")") -> List[ArgumentType]:
|
||||||
"""
|
"""
|
||||||
Begin parsing function args after the first ``(``, i.e. ``function_name(``
|
Begin parsing function args after the first ``(``, i.e. ``function_name(``
|
||||||
"""
|
"""
|
||||||
|
|
@ -131,7 +135,7 @@ class _Parser:
|
||||||
|
|
||||||
arguments: List[ArgumentType] = []
|
arguments: List[ArgumentType] = []
|
||||||
while ch := self._read(increment_pos=False):
|
while ch := self._read(increment_pos=False):
|
||||||
if ch == ")":
|
if ch == breaking_char:
|
||||||
break
|
break
|
||||||
|
|
||||||
if ch.isspace():
|
if ch.isspace():
|
||||||
|
|
@ -162,7 +166,22 @@ class _Parser:
|
||||||
if ch != "(":
|
if ch != "(":
|
||||||
function_name += ch
|
function_name += ch
|
||||||
else:
|
else:
|
||||||
function_args = self._parse_function_args()
|
function_args = self._parse_args()
|
||||||
|
|
||||||
|
raise StringFormattingException("Invalid function")
|
||||||
|
|
||||||
|
def _parse_array(self) -> Array:
|
||||||
|
"""
|
||||||
|
Begin parsing an array after reading the first ``[``
|
||||||
|
"""
|
||||||
|
function_args: List[String | Variable | "Function"] = []
|
||||||
|
|
||||||
|
while ch := self._read(increment_pos=False):
|
||||||
|
if ch == "]":
|
||||||
|
self._pos += 1
|
||||||
|
return Array(value=function_args)
|
||||||
|
else:
|
||||||
|
function_args = self._parse_args(breaking_char="]")
|
||||||
|
|
||||||
raise StringFormattingException("Invalid function")
|
raise StringFormattingException("Invalid function")
|
||||||
|
|
||||||
|
|
@ -193,6 +212,9 @@ class _Parser:
|
||||||
if ch1 == "%":
|
if ch1 == "%":
|
||||||
self._pos += 1
|
self._pos += 1
|
||||||
self._ast.append(self._parse_function())
|
self._ast.append(self._parse_function())
|
||||||
|
elif ch1 == "[":
|
||||||
|
self._pos += 1
|
||||||
|
self._ast.append(self._parse_array())
|
||||||
else:
|
else:
|
||||||
self._ast.append(self._parse_variable())
|
self._ast.append(self._parse_variable())
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from typing import final
|
||||||
from typing import get_origin
|
from typing import get_origin
|
||||||
|
|
||||||
from ytdl_sub.script.functions import Functions
|
from ytdl_sub.script.functions import Functions
|
||||||
|
from ytdl_sub.script.types.resolvable import Array
|
||||||
from ytdl_sub.script.types.resolvable import Boolean
|
from ytdl_sub.script.types.resolvable import Boolean
|
||||||
from ytdl_sub.script.types.resolvable import Float
|
from ytdl_sub.script.types.resolvable import Float
|
||||||
from ytdl_sub.script.types.resolvable import Integer
|
from ytdl_sub.script.types.resolvable import Integer
|
||||||
|
|
@ -26,7 +27,7 @@ from ytdl_sub.script.types.resolvable import String
|
||||||
from ytdl_sub.script.types.variable import Variable
|
from ytdl_sub.script.types.variable import Variable
|
||||||
from ytdl_sub.utils.exceptions import StringFormattingException
|
from ytdl_sub.utils.exceptions import StringFormattingException
|
||||||
|
|
||||||
ArgumentType = Union[Integer, Float, String, Boolean, Variable, "Function"]
|
ArgumentType = Union[Integer, Float, String, Boolean, Variable, "Function", Array]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,8 @@ class String(ResolvableT[str]):
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class _List(Resolvable, Generic[T], ABC):
|
class Array(Resolvable):
|
||||||
value: List[T]
|
value: List[Resolvable]
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"[{', '.join([str(val) for val in self.value])}]"
|
return f"[{', '.join([val.value for val in self.value])}]"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class StringList(_List[String]):
|
|
||||||
pass
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,13 @@ class TestParser:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_array(self):
|
||||||
|
parsed = parse("hello {['elem1', 'elem2']}")
|
||||||
|
parsed_empty = parse("hello {[]}")
|
||||||
|
parsed_with_var = parse("hello {['elem1', variable_name]}")
|
||||||
|
parsed_extend = parse("hi {%extend(['elem1', 'elem2'], ['elem3'], [], ['elem4'])}")
|
||||||
|
assert False
|
||||||
|
|
||||||
def test_conditional(self):
|
def test_conditional(self):
|
||||||
parsed = parse("hello {%if(True, 'hi', 3.4)}")
|
parsed = parse("hello {%if(True, 'hi', 3.4)}")
|
||||||
assert parsed == SyntaxTree(
|
assert parsed == SyntaxTree(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue