multiple lists work :o

This commit is contained in:
Jesse Bannon 2023-11-07 01:00:59 -08:00
parent 870b3ee018
commit ec62c7fe67
6 changed files with 54 additions and 14 deletions

View file

@ -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

View 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)

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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(