from __future__ import annotations
from typing import TYPE_CHECKING
import amalgam.amalgams as am
import amalgam.primordials.boolean as boolean
from amalgam.primordials.utils import make_function
if TYPE_CHECKING: # pragma: no cover
from amalgam.environment import Environment
from amalgam.primordials.utils import Store
CONTROL: Store = {}
[docs]@make_function(CONTROL, "if", defer=True)
def _if(
env: Environment, cond: am.Amalgam, then: am.Amalgam, else_: am.Amalgam,
) -> am.Amalgam:
"""
Checks the truthiness of the evaluated :data:`cond`, evaluates and
returns :data:`then` if :data:`:TRUE`, otherwise, evaluates and
returns :data:`else_`.
"""
cond = boolean._bool(env, cond.evaluate(env))
if cond == am.Atom("TRUE"):
return then.evaluate(env)
return else_.evaluate(env)
[docs]@make_function(CONTROL, "when", defer=True)
def _when(
env: Environment, cond: am.Amalgam, body: am.Amalgam,
) -> am.Amalgam:
"""
Synonym for :func:`._if` that defaults :data:`else` to
:data:`:NIL`.
"""
cond = boolean._bool(env, cond.evaluate(env))
if cond == am.Atom("TRUE"):
return body.evaluate(env)
return am.Atom("NIL")
[docs]@make_function(CONTROL, "cond", defer=True)
def _cond(env: Environment, *pairs: am.Vector[am.Amalgam]) -> am.Amalgam:
"""
Traverses pairs of conditions and values. If the condition evaluates
to :data:`:TRUE`, returns the value pair and short-circuits
evaluation. If no conditions are met, :data:`:NIL` is returned.
"""
for pair in pairs:
pred, expr = pair
if boolean._bool(env, pred.evaluate(env)) == am.Atom("TRUE"):
return expr.evaluate(env)
return am.Atom("NIL")
[docs]@make_function(CONTROL, "do", defer=True)
def _do(env: Environment, *exprs: am.Amalgam) -> am.Amalgam:
"""
Evaluates a variadic amount of :data:`exprs`, returning the final
expression evaluated.
"""
accumulator: am.Amalgam = am.Atom("NIL")
for expr in exprs:
accumulator = expr.evaluate(env)
return accumulator
[docs]@make_function(CONTROL, "return", contextual=True)
def _return(env: Environment, result: am.Amalgam) -> am.Vector:
"""Exits a context with a :data:`result`."""
return am.Vector(am.Atom("payload"), result)
[docs]@make_function(CONTROL, "break", contextual=True)
def _break(env: Environment) -> am.Vector:
"""Exits a loop with :data:`:NIL`."""
return am.Vector(am.Atom("payload"), am.Atom("NIL"))
[docs]@make_function(CONTROL, "loop", defer=True, allows=("break", "return"))
def _loop(env: Environment, *exprs: am.Amalgam) -> am.Amalgam:
"""
Loops through and evaluates :data:`exprs` indefinitely until a
:data:`break` or :data:`return` is encountered.
"""
while True:
for expr in exprs:
result = expr.evaluate(env)
try:
return result.mapping["payload"]
except (AttributeError, KeyError):
pass