diff --git a/src/ytdl_sub/script/functions/string_functions.py b/src/ytdl_sub/script/functions/string_functions.py index 0fbda658..4db55855 100644 --- a/src/ytdl_sub/script/functions/string_functions.py +++ b/src/ytdl_sub/script/functions/string_functions.py @@ -1,4 +1,4 @@ -from ytdl_sub.script.types import String +from ytdl_sub.script.types.resolvable import String class StringFunctions: diff --git a/src/ytdl_sub/script/overrides_resolver.py b/src/ytdl_sub/script/overrides_resolver.py index e3101e37..34a58a9d 100644 --- a/src/ytdl_sub/script/overrides_resolver.py +++ b/src/ytdl_sub/script/overrides_resolver.py @@ -6,7 +6,7 @@ from typing import Set from ytdl_sub.script.functions import Resolvable from ytdl_sub.script.parser import parse from ytdl_sub.script.syntax_tree import SyntaxTree -from ytdl_sub.script.syntax_tree import Variable +from ytdl_sub.script.types.variable import Variable from ytdl_sub.utils.exceptions import StringFormattingException diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index 1c55ef08..5bc59e62 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -1,14 +1,14 @@ from typing import List from typing import Optional -from ytdl_sub.script.syntax_tree import ArgumentType -from ytdl_sub.script.syntax_tree import Function from ytdl_sub.script.syntax_tree import SyntaxTree -from ytdl_sub.script.syntax_tree import Variable -from ytdl_sub.script.types import Boolean -from ytdl_sub.script.types import Float -from ytdl_sub.script.types import Integer -from ytdl_sub.script.types import String +from ytdl_sub.script.types.function import ArgumentType +from ytdl_sub.script.types.function import Function +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 String +from ytdl_sub.script.types.variable import Variable from ytdl_sub.utils.exceptions import StringFormattingException from ytdl_sub.validators.string_formatter_validators import is_valid_source_variable_name diff --git a/src/ytdl_sub/script/syntax_tree.py b/src/ytdl_sub/script/syntax_tree.py index 4a086dbb..3df074c8 100644 --- a/src/ytdl_sub/script/syntax_tree.py +++ b/src/ytdl_sub/script/syntax_tree.py @@ -1,83 +1,17 @@ -from abc import ABC -from abc import abstractmethod from dataclasses import dataclass from typing import Dict from typing import List from typing import Optional from typing import Set -from typing import Union -from typing import final -from ytdl_sub.script.functions import Functions -from ytdl_sub.script.types import Boolean -from ytdl_sub.script.types import Float -from ytdl_sub.script.types import Integer -from ytdl_sub.script.types import Resolvable -from ytdl_sub.script.types import String +from ytdl_sub.script.types.function import Function +from ytdl_sub.script.types.function import VariableDependency +from ytdl_sub.script.types.resolvable import Resolvable +from ytdl_sub.script.types.resolvable import String +from ytdl_sub.script.types.variable import Variable from ytdl_sub.utils.exceptions import StringFormattingException -@dataclass(frozen=True) -class Variable: - name: str - - -ArgumentType = Union[Integer, Float, String, Boolean, Variable, "Function"] - - -@dataclass(frozen=True) -class VariableDependency(ABC): - @property - @abstractmethod - def variables(self) -> Set[Variable]: - raise NotImplemented() - - @abstractmethod - def resolve(self, resolved_variables: Dict[Variable, Resolvable]) -> str: - raise NotImplemented() - - @final - def has_variable_dependency(self, resolved_variables: Dict[Variable, Resolvable]) -> bool: - """ - Returns - ------- - True if variable dependency. False otherwise. - """ - return self.variables.issubset(set(resolved_variables.keys())) - - -@dataclass(frozen=True) -class Function(VariableDependency): - name: str - args: List[ArgumentType] - - def __post_init__(self): - # TODO: Figure out resolution via introspecting args and outputs of function - try: - getattr(Functions, self.name) - except AttributeError: - raise StringFormattingException(f"Function name {self.name} does not exist") - - @property - def variables(self) -> Set[Variable]: - """ - Returns - ------- - All variables used within the function - """ - variables: Set[Variable] = set() - for arg in self.args: - if isinstance(arg, Variable): - variables.add(arg) - elif isinstance(arg, Function): - variables.update(arg.variables) - - return variables - - def resolve(self, resolved_variables: Dict[Variable, Resolvable]) -> Resolvable: - raise NotImplemented() - - @dataclass(frozen=True) class SyntaxTree(VariableDependency): ast: List[String | Variable | Function] diff --git a/src/ytdl_sub/script/types.py b/src/ytdl_sub/script/types.py index 5bb41edf..b28b04f6 100644 --- a/src/ytdl_sub/script/types.py +++ b/src/ytdl_sub/script/types.py @@ -1,48 +1,3 @@ -from abc import ABC -from abc import abstractmethod -from dataclasses import dataclass -from typing import Generic -from typing import TypeVar - -T = TypeVar("T") -NumericT = TypeVar("NumericT", bound=int | float) -@dataclass(frozen=True) -class Resolvable(ABC): - @abstractmethod - def resolve(self) -> str: - ... - -@dataclass(frozen=True) -class ResolvableT(Resolvable, Generic[T]): - value: T - - def resolve(self) -> str: - return str(self.value) - - -@dataclass(frozen=True) -class Numeric(ResolvableT[NumericT], ABC, Generic[NumericT]): - pass - - -@dataclass(frozen=True) -class Integer(ResolvableT[int]): - pass - - -@dataclass(frozen=True) -class Float(ResolvableT[float]): - pass - - -@dataclass(frozen=True) -class Boolean(ResolvableT[bool]): - pass - - -@dataclass(frozen=True) -class String(ResolvableT[str]): - pass diff --git a/src/ytdl_sub/script/types/__init__.py b/src/ytdl_sub/script/types/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ytdl_sub/script/types/function.py b/src/ytdl_sub/script/types/function.py new file mode 100644 index 00000000..7bf652b0 --- /dev/null +++ b/src/ytdl_sub/script/types/function.py @@ -0,0 +1,72 @@ +from abc import ABC +from abc import abstractmethod +from dataclasses import dataclass +from typing import Dict +from typing import List +from typing import Set +from typing import Union +from typing import final + +from ytdl_sub.script.functions import Functions +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 Resolvable +from ytdl_sub.script.types.resolvable import String +from ytdl_sub.script.types.variable import Variable +from ytdl_sub.utils.exceptions import StringFormattingException + +ArgumentType = Union[Integer, Float, String, Boolean, Variable, "Function"] + + +@dataclass(frozen=True) +class VariableDependency(ABC): + @property + @abstractmethod + def variables(self) -> Set[Variable]: + raise NotImplemented() + + @abstractmethod + def resolve(self, resolved_variables: Dict[Variable, Resolvable]) -> str: + raise NotImplemented() + + @final + def has_variable_dependency(self, resolved_variables: Dict[Variable, Resolvable]) -> bool: + """ + Returns + ------- + True if variable dependency. False otherwise. + """ + return self.variables.issubset(set(resolved_variables.keys())) + + +@dataclass(frozen=True) +class Function(VariableDependency): + name: str + args: List[ArgumentType] + + def __post_init__(self): + # TODO: Figure out resolution via introspecting args and outputs of function + try: + getattr(Functions, self.name) + except AttributeError: + raise StringFormattingException(f"Function name {self.name} does not exist") + + @property + def variables(self) -> Set[Variable]: + """ + Returns + ------- + All variables used within the function + """ + variables: Set[Variable] = set() + for arg in self.args: + if isinstance(arg, Variable): + variables.add(arg) + elif isinstance(arg, Function): + variables.update(arg.variables) + + return variables + + def resolve(self, resolved_variables: Dict[Variable, Resolvable]) -> Resolvable: + raise NotImplemented() diff --git a/src/ytdl_sub/script/types/resolvable.py b/src/ytdl_sub/script/types/resolvable.py new file mode 100644 index 00000000..5bb41edf --- /dev/null +++ b/src/ytdl_sub/script/types/resolvable.py @@ -0,0 +1,48 @@ +from abc import ABC +from abc import abstractmethod +from dataclasses import dataclass +from typing import Generic +from typing import TypeVar + +T = TypeVar("T") +NumericT = TypeVar("NumericT", bound=int | float) + + +@dataclass(frozen=True) +class Resolvable(ABC): + @abstractmethod + def resolve(self) -> str: + ... + + +@dataclass(frozen=True) +class ResolvableT(Resolvable, Generic[T]): + value: T + + def resolve(self) -> str: + return str(self.value) + + +@dataclass(frozen=True) +class Numeric(ResolvableT[NumericT], ABC, Generic[NumericT]): + pass + + +@dataclass(frozen=True) +class Integer(ResolvableT[int]): + pass + + +@dataclass(frozen=True) +class Float(ResolvableT[float]): + pass + + +@dataclass(frozen=True) +class Boolean(ResolvableT[bool]): + pass + + +@dataclass(frozen=True) +class String(ResolvableT[str]): + pass diff --git a/src/ytdl_sub/script/types/variable.py b/src/ytdl_sub/script/types/variable.py new file mode 100644 index 00000000..4bcb8b63 --- /dev/null +++ b/src/ytdl_sub/script/types/variable.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass + + +@dataclass(frozen=True) +class Variable: + name: str diff --git a/tests/unit/script/test_parser.py b/tests/unit/script/test_parser.py index 6c6f010f..f785a03b 100644 --- a/tests/unit/script/test_parser.py +++ b/tests/unit/script/test_parser.py @@ -1,13 +1,13 @@ import pytest from ytdl_sub.script.parser import parse -from ytdl_sub.script.syntax_tree import Function from ytdl_sub.script.syntax_tree import SyntaxTree -from ytdl_sub.script.syntax_tree import Variable -from ytdl_sub.script.types import Boolean -from ytdl_sub.script.types import Float -from ytdl_sub.script.types import Integer -from ytdl_sub.script.types import String +from ytdl_sub.script.types.function import Function +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 String +from ytdl_sub.script.types.variable import Variable from ytdl_sub.utils.exceptions import StringFormattingException