Worked on semantics for top level animations

This commit is contained in:
Kyler Olsen 2025-02-11 22:56:23 -07:00
parent 230b4d1080
commit a30be96bf2
1 changed files with 154 additions and 72 deletions

View File

@ -795,6 +795,27 @@ class Animation:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def identifier(self) -> Identifier: return self._identifier
@property
def range_start(self) -> Expression: return self._range_start
@property
def range_start_inclusive(self) -> bool: return self._range_start_inclusive
@property
def range_end(self) -> Expression: return self._range_end
@property
def range_end_inclusive(self) -> bool: return self._range_end_inclusive
@property
def step(self) -> Expression: return self._step
@property
def direction(self) -> AnimationDirection: return self._direction
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Animation ({self._identifier.value})\n"
s += f"{pre_cont}├─ Range Start \
@ -1858,6 +1879,31 @@ class FunctionDomainErrorRuntime(GraphRuntimeError):
)
class ContextNamespace:
_parent: "Context | ContextNamespace"
_values: "dict[str, int | float | ContextExpression | ContextAnimation]"
def __init__(
self,
parent: "Context | ContextNamespace",
values: "dict[str, int | float | ContextExpression | ContextAnimation] | None" = None,
):
self._parent = parent
self._values = values or {}
def lookup_var(self, variable: "ContextVariable") -> int | float:
if variable.name in self._values:
value = self._values[variable.name]
if isinstance(value, (int , float)): return value
else: return value.value(self)
else: return self._parent.lookup_var(variable)
def lookup_func(
self, function: "ContextFunctionCall") -> "ContextFunctionCallable":
return self._parent.lookup_func(function)
class ContextExpression:
_file_info: FileInfo
@ -1865,7 +1911,7 @@ class ContextExpression:
@property
def file_info(self) -> FileInfo: return self._file_info
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
raise RuntimeError
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
@ -1886,7 +1932,7 @@ class ContextLiteral(ContextExpression):
self._file_info = file_info
self._value = value
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
if self._value == 'π': return math.pi
elif '.' in self._value: return float(self._value)
else: return int(self._value)
@ -1912,7 +1958,7 @@ class ContextVariable(ContextExpression):
@property
def name(self) -> str: return self._name
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
return context.lookup_var(self)
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
@ -1942,7 +1988,7 @@ class ContextUnaryExpression(ContextExpression):
@property
def operator(self) -> UnaryOperator: return self._operator
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
match self.operator:
case UnaryOperator.Negate:
return - self.expression.value(context)
@ -1984,7 +2030,7 @@ class ContextBinaryExpression(ContextExpression):
@property
def operator(self) -> BinaryOperator: return self._operator
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
a, b = self.expression1.value(context), self.expression2.value(context)
match self.operator:
case BinaryOperator.Exponential:
@ -2111,7 +2157,7 @@ class ContextFunctionCall(ContextExpression):
@property
def arguments(self) -> list[ContextExpression]: return self._arguments[:]
def value(self, context: "Context") -> int | float:
def value(self, context: ContextNamespace) -> int | float:
args = [a.value(context) for a in self._arguments]
func = context.lookup_func(self)
if len(args) > func.parameters:
@ -2158,16 +2204,9 @@ class ContextFunction:
_context: "Context"
_values: "dict[str, ContextAnimation | ContextExpression]"
@property
def value(self) -> int | float: pass
def lookup(self, value: str) -> int | float:
if value in self._values:
return self._values[value].value(context)
else: return self._context.lookup_var(value)
def call(self, value: str, args: list[ContextExpression]) -> int | float:
return self._context.call(value, args)
def value(self, context: ContextNamespace) -> int | float:
pass
class ContextAnimation:
@ -2181,7 +2220,26 @@ class ContextAnimation:
_direction: AnimationDirection
_reversed: bool
def step(self, context: "Context"):
def __init__(
self,
start_value: int | float,
range_start: ContextExpression,
range_start_inclusive: bool,
range_end: ContextExpression,
range_end_inclusive: bool,
step: ContextExpression,
direction: AnimationDirection,
):
self._current = start_value
self._range_start = range_start
self._range_start_inclusive = range_start_inclusive
self._range_end = range_end
self._range_end_inclusive = range_end_inclusive
self._step = step
self._direction = direction
self._reversed = direction == AnimationDirection.Decrease
def step(self, context: ContextNamespace) -> int | float:
if self._direction == AnimationDirection.Increase or (
self._direction == AnimationDirection.Bounce and not self._reversed
):
@ -2218,10 +2276,10 @@ class ContextAnimation:
self._reversed = False
if not self._range_end_inclusive:
self._current -= self._step.value(context)
return self.value
return self.value(context)
@property
def value(self) -> int | float: return self._current
def value(self, context: ContextNamespace) -> int | float:
return self._current
class GraphType(Enum):
@ -2253,9 +2311,7 @@ class ContextGraph:
_color_saturation: None | ContextFunction
_color_luminosity: None | ContextFunction
def __iter__(self): return self
def step(self, context: "Context") -> tuple[
def step(self, context: ContextNamespace) -> tuple[
int | float,
int | float,
int | float,
@ -2264,31 +2320,31 @@ class ContextGraph:
]:
match self.graph_type:
case GraphType.X_Independent:
x_last = self._x.value # type: ignore
y_last = self._y.value # type: ignore
x = next(self._x) # type: ignore
y = self._y.value # type: ignore
x_last = self._x.value(context) # type: ignore
y_last = self._y.value(context) # type: ignore
x = self._x.step(context) # type: ignore
y = self._y.value(context) # type: ignore
case GraphType.Y_Independent:
y_last = self._y.value # type: ignore
x_last = self._x.value # type: ignore
y = next(self._y) # type: ignore
x = self._x.value # type: ignore
y_last = self._y.value(context) # type: ignore
x_last = self._x.value(context) # type: ignore
y = self._y.step(context) # type: ignore
x = self._x.value(context) # type: ignore
case GraphType.Parametric:
x_last = self._x.value # type: ignore
y_last = self._y.value # type: ignore
next(self._t) # type: ignore
x = self._x.value # type: ignore
y = self._y.value # type: ignore
x_last = self._x.value(context) # type: ignore
y_last = self._y.value(context) # type: ignore
self._t.step(context) # type: ignore
x = self._x.value(context) # type: ignore
y = self._y.value(context) # type: ignore
case GraphType.Polar:
theta_last = self._theta.value # type: ignore
r_last = self._r.value # type: ignore
theta = next(self._theta) # type: ignore
r = self._r.value # type: ignore
theta_last = self._theta.value(context) # type: ignore
r_last = self._r.value(context) # type: ignore
theta = self._theta.step(context) # type: ignore
r = self._r.value(context) # type: ignore
x_last = r_last * math.cos(theta_last)
y_last = r_last * math.sin(theta_last)
x = r * math.cos(theta)
y = r * math.sin(theta)
return x_last, y_last, x, y, self.color
return x_last, y_last, x, y, self.color(context)
@property
def graph_type(self) -> GraphType:
@ -2320,54 +2376,41 @@ class ContextGraph:
else:
return ColorSpace.Grey
@property
def color(self) -> tuple[float,float,float,float,]:
def color(
self, context: ContextNamespace) -> tuple[float,float,float,float,]:
if self._color_alpha is not None:
a = self._color_alpha.value
a = self._color_alpha.value(context)
else: a = 1
match self.color_space:
case ColorSpace.Grey:
if self._color_grey is not None:
c = self._color_grey.value
c = self._color_grey.value(context)
else: c = 1
r, g, b = c, c, c
case ColorSpace.RGB:
if self._color_red is not None:
r = self._color_red.value
r = self._color_red.value(context)
else: r = 1
if self._color_green is not None:
g = self._color_green.value
g = self._color_green.value(context)
else: g = 1
if self._color_blue is not None:
b = self._color_blue.value
b = self._color_blue.value(context)
else: b = 1
case ColorSpace.HSL:
r,g,b = 1,1,1
return r, g, b, a
@property
def value(self) -> int | float:
def value(self, context: ContextNamespace) -> int | float:
match self.graph_type:
case GraphType.X_Independent:
return self._x.value # type: ignore
return self._x.value(context) # type: ignore
case GraphType.Y_Independent:
return self._y.value # type: ignore
return self._y.value(context) # type: ignore
case GraphType.Parametric:
return self._t.value # type: ignore
return self._t.value(context) # type: ignore
case GraphType.Polar:
return self._theta.value # type: ignore
funcs_1 = {
"sin": math.sin,
"asin": math.asin,
"cos": math.cos,
"acos": math.acos,
"tan": math.tan,
"atan": math.atan,
"ln": math.log,
"log": math.log10,
}
return self._theta.value(context) # type: ignore
class Context:
@ -2379,15 +2422,18 @@ class Context:
def step(self):
for anim in self._animations.values():
next(anim)
anim.step(ContextNamespace(self))
def lookup_var(self, variable: ContextVariable) -> int | float:
if variable.name not in self._constants:
raise UndefinedVariableIdentifierRuntime(
variable.name, variable.file_info)
return self._constants[variable.name]
if variable.name in self._animations:
return self._animations[variable.name].value(ContextNamespace(self))
elif variable.name in self._constants:
return self._constants[variable.name]
raise UndefinedVariableIdentifierRuntime(
variable.name, variable.file_info)
def lookup_func(self, function: ContextFunctionCall) -> ContextFunctionCallable:
def lookup_func(
self, function: ContextFunctionCall) -> ContextFunctionCallable:
if function.identifier not in self._functions:
raise UndefinedFunctionIdentifierRuntime(
function.identifier, function.file_info)
@ -2588,7 +2634,43 @@ def semantics_analyzer(file: File) -> Context:
raise UnderDefinedConstantDefinition(child.file_info, None)
constants[child.identifier.value] = value
elif isinstance(child, Animation):
pass
range_start = _simplify_expression(
child.range_start,
constants,
functions,
True,
)
if not isinstance(range_start, ContextLiteral):
raise UnderDefinedConstantDefinition(
child.range_start.file_info, None)
start_value = float(range_start._value)
range_end = _simplify_expression(
child.range_end,
constants,
functions,
True,
)
if not isinstance(range_end, ContextLiteral):
raise UnderDefinedConstantDefinition(
child.range_end.file_info, None)
step = _simplify_expression(
child.step,
constants,
functions,
True,
)
if not isinstance(step, ContextLiteral):
raise UnderDefinedConstantDefinition(
child.step.file_info, None)
animations[child.identifier.value] = ContextAnimation(
start_value,
range_start,
child.range_start_inclusive,
range_end,
child.range_end_inclusive,
step,
child.direction,
)
elif isinstance(child, Graph):
pass
if screen is None: