diff --git a/shared/sx/__init__.py b/shared/sx/__init__.py
index 93a0eb2..20d357c 100644
--- a/shared/sx/__init__.py
+++ b/shared/sx/__init__.py
@@ -31,20 +31,11 @@ from .parser import (
parse_all,
serialize,
)
-import os as _os
-
-if _os.environ.get("SX_USE_REF") == "1":
- from .ref.sx_ref import (
- EvalError,
- evaluate,
- make_env,
- )
-else:
- from .evaluator import (
- EvalError,
- evaluate,
- make_env,
- )
+from .evaluator import (
+ EvalError,
+ evaluate,
+ make_env,
+)
from .primitives import (
all_primitives,
diff --git a/shared/sx/evaluator.py b/shared/sx/evaluator.py
index 1f61c99..4e70352 100644
--- a/shared/sx/evaluator.py
+++ b/shared/sx/evaluator.py
@@ -1,1199 +1,94 @@
"""
-S-expression evaluator.
+S-expression evaluator — thin shim over bootstrapped sx_ref.py.
-Walks a parsed s-expression tree and evaluates it in an environment.
+All evaluation logic lives in the spec (shared/sx/ref/eval.sx) and is
+bootstrapped to Python (shared/sx/ref/sx_ref.py). This module re-exports
+the public API and internal helpers under their historical names so that
+existing callers don't need updating.
-Special forms:
- (if cond then else?)
- (when cond body)
- (cond clause...) — Scheme-style ((test body)...) or Clojure-style (test body...)
- (case expr val body... :else default)
- (and expr...) (or expr...)
- (let ((name val)...) body) or (let (name val name val...) body)
- (lambda (params...) body) or (fn (params...) body)
- (define name value)
- (defcomp ~name (&key param...) body)
- (defrelation :name :from "type" :to "type" :cardinality :card ...)
- (begin expr...)
- (quote expr)
- (do expr...) — alias for begin
- (-> val form...) — thread-first macro
-
-Higher-order forms (operate on lambdas):
- (map fn coll)
- (map-indexed fn coll)
- (filter fn coll)
- (reduce fn init coll)
- (some fn coll)
- (every? fn coll)
- (for-each fn coll)
+Imports are lazy (inside functions/properties) to avoid circular imports
+during bootstrapping: bootstrap_py.py → parser → __init__ → evaluator → sx_ref.
"""
from __future__ import annotations
-from typing import Any
-from .types import Component, Continuation, HandlerDef, Island, Keyword, Lambda, Macro, NIL, PageDef, RelationDef, Symbol, _ShiftSignal
-from .primitives import _PRIMITIVES
+def _ref():
+ """Lazy import of the bootstrapped evaluator."""
+ from .ref import sx_ref
+ return sx_ref
+# ---------------------------------------------------------------------------
+# Public API — these are the most used, so we make them importable directly
+# ---------------------------------------------------------------------------
+
class EvalError(Exception):
- """Error during expression evaluation."""
+ """Error during expression evaluation.
+
+ Delegates to the bootstrapped EvalError at runtime but is defined here
+ so imports don't fail during bootstrapping.
+ """
pass
-class _Thunk:
- """Deferred evaluation — returned from tail positions for TCO."""
- __slots__ = ("expr", "env")
-
- def __init__(self, expr: Any, env: dict[str, Any]):
- self.expr = expr
- self.env = env
+def evaluate(expr, env=None):
+ return _ref().evaluate(expr, env)
-def _trampoline(val: Any) -> Any:
- """Unwrap thunks by re-entering the evaluator until we get an actual value."""
- while isinstance(val, _Thunk):
- val = _eval(val.expr, val.env)
- return val
+def make_env(**kwargs):
+ return _ref().make_env(**kwargs)
# ---------------------------------------------------------------------------
-# Public API
+# Internal helpers — used by html.py, async_eval.py, handlers.py, etc.
# ---------------------------------------------------------------------------
-def evaluate(expr: Any, env: dict[str, Any] | None = None) -> Any:
- """Evaluate *expr* in *env* and return the result."""
- if env is None:
- env = {}
- result = _eval(expr, env)
- while isinstance(result, _Thunk):
- result = _eval(result.expr, result.env)
- return result
+def _eval(expr, env):
+ return _ref().eval_expr(expr, env)
-def make_env(**kwargs: Any) -> dict[str, Any]:
- """Convenience: create an environment dict with initial bindings."""
- return dict(kwargs)
+def _trampoline(val):
+ return _ref().trampoline(val)
+
+
+def _call_lambda(fn, args, caller_env):
+ return _ref().call_lambda(fn, args, caller_env)
+
+
+def _call_component(comp, raw_args, env):
+ return _ref().call_component(comp, raw_args, env)
+
+
+def _expand_macro(macro, raw_args, env):
+ return _ref().expand_macro(macro, raw_args, env)
# ---------------------------------------------------------------------------
-# Internal evaluator
+# Special-form wrappers: callers pass (expr, env) with expr[0] = head symbol.
+# sx_ref.py special forms take (args, env) where args = expr[1:].
# ---------------------------------------------------------------------------
-def _eval(expr: Any, env: dict[str, Any]) -> Any:
- # --- literals ---------------------------------------------------------
- if isinstance(expr, (int, float, str, bool)):
- return expr
- if expr is None or expr is NIL:
- return NIL
+def _sf_defcomp(expr, env):
+ return _ref().sf_defcomp(expr[1:], env)
- # --- symbol lookup ----------------------------------------------------
- if isinstance(expr, Symbol):
- name = expr.name
- if name in env:
- return env[name]
- if name in _PRIMITIVES:
- return _PRIMITIVES[name]
- if name == "true":
- return True
- if name == "false":
- return False
- if name == "nil":
- return NIL
- raise EvalError(f"Undefined symbol: {name}")
+def _sf_defisland(expr, env):
+ return _ref().sf_defisland(expr[1:], env)
- # --- keyword → its string name ----------------------------------------
- if isinstance(expr, Keyword):
- return expr.name
+def _sf_defstyle(expr, env):
+ return _ref().sf_defstyle(expr[1:], env)
- # --- dict literal -----------------------------------------------------
- if isinstance(expr, dict):
- return {k: _trampoline(_eval(v, env)) for k, v in expr.items()}
+def _sf_defmacro(expr, env):
+ return _ref().sf_defmacro(expr[1:], env)
- # --- list = call or special form --------------------------------------
- if not isinstance(expr, list):
- return expr
+def _sf_defhandler(expr, env):
+ return _ref().sf_defhandler(expr[1:], env)
- if not expr:
- return []
+def _sf_defpage(expr, env):
+ return _ref().sf_defpage(expr[1:], env)
- head = expr[0]
+def _sf_defquery(expr, env):
+ return _ref().sf_defquery(expr[1:], env)
- # If head is not a symbol/lambda/list, treat entire list as data
- if not isinstance(head, (Symbol, Lambda, list)):
- return [_trampoline(_eval(x, env)) for x in expr]
-
- # --- special forms ----------------------------------------------------
- if isinstance(head, Symbol):
- name = head.name
- handler = _SPECIAL_FORMS.get(name)
- if handler is not None:
- return handler(expr, env)
-
- # Higher-order forms (need lazy eval of lambda arg)
- ho = _HO_FORMS.get(name)
- if ho is not None:
- return ho(expr, env)
-
- # Macro expansion — if head resolves to a Macro, expand then eval
- if name in env:
- val = env[name]
- if isinstance(val, Macro):
- expanded = _expand_macro(val, expr[1:], env)
- return _Thunk(expanded, env)
-
- # --- function / lambda call -------------------------------------------
- fn = _trampoline(_eval(head, env))
- args = [_trampoline(_eval(a, env)) for a in expr[1:]]
-
- if callable(fn) and not isinstance(fn, (Lambda, Component, Island)):
- return fn(*args)
-
- if isinstance(fn, Lambda):
- return _call_lambda(fn, args, env)
-
- if isinstance(fn, (Component, Island)):
- return _call_component(fn, expr[1:], env)
-
- raise EvalError(f"Not callable: {fn!r}")
-
-
-# ---------------------------------------------------------------------------
-# Lambda / component invocation
-# ---------------------------------------------------------------------------
-
-def _call_lambda(fn: Lambda, args: list[Any], caller_env: dict[str, Any]) -> Any:
- # Too many args is an error; too few pads with nil
- if len(args) > len(fn.params):
- raise EvalError(f"{fn!r} expects {len(fn.params)} args, got {len(args)}")
- local = dict(fn.closure)
- local.update(caller_env)
- for p, v in zip(fn.params, args):
- local[p] = v
- # Pad missing params with nil
- for p in fn.params[len(args):]:
- local[p] = None
- return _Thunk(fn.body, local)
-
-
-def _call_component(comp: Component, raw_args: list[Any], env: dict[str, Any]) -> Any:
- """Evaluate a component invocation with keyword arguments.
-
- ``(~card :title "Hello" (p "child"))``
- → comp.params gets ``title="Hello"``, comp children gets ``[(p "child")]``
- """
- kwargs: dict[str, Any] = {}
- children: list[Any] = []
- i = 0
- while i < len(raw_args):
- arg = raw_args[i]
- if isinstance(arg, Keyword) and i + 1 < len(raw_args):
- kwargs[arg.name] = _trampoline(_eval(raw_args[i + 1], env))
- i += 2
- else:
- children.append(_trampoline(_eval(arg, env)))
- i += 1
-
- local = dict(comp.closure)
- local.update(env)
- for p in comp.params:
- if p in kwargs:
- local[p] = kwargs[p]
- else:
- local[p] = NIL
- if comp.has_children:
- local["children"] = children
- return _Thunk(comp.body, local)
-
-
-# ---------------------------------------------------------------------------
-# Special forms
-# ---------------------------------------------------------------------------
-
-def _sf_if(expr: list, env: dict) -> Any:
- if len(expr) < 3:
- raise EvalError("if requires condition and then-branch")
- cond = _trampoline(_eval(expr[1], env))
- if cond and cond is not NIL:
- return _Thunk(expr[2], env)
- if len(expr) > 3:
- return _Thunk(expr[3], env)
- return NIL
-
-
-def _sf_when(expr: list, env: dict) -> Any:
- if len(expr) < 3:
- raise EvalError("when requires condition and body")
- cond = _trampoline(_eval(expr[1], env))
- if cond and cond is not NIL:
- for body_expr in expr[2:-1]:
- _trampoline(_eval(body_expr, env))
- return _Thunk(expr[-1], env)
- return NIL
-
-
-def _sf_cond(expr: list, env: dict) -> Any:
- clauses = expr[1:]
- if not clauses:
- return NIL
- # Detect scheme-style: first clause is a 2-element list that isn't a
- # comparison or predicate call (predicates end in ?)
- def _is_clojure_test(clause):
- if not isinstance(clause, list) or len(clause) != 2:
- return False
- head = clause[0]
- if not isinstance(head, Symbol):
- return False
- return (head.name in ("=", "<", ">", "<=", ">=", "!=", "and", "or")
- or head.name.endswith("?"))
- if (
- isinstance(clauses[0], list)
- and len(clauses[0]) == 2
- and not _is_clojure_test(clauses[0])
- ):
- for clause in clauses:
- if not isinstance(clause, list) or len(clause) < 2:
- raise EvalError("cond clause must be (test result)")
- test = clause[0]
- if isinstance(test, Symbol) and test.name in ("else", ":else"):
- return _Thunk(clause[1], env)
- if isinstance(test, Keyword) and test.name == "else":
- return _Thunk(clause[1], env)
- if _trampoline(_eval(test, env)):
- return _Thunk(clause[1], env)
- else:
- i = 0
- while i < len(clauses) - 1:
- test = clauses[i]
- result = clauses[i + 1]
- if isinstance(test, Keyword) and test.name == "else":
- return _Thunk(result, env)
- if isinstance(test, Symbol) and test.name in (":else", "else"):
- return _Thunk(result, env)
- if _trampoline(_eval(test, env)):
- return _Thunk(result, env)
- i += 2
- return NIL
-
-
-def _sf_case(expr: list, env: dict) -> Any:
- if len(expr) < 2:
- raise EvalError("case requires expression to match")
- match_val = _trampoline(_eval(expr[1], env))
- clauses = expr[2:]
- i = 0
- while i < len(clauses) - 1:
- test = clauses[i]
- result = clauses[i + 1]
- if isinstance(test, Keyword) and test.name == "else":
- return _Thunk(result, env)
- if isinstance(test, Symbol) and test.name in (":else", "else"):
- return _Thunk(result, env)
- if match_val == _trampoline(_eval(test, env)):
- return _Thunk(result, env)
- i += 2
- return NIL
-
-
-def _sf_and(expr: list, env: dict) -> Any:
- result: Any = True
- for arg in expr[1:]:
- result = _trampoline(_eval(arg, env))
- if not result:
- return result
- return result
-
-
-def _sf_or(expr: list, env: dict) -> Any:
- result: Any = False
- for arg in expr[1:]:
- result = _trampoline(_eval(arg, env))
- if result:
- return result
- return result
-
-
-def _sf_let(expr: list, env: dict) -> Any:
- if len(expr) < 3:
- raise EvalError("let requires bindings and body")
-
- # Named let: (let name ((x 0) ...) body)
- if isinstance(expr[1], Symbol):
- return _sf_named_let(expr, env)
-
- bindings = expr[1]
- local = dict(env)
-
- if isinstance(bindings, list):
- if bindings and isinstance(bindings[0], list):
- # Scheme-style: ((name val) ...)
- for binding in bindings:
- if len(binding) != 2:
- raise EvalError("let binding must be (name value)")
- var = binding[0]
- vname = var.name if isinstance(var, Symbol) else var
- local[vname] = _trampoline(_eval(binding[1], local))
- elif len(bindings) % 2 == 0:
- # Clojure-style: (name val name val ...)
- for i in range(0, len(bindings), 2):
- var = bindings[i]
- vname = var.name if isinstance(var, Symbol) else var
- local[vname] = _trampoline(_eval(bindings[i + 1], local))
- else:
- raise EvalError("let bindings must be (name val ...) pairs")
- else:
- raise EvalError("let bindings must be a list")
-
- # Evaluate body expressions — all but last non-tail, last is tail
- body = expr[2:]
- for body_expr in body[:-1]:
- _trampoline(_eval(body_expr, local))
- return _Thunk(body[-1], local)
-
-
-def _sf_named_let(expr: list, env: dict) -> Any:
- """``(let name ((x 0) (y 1)) body...)`` — self-recursive loop.
-
- Desugars to a lambda bound to *name* whose closure includes itself,
- called with the initial values. Tail calls to *name* produce TCO thunks.
- """
- loop_name = expr[1].name
- bindings = expr[2]
- body = expr[3:]
-
- params: list[str] = []
- inits: list[Any] = []
-
- if isinstance(bindings, list):
- if bindings and isinstance(bindings[0], list):
- for binding in bindings:
- var = binding[0]
- params.append(var.name if isinstance(var, Symbol) else var)
- inits.append(binding[1])
- elif len(bindings) % 2 == 0:
- for i in range(0, len(bindings), 2):
- var = bindings[i]
- params.append(var.name if isinstance(var, Symbol) else var)
- inits.append(bindings[i + 1])
-
- # Build loop body (wrap in begin if multiple expressions)
- loop_body = body[0] if len(body) == 1 else [Symbol("begin")] + list(body)
-
- # Create self-recursive lambda
- loop_fn = Lambda(params, loop_body, dict(env), name=loop_name)
- loop_fn.closure[loop_name] = loop_fn
-
- # Evaluate initial values in enclosing env, then call
- init_vals = [_trampoline(_eval(init, env)) for init in inits]
- return _call_lambda(loop_fn, init_vals, env)
-
-
-def _sf_letrec(expr: list, env: dict) -> Any:
- """``(letrec ((name1 val1) ...) body)`` — mutually recursive bindings.
-
- All names are bound to NIL first, then values are evaluated (so they
- can reference each other), then lambda closures are patched.
- """
- if len(expr) < 3:
- raise EvalError("letrec requires bindings and body")
- bindings = expr[1]
- local = dict(env)
-
- names: list[str] = []
- val_exprs: list[Any] = []
-
- if isinstance(bindings, list):
- if bindings and isinstance(bindings[0], list):
- for binding in bindings:
- var = binding[0]
- vname = var.name if isinstance(var, Symbol) else var
- names.append(vname)
- val_exprs.append(binding[1])
- local[vname] = NIL
- elif len(bindings) % 2 == 0:
- for i in range(0, len(bindings), 2):
- var = bindings[i]
- vname = var.name if isinstance(var, Symbol) else var
- names.append(vname)
- val_exprs.append(bindings[i + 1])
- local[vname] = NIL
-
- # Evaluate all values — they can see each other's names (initially NIL)
- values = [_trampoline(_eval(ve, local)) for ve in val_exprs]
-
- # Bind final values
- for name, val in zip(names, values):
- local[name] = val
-
- # Patch lambda closures so they see the final bindings
- for val in values:
- if isinstance(val, Lambda):
- for name in names:
- val.closure[name] = local[name]
-
- body = expr[2:]
- for body_expr in body[:-1]:
- _trampoline(_eval(body_expr, local))
- return _Thunk(body[-1], local)
-
-
-def _sf_dynamic_wind(expr: list, env: dict) -> Any:
- """``(dynamic-wind before body after)`` — entry/exit guards.
-
- All three arguments are thunks (zero-arg functions).
- *before* is called on entry, *after* is always called on exit (even on
- error). The wind stack is maintained for future continuation support.
- """
- if len(expr) != 4:
- raise EvalError("dynamic-wind requires 3 arguments (before, body, after)")
- before = _trampoline(_eval(expr[1], env))
- body_fn = _trampoline(_eval(expr[2], env))
- after = _trampoline(_eval(expr[3], env))
-
- def _call_thunk(fn: Any) -> Any:
- if isinstance(fn, Lambda):
- return _trampoline(_call_lambda(fn, [], env))
- if callable(fn):
- return fn()
- raise EvalError(f"dynamic-wind: expected thunk, got {type(fn).__name__}")
-
- # Entry
- _call_thunk(before)
- _WIND_STACK.append((before, after))
- try:
- result = _call_thunk(body_fn)
- finally:
- _WIND_STACK.pop()
- _call_thunk(after)
- return result
-
-
-# Wind stack for dynamic-wind (thread-safe enough for sync evaluator)
-_WIND_STACK: list[tuple] = []
-
-
-def _sf_lambda(expr: list, env: dict) -> Lambda:
- if len(expr) < 3:
- raise EvalError("lambda requires params and body")
- params_expr = expr[1]
- if not isinstance(params_expr, list):
- raise EvalError("lambda params must be a list")
- param_names = []
- for p in params_expr:
- if isinstance(p, Symbol):
- param_names.append(p.name)
- elif isinstance(p, str):
- param_names.append(p)
- else:
- raise EvalError(f"Invalid lambda param: {p}")
- body_exprs = expr[2:]
- body = body_exprs[0] if len(body_exprs) == 1 else [Symbol("begin")] + body_exprs
- return Lambda(param_names, body, dict(env))
-
-
-def _sf_define(expr: list, env: dict) -> Any:
- if len(expr) < 3:
- raise EvalError("define requires name and value")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"define name must be symbol, got {type(name_sym).__name__}")
- value = _trampoline(_eval(expr[2], env))
- if isinstance(value, Lambda) and value.name is None:
- value.name = name_sym.name
- env[name_sym.name] = value
- return value
-
-
-def _sf_defstyle(expr: list, env: dict) -> Any:
- """``(defstyle card-base ...)``
-
- Evaluates body and binds to name in env.
- """
- if len(expr) < 3:
- raise EvalError("defstyle requires name and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defstyle name must be symbol, got {type(name_sym).__name__}")
- value = _trampoline(_eval(expr[2], env))
- env[name_sym.name] = value
- return value
-
-
-def _sf_defcomp(expr: list, env: dict) -> Component:
- """``(defcomp ~name (&key ...) [:affinity :client|:server] body)``"""
- if len(expr) < 4:
- raise EvalError("defcomp requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defcomp name must be symbol, got {type(name_sym).__name__}")
- comp_name = name_sym.name.lstrip("~")
-
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defcomp params must be a list")
-
- params: list[str] = []
- has_children = False
- in_key = False
- for p in params_expr:
- if isinstance(p, Symbol):
- if p.name == "&key":
- in_key = True
- continue
- if p.name == "&rest":
- has_children = True
- continue
- if in_key or has_children:
- if not has_children:
- params.append(p.name)
- else:
- params.append(p.name)
- elif isinstance(p, str):
- params.append(p)
-
- # Body is always last element; keyword annotations between params and body
- body = expr[-1]
- affinity = _defcomp_kwarg(expr, "affinity", "auto")
-
- comp = Component(
- name=comp_name,
- params=params,
- has_children=has_children,
- body=body,
- closure=dict(env),
- affinity=affinity,
- )
- env[name_sym.name] = comp
- return comp
-
-
-def _sf_defisland(expr: list, env: dict) -> Island:
- """``(defisland ~name (&key ...) body)``"""
- if len(expr) < 4:
- raise EvalError("defisland requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defisland name must be symbol, got {type(name_sym).__name__}")
- comp_name = name_sym.name.lstrip("~")
-
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defisland params must be a list")
-
- params: list[str] = []
- has_children = False
- in_key = False
- for p in params_expr:
- if isinstance(p, Symbol):
- if p.name == "&key":
- in_key = True
- continue
- if p.name == "&rest":
- has_children = True
- continue
- if in_key or has_children:
- if not has_children:
- params.append(p.name)
- else:
- params.append(p.name)
- elif isinstance(p, str):
- params.append(p)
-
- body = expr[-1]
-
- island = Island(
- name=comp_name,
- params=params,
- has_children=has_children,
- body=body,
- closure=dict(env),
- )
- env[name_sym.name] = island
- return island
-
-
-def _defcomp_kwarg(expr: list, key: str, default: str) -> str:
- """Extract a keyword annotation from defcomp, e.g. :affinity :client."""
- # Scan from index 3 to second-to-last for :key value pairs
- for i in range(3, len(expr) - 1):
- item = expr[i]
- if isinstance(item, Keyword) and item.name == key:
- val = expr[i + 1]
- if isinstance(val, Keyword):
- return val.name
- return str(val)
- return default
-
-
-def _sf_begin(expr: list, env: dict) -> Any:
- if len(expr) < 2:
- return NIL
- for sub in expr[1:-1]:
- _trampoline(_eval(sub, env))
- return _Thunk(expr[-1], env)
-
-
-def _sf_quote(expr: list, _env: dict) -> Any:
- return expr[1] if len(expr) > 1 else NIL
-
-
-def _sf_thread_first(expr: list, env: dict) -> Any:
- """``(-> val (f a) (g b))`` → ``(g (f val a) b)``"""
- if len(expr) < 2:
- raise EvalError("-> requires at least a value")
- result = _trampoline(_eval(expr[1], env))
- for form in expr[2:]:
- if isinstance(form, list):
- fn = _trampoline(_eval(form[0], env))
- args = [result] + [_trampoline(_eval(a, env)) for a in form[1:]]
- else:
- fn = _trampoline(_eval(form, env))
- args = [result]
- if callable(fn) and not isinstance(fn, (Lambda, Component, Island)):
- result = fn(*args)
- elif isinstance(fn, Lambda):
- result = _trampoline(_call_lambda(fn, args, env))
- else:
- raise EvalError(f"-> form not callable: {fn!r}")
- return result
-
-
-def _sf_defmacro(expr: list, env: dict) -> Macro:
- """``(defmacro name (params... &rest rest) body)``"""
- if len(expr) < 4:
- raise EvalError("defmacro requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defmacro name must be symbol, got {type(name_sym).__name__}")
-
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defmacro params must be a list")
-
- params: list[str] = []
- rest_param: str | None = None
- i = 0
- while i < len(params_expr):
- p = params_expr[i]
- if isinstance(p, Symbol) and p.name == "&rest":
- if i + 1 < len(params_expr):
- rp = params_expr[i + 1]
- rest_param = rp.name if isinstance(rp, Symbol) else str(rp)
- break
- if isinstance(p, Symbol):
- params.append(p.name)
- elif isinstance(p, str):
- params.append(p)
- i += 1
-
- macro = Macro(
- params=params,
- rest_param=rest_param,
- body=expr[3],
- closure=dict(env),
- name=name_sym.name,
- )
- env[name_sym.name] = macro
- return macro
-
-
-def _sf_quasiquote(expr: list, env: dict) -> Any:
- """``(quasiquote template)`` — process quasiquote template."""
- if len(expr) < 2:
- raise EvalError("quasiquote requires a template")
- return _qq_expand(expr[1], env)
-
-
-def _qq_expand(template: Any, env: dict) -> Any:
- """Walk a quasiquote template, replacing unquote/splice-unquote."""
- if not isinstance(template, list):
- return template
- if not template:
- return []
- # Check for (unquote x) or (splice-unquote x)
- head = template[0]
- if isinstance(head, Symbol):
- if head.name == "unquote":
- if len(template) < 2:
- raise EvalError("unquote requires an expression")
- return _trampoline(_eval(template[1], env))
- if head.name == "splice-unquote":
- raise EvalError("splice-unquote not inside a list")
- # Walk children, handling splice-unquote
- result: list[Any] = []
- for item in template:
- if isinstance(item, list) and len(item) == 2 and isinstance(item[0], Symbol) and item[0].name == "splice-unquote":
- spliced = _trampoline(_eval(item[1], env))
- if isinstance(spliced, list):
- result.extend(spliced)
- elif spliced is not None and spliced is not NIL:
- result.append(spliced)
- else:
- result.append(_qq_expand(item, env))
- return result
-
-
-def _expand_macro(macro: Macro, raw_args: list[Any], env: dict) -> Any:
- """Expand a macro: bind unevaluated args, evaluate body to get new AST."""
- local = dict(macro.closure)
- local.update(env)
-
- # Bind positional params
- for i, param in enumerate(macro.params):
- if i < len(raw_args):
- local[param] = raw_args[i]
- else:
- local[param] = NIL
-
- # Bind &rest param
- if macro.rest_param is not None:
- rest_start = len(macro.params)
- local[macro.rest_param] = list(raw_args[rest_start:])
-
- return _trampoline(_eval(macro.body, local))
-
-
-def _sf_defhandler(expr: list, env: dict) -> HandlerDef:
- """``(defhandler name (&key param...) body)``"""
- if len(expr) < 4:
- raise EvalError("defhandler requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defhandler name must be symbol, got {type(name_sym).__name__}")
-
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defhandler params must be a list")
-
- params: list[str] = []
- in_key = False
- for p in params_expr:
- if isinstance(p, Symbol):
- if p.name == "&key":
- in_key = True
- continue
- if in_key:
- params.append(p.name)
- elif isinstance(p, str):
- params.append(p)
-
- handler = HandlerDef(
- name=name_sym.name,
- params=params,
- body=expr[3],
- closure=dict(env),
- )
- env[f"handler:{name_sym.name}"] = handler
- return handler
-
-
-def _parse_key_params(params_expr: list) -> list[str]:
- """Parse ``(&key param1 param2 ...)`` into a list of param name strings."""
- params: list[str] = []
- in_key = False
- for p in params_expr:
- if isinstance(p, Symbol):
- if p.name == "&key":
- in_key = True
- continue
- if in_key:
- params.append(p.name)
- elif isinstance(p, str):
- params.append(p)
- return params
-
-
-def _sf_defquery(expr: list, env: dict):
- """``(defquery name (&key param...) "docstring" body)``"""
- from .types import QueryDef
- if len(expr) < 4:
- raise EvalError("defquery requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defquery name must be symbol, got {type(name_sym).__name__}")
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defquery params must be a list")
- params = _parse_key_params(params_expr)
- # Optional docstring before body
- if len(expr) >= 5 and isinstance(expr[3], str):
- doc = expr[3]
- body = expr[4]
- else:
- doc = ""
- body = expr[3]
- qdef = QueryDef(
- name=name_sym.name, params=params, doc=doc,
- body=body, closure=dict(env),
- )
- env[f"query:{name_sym.name}"] = qdef
- return qdef
-
-
-def _sf_defaction(expr: list, env: dict):
- """``(defaction name (&key param...) "docstring" body)``"""
- from .types import ActionDef
- if len(expr) < 4:
- raise EvalError("defaction requires name, params, and body")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defaction name must be symbol, got {type(name_sym).__name__}")
- params_expr = expr[2]
- if not isinstance(params_expr, list):
- raise EvalError("defaction params must be a list")
- params = _parse_key_params(params_expr)
- if len(expr) >= 5 and isinstance(expr[3], str):
- doc = expr[3]
- body = expr[4]
- else:
- doc = ""
- body = expr[3]
- adef = ActionDef(
- name=name_sym.name, params=params, doc=doc,
- body=body, closure=dict(env),
- )
- env[f"action:{name_sym.name}"] = adef
- return adef
-
-
-def _sf_set_bang(expr: list, env: dict) -> Any:
- """``(set! name value)`` — mutate existing binding."""
- if len(expr) != 3:
- raise EvalError("set! requires name and value")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"set! name must be symbol, got {type(name_sym).__name__}")
- value = _trampoline(_eval(expr[2], env))
- # Walk up scope if using Env objects; for plain dicts just overwrite
- env[name_sym.name] = value
- return value
-
-
-_VALID_CARDINALITIES = {"one-to-one", "one-to-many", "many-to-many"}
-_VALID_NAV = {"submenu", "tab", "badge", "inline", "hidden"}
-
-
-def _sf_defrelation(expr: list, env: dict) -> RelationDef:
- """``(defrelation :name :from "t" :to "t" :cardinality :card ...)``"""
- if len(expr) < 2:
- raise EvalError("defrelation requires a name")
-
- name_kw = expr[1]
- if not isinstance(name_kw, Keyword):
- raise EvalError(f"defrelation name must be a keyword, got {type(name_kw).__name__}")
- rel_name = name_kw.name
-
- # Parse keyword args from remaining elements
- kwargs: dict[str, str | None] = {}
- i = 2
- while i < len(expr):
- key = expr[i]
- if isinstance(key, Keyword):
- if i + 1 < len(expr):
- val = expr[i + 1]
- if isinstance(val, Keyword):
- kwargs[key.name] = val.name
- else:
- kwargs[key.name] = _trampoline(_eval(val, env)) if not isinstance(val, str) else val
- i += 2
- else:
- kwargs[key.name] = None
- i += 1
- else:
- i += 1
-
- for field in ("from", "to", "cardinality"):
- if field not in kwargs:
- raise EvalError(f"defrelation {rel_name} missing required :{field}")
-
- card = kwargs["cardinality"]
- if card not in _VALID_CARDINALITIES:
- raise EvalError(
- f"defrelation {rel_name}: invalid cardinality {card!r}, "
- f"expected one of {_VALID_CARDINALITIES}"
- )
-
- nav = kwargs.get("nav", "hidden")
- if nav not in _VALID_NAV:
- raise EvalError(
- f"defrelation {rel_name}: invalid nav {nav!r}, "
- f"expected one of {_VALID_NAV}"
- )
-
- defn = RelationDef(
- name=rel_name,
- from_type=kwargs["from"],
- to_type=kwargs["to"],
- cardinality=card,
- inverse=kwargs.get("inverse"),
- nav=nav,
- nav_icon=kwargs.get("nav-icon"),
- nav_label=kwargs.get("nav-label"),
- )
-
- from .relations import register_relation
- register_relation(defn)
-
- env[f"relation:{rel_name}"] = defn
- return defn
-
-
-def _sf_defpage(expr: list, env: dict) -> PageDef:
- """``(defpage name :path "/..." :auth :public :content expr ...)``
-
- Parses keyword args from the expression. All slot values are stored
- as unevaluated AST — they are resolved at request time by execute_page().
- """
- if len(expr) < 2:
- raise EvalError("defpage requires a name")
- name_sym = expr[1]
- if not isinstance(name_sym, Symbol):
- raise EvalError(f"defpage name must be symbol, got {type(name_sym).__name__}")
-
- # Parse keyword args — values are NOT evaluated (stored as AST)
- slots: dict[str, Any] = {}
- i = 2
- while i < len(expr):
- key = expr[i]
- if isinstance(key, Keyword) and i + 1 < len(expr):
- slots[key.name] = expr[i + 1]
- i += 2
- else:
- i += 1
-
- # Required fields
- path = slots.get("path")
- if path is None:
- raise EvalError(f"defpage {name_sym.name} missing required :path")
- if not isinstance(path, str):
- raise EvalError(f"defpage {name_sym.name} :path must be a string")
-
- auth_val = slots.get("auth", "public")
- if isinstance(auth_val, Keyword):
- auth: str | list = auth_val.name
- elif isinstance(auth_val, list):
- # (:rights "a" "b") → ["rights", "a", "b"]
- auth = []
- for item in auth_val:
- if isinstance(item, Keyword):
- auth.append(item.name)
- elif isinstance(item, str):
- auth.append(item)
- else:
- auth.append(_trampoline(_eval(item, env)))
- else:
- auth = str(auth_val) if auth_val else "public"
-
- # Layout — keep unevaluated
- layout = slots.get("layout")
- if isinstance(layout, Keyword):
- layout = layout.name
- elif isinstance(layout, list):
- # Keep as unevaluated list for execute_page to resolve at request time
- pass
-
- # Cache — evaluate if present (it's a static config dict)
- cache_val = slots.get("cache")
- cache = None
- if cache_val is not None:
- cache_result = _trampoline(_eval(cache_val, env))
- if isinstance(cache_result, dict):
- cache = cache_result
-
- # Stream — evaluate (it's a static boolean)
- stream_val = slots.get("stream")
- stream = False
- if stream_val is not None:
- stream_result = _trampoline(_eval(stream_val, env))
- stream = bool(stream_result)
-
- page = PageDef(
- name=name_sym.name,
- path=path,
- auth=auth,
- layout=layout,
- cache=cache,
- data_expr=slots.get("data"),
- content_expr=slots.get("content"),
- filter_expr=slots.get("filter"),
- aside_expr=slots.get("aside"),
- menu_expr=slots.get("menu"),
- stream=stream,
- fallback_expr=slots.get("fallback"),
- shell_expr=slots.get("shell"),
- closure=dict(env),
- )
- env[f"page:{name_sym.name}"] = page
- return page
-
-
-# ---------------------------------------------------------------------------
-# Delimited continuations — shift / reset
-# ---------------------------------------------------------------------------
-
-_RESET_RESUME = [] # stack of resume values; empty = not resuming
-
-_RESET_SENTINEL = object()
-
-
-def _sf_reset(expr, env):
- """(reset body) — establish a continuation delimiter."""
- body = expr[1]
- try:
- return _trampoline(_eval(body, env))
- except _ShiftSignal as sig:
- def cont_fn(value=NIL):
- _RESET_RESUME.append(value)
- try:
- return _trampoline(_eval(body, env))
- finally:
- _RESET_RESUME.pop()
- k = Continuation(cont_fn)
- sig_env = dict(sig.env)
- sig_env[sig.k_name] = k
- return _trampoline(_eval(sig.body, sig_env))
-
-
-def _sf_shift(expr, env):
- """(shift k body) — capture continuation to nearest reset."""
- if _RESET_RESUME:
- return _RESET_RESUME[-1]
- k_name = expr[1].name # symbol
- body = expr[2]
- raise _ShiftSignal(k_name, body, env)
-
-
-_SPECIAL_FORMS: dict[str, Any] = {
- "if": _sf_if,
- "when": _sf_when,
- "cond": _sf_cond,
- "case": _sf_case,
- "and": _sf_and,
- "or": _sf_or,
- "let": _sf_let,
- "let*": _sf_let,
- "letrec": _sf_letrec,
- "lambda": _sf_lambda,
- "fn": _sf_lambda,
- "define": _sf_define,
- "defstyle": _sf_defstyle,
- "defcomp": _sf_defcomp,
- "defisland": _sf_defisland,
- "defrelation": _sf_defrelation,
- "begin": _sf_begin,
- "do": _sf_begin,
- "quote": _sf_quote,
- "->": _sf_thread_first,
- "set!": _sf_set_bang,
- "dynamic-wind": _sf_dynamic_wind,
- "defmacro": _sf_defmacro,
- "quasiquote": _sf_quasiquote,
- "defhandler": _sf_defhandler,
- "defpage": _sf_defpage,
- "defquery": _sf_defquery,
- "defaction": _sf_defaction,
- "reset": _sf_reset,
- "shift": _sf_shift,
-}
-
-
-# ---------------------------------------------------------------------------
-# Higher-order forms (need to evaluate the fn arg first)
-# ---------------------------------------------------------------------------
-
-def _ho_map(expr: list, env: dict) -> list:
- if len(expr) != 3:
- raise EvalError("map requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if isinstance(fn, Lambda):
- return [_trampoline(_call_lambda(fn, [item], env)) for item in coll]
- if callable(fn):
- return [fn(item) for item in coll]
- raise EvalError(f"map requires lambda, got {type(fn).__name__}")
-
-
-def _ho_map_indexed(expr: list, env: dict) -> list:
- if len(expr) != 3:
- raise EvalError("map-indexed requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"map-indexed requires lambda, got {type(fn).__name__}")
- if len(fn.params) < 2:
- raise EvalError("map-indexed lambda needs (i item) params")
- return [_trampoline(_call_lambda(fn, [i, item], env)) for i, item in enumerate(coll)]
-
-
-def _ho_filter(expr: list, env: dict) -> list:
- if len(expr) != 3:
- raise EvalError("filter requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"filter requires lambda, got {type(fn).__name__}")
- return [item for item in coll if _trampoline(_call_lambda(fn, [item], env))]
-
-
-def _ho_reduce(expr: list, env: dict) -> Any:
- if len(expr) != 4:
- raise EvalError("reduce requires fn, init, and collection")
- fn = _trampoline(_eval(expr[1], env))
- acc = _trampoline(_eval(expr[2], env))
- coll = _trampoline(_eval(expr[3], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"reduce requires lambda, got {type(fn).__name__}")
- for item in coll:
- acc = _trampoline(_call_lambda(fn, [acc, item], env))
- return acc
-
-
-def _ho_some(expr: list, env: dict) -> Any:
- if len(expr) != 3:
- raise EvalError("some requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"some requires lambda, got {type(fn).__name__}")
- for item in coll:
- result = _trampoline(_call_lambda(fn, [item], env))
- if result:
- return result
- return NIL
-
-
-def _ho_every(expr: list, env: dict) -> bool:
- if len(expr) != 3:
- raise EvalError("every? requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"every? requires lambda, got {type(fn).__name__}")
- for item in coll:
- if not _trampoline(_call_lambda(fn, [item], env)):
- return False
- return True
-
-
-def _ho_for_each(expr: list, env: dict) -> Any:
- if len(expr) != 3:
- raise EvalError("for-each requires fn and collection")
- fn = _trampoline(_eval(expr[1], env))
- coll = _trampoline(_eval(expr[2], env))
- if not isinstance(fn, Lambda):
- raise EvalError(f"for-each requires lambda, got {type(fn).__name__}")
- for item in coll:
- _trampoline(_call_lambda(fn, [item], env))
- return NIL
-
-
-_HO_FORMS: dict[str, Any] = {
- "map": _ho_map,
- "map-indexed": _ho_map_indexed,
- "filter": _ho_filter,
- "reduce": _ho_reduce,
- "some": _ho_some,
- "every?": _ho_every,
- "for-each": _ho_for_each,
-}
+def _sf_defaction(expr, env):
+ return _ref().sf_defaction(expr[1:], env)
diff --git a/shared/sx/ref/bootstrap_py.py b/shared/sx/ref/bootstrap_py.py
index 31299b1..20c2383 100644
--- a/shared/sx/ref/bootstrap_py.py
+++ b/shared/sx/ref/bootstrap_py.py
@@ -635,11 +635,9 @@ class PyEmitter:
pad = " " * indent
name = expr[1].name if isinstance(expr[1], Symbol) else str(expr[1])
val_expr = expr[2]
- # If value is a lambda/fn, check if body uses set! on let-bound vars
- # and emit as def for proper mutation support
+ # Always emit fn-bodied defines as def statements for flat control flow
if (isinstance(val_expr, list) and val_expr and
- isinstance(val_expr[0], Symbol) and val_expr[0].name in ("fn", "lambda")
- and self._body_uses_set(val_expr)):
+ isinstance(val_expr[0], Symbol) and val_expr[0].name in ("fn", "lambda")):
return self._emit_define_as_def(name, val_expr, indent)
val = self.emit(val_expr)
return f"{pad}{self._mangle(name)} = {val}"
@@ -667,11 +665,23 @@ class PyEmitter:
params = fn_expr[1]
body = fn_expr[2:]
param_names = []
- for p in params:
+ i = 0
+ while i < len(params):
+ p = params[i]
+ if isinstance(p, Symbol) and p.name == "&rest":
+ if i + 1 < len(params):
+ rest_name = self._mangle(params[i + 1].name if isinstance(params[i + 1], Symbol) else str(params[i + 1]))
+ param_names.append(f"*{rest_name}")
+ i += 2
+ continue
+ else:
+ i += 1
+ continue
if isinstance(p, Symbol):
param_names.append(self._mangle(p.name))
else:
param_names.append(str(p))
+ i += 1
params_str = ", ".join(param_names)
py_name = self._mangle(name)
# Find set! target variables that are used from nested lambda scopes
@@ -718,7 +728,8 @@ class PyEmitter:
"""Emit body expressions as statements into lines list.
Handles let as local variable declarations, and returns the last
- expression.
+ expression. Control flow in tail position (if, cond, case, when)
+ is flattened to if/elif statements with returns in each branch.
"""
pad = " " * indent
for i, expr in enumerate(body):
@@ -737,10 +748,126 @@ class PyEmitter:
lines.append(self.emit_statement(sub, indent))
continue
if is_last:
- lines.append(f"{pad}return {self.emit(expr)}")
+ self._emit_return_expr(expr, lines, indent)
else:
lines.append(self.emit_statement(expr, indent))
+ def _emit_return_expr(self, expr, lines: list, indent: int) -> None:
+ """Emit an expression in return position, flattening control flow."""
+ pad = " " * indent
+ if isinstance(expr, list) and expr and isinstance(expr[0], Symbol):
+ name = expr[0].name
+ if name == "if":
+ self._emit_if_return(expr, lines, indent)
+ return
+ if name == "cond":
+ self._emit_cond_return(expr, lines, indent)
+ return
+ if name == "case":
+ self._emit_case_return(expr, lines, indent)
+ return
+ if name == "when":
+ self._emit_when_return(expr, lines, indent)
+ return
+ if name in ("let", "let*"):
+ self._emit_let_as_stmts(expr, lines, indent, True)
+ return
+ if name in ("do", "begin"):
+ self._emit_body_stmts(expr[1:], lines, indent)
+ return
+ lines.append(f"{pad}return {self.emit(expr)}")
+
+ def _emit_if_return(self, expr, lines: list, indent: int) -> None:
+ """Emit if as statement with returns in each branch."""
+ pad = " " * indent
+ lines.append(f"{pad}if sx_truthy({self.emit(expr[1])}):")
+ self._emit_return_expr(expr[2], lines, indent + 1)
+ if len(expr) > 3:
+ lines.append(f"{pad}else:")
+ self._emit_return_expr(expr[3], lines, indent + 1)
+ else:
+ lines.append(f"{pad}return NIL")
+
+ def _emit_when_return(self, expr, lines: list, indent: int) -> None:
+ """Emit when as statement with return in body, else return NIL."""
+ pad = " " * indent
+ lines.append(f"{pad}if sx_truthy({self.emit(expr[1])}):")
+ body_parts = expr[2:]
+ if len(body_parts) == 1:
+ self._emit_return_expr(body_parts[0], lines, indent + 1)
+ else:
+ for b in body_parts[:-1]:
+ lines.append(self.emit_statement(b, indent + 1))
+ self._emit_return_expr(body_parts[-1], lines, indent + 1)
+ lines.append(f"{pad}return NIL")
+
+ def _emit_cond_return(self, expr, lines: list, indent: int) -> None:
+ """Emit cond as if/elif/else with returns in each branch."""
+ pad = " " * indent
+ clauses = expr[1:]
+ if not clauses:
+ lines.append(f"{pad}return NIL")
+ return
+ is_scheme = (
+ all(isinstance(c, list) and len(c) == 2 for c in clauses)
+ and not any(isinstance(c, Keyword) for c in clauses)
+ )
+ has_else = False
+ first_clause = True
+ if is_scheme:
+ for clause in clauses:
+ test, body = clause[0], clause[1]
+ if ((isinstance(test, Symbol) and test.name in ("else", ":else")) or
+ (isinstance(test, Keyword) and test.name == "else")):
+ lines.append(f"{pad}else:")
+ has_else = True
+ else:
+ kw = "if" if first_clause else "elif"
+ lines.append(f"{pad}{kw} sx_truthy({self.emit(test)}):")
+ first_clause = False
+ self._emit_return_expr(body, lines, indent + 1)
+ else:
+ i = 0
+ while i < len(clauses) - 1:
+ test, body = clauses[i], clauses[i + 1]
+ if ((isinstance(test, Keyword) and test.name == "else") or
+ (isinstance(test, Symbol) and test.name in ("else", ":else"))):
+ lines.append(f"{pad}else:")
+ has_else = True
+ else:
+ kw = "if" if first_clause else "elif"
+ lines.append(f"{pad}{kw} sx_truthy({self.emit(test)}):")
+ first_clause = False
+ self._emit_return_expr(body, lines, indent + 1)
+ i += 2
+ if not has_else:
+ lines.append(f"{pad}return NIL")
+
+ def _emit_case_return(self, expr, lines: list, indent: int) -> None:
+ """Emit case as if/elif/else with returns in each branch."""
+ pad = " " * indent
+ match_val = self.emit(expr[1])
+ clauses = expr[2:]
+ lines.append(f"{pad}_match = {match_val}")
+ has_else = False
+ first_clause = True
+ i = 0
+ while i < len(clauses) - 1:
+ test = clauses[i]
+ body = clauses[i + 1]
+ if ((isinstance(test, Keyword) and test.name == "else") or
+ (isinstance(test, Symbol) and test.name in ("else", ":else"))):
+ lines.append(f"{pad}else:")
+ has_else = True
+ else:
+ kw = "if" if first_clause else "elif"
+ lines.append(f"{pad}{kw} _match == {self.emit(test)}:")
+ first_clause = False
+ self._emit_return_expr(body, lines, indent + 1)
+ i += 2
+ if not has_else:
+ lines.append(f"{pad}return NIL")
+
def _emit_let_as_stmts(self, expr, lines: list, indent: int, is_last: bool) -> None:
"""Emit a let expression as local variable declarations."""
pad = " " * indent
diff --git a/shared/sx/ref/platform_py.py b/shared/sx/ref/platform_py.py
index fcbb7ba..0d08b78 100644
--- a/shared/sx/ref/platform_py.py
+++ b/shared/sx/ref/platform_py.py
@@ -578,8 +578,11 @@ def sx_expr_source(x):
return x.source if isinstance(x, SxExpr) else str(x)
-class EvalError(Exception):
- pass
+try:
+ from shared.sx.evaluator import EvalError
+except ImportError:
+ class EvalError(Exception):
+ pass
def _sx_append(lst, item):
diff --git a/shared/sx/ref/sx_ref.py b/shared/sx/ref/sx_ref.py
index f5e59e7..072a625 100644
--- a/shared/sx/ref/sx_ref.py
+++ b/shared/sx/ref/sx_ref.py
@@ -1,5 +1,3 @@
-# WARNING: special-forms.sx declares forms not in eval.sx: reset, shift
-# WARNING: eval.sx dispatches forms not in special-forms.sx: form?
"""
sx_ref.py -- Generated from reference SX evaluator specification.
@@ -539,8 +537,11 @@ def sx_expr_source(x):
return x.source if isinstance(x, SxExpr) else str(x)
-class EvalError(Exception):
- pass
+try:
+ from shared.sx.evaluator import EvalError
+except ImportError:
+ class EvalError(Exception):
+ pass
def _sx_append(lst, item):
@@ -947,70 +948,355 @@ def component_set_io_refs(c, refs):
# === Transpiled from eval ===
# trampoline
-trampoline = lambda val: (lambda result: (trampoline(eval_expr(thunk_expr(result), thunk_env(result))) if sx_truthy(is_thunk(result)) else result))(val)
+def trampoline(val):
+ result = val
+ if sx_truthy(is_thunk(result)):
+ return trampoline(eval_expr(thunk_expr(result), thunk_env(result)))
+ else:
+ return result
# eval-expr
-eval_expr = lambda expr, env: _sx_case(type_of(expr), [('number', lambda: expr), ('string', lambda: expr), ('boolean', lambda: expr), ('nil', lambda: NIL), ('symbol', lambda: (lambda name: (env_get(env, name) if sx_truthy(env_has(env, name)) else (get_primitive(name) if sx_truthy(is_primitive(name)) else (True if sx_truthy((name == 'true')) else (False if sx_truthy((name == 'false')) else (NIL if sx_truthy((name == 'nil')) else error(sx_str('Undefined symbol: ', name))))))))(symbol_name(expr))), ('keyword', lambda: keyword_name(expr)), ('dict', lambda: map_dict(lambda k, v: trampoline(eval_expr(v, env)), expr)), ('list', lambda: ([] if sx_truthy(empty_p(expr)) else eval_list(expr, env))), (None, lambda: expr)])
+def eval_expr(expr, env):
+ _match = type_of(expr)
+ if _match == 'number':
+ return expr
+ elif _match == 'string':
+ return expr
+ elif _match == 'boolean':
+ return expr
+ elif _match == 'nil':
+ return NIL
+ elif _match == 'symbol':
+ name = symbol_name(expr)
+ if sx_truthy(env_has(env, name)):
+ return env_get(env, name)
+ elif sx_truthy(is_primitive(name)):
+ return get_primitive(name)
+ elif sx_truthy((name == 'true')):
+ return True
+ elif sx_truthy((name == 'false')):
+ return False
+ elif sx_truthy((name == 'nil')):
+ return NIL
+ else:
+ return error(sx_str('Undefined symbol: ', name))
+ elif _match == 'keyword':
+ return keyword_name(expr)
+ elif _match == 'dict':
+ return map_dict(lambda k, v: trampoline(eval_expr(v, env)), expr)
+ elif _match == 'list':
+ if sx_truthy(empty_p(expr)):
+ return []
+ else:
+ return eval_list(expr, env)
+ else:
+ return expr
# eval-list
-eval_list = lambda expr, env: (lambda head: (lambda args: (map(lambda x: trampoline(eval_expr(x, env)), expr) if sx_truthy((not sx_truthy(((type_of(head) == 'symbol') if sx_truthy((type_of(head) == 'symbol')) else ((type_of(head) == 'lambda') if sx_truthy((type_of(head) == 'lambda')) else (type_of(head) == 'list')))))) else ((lambda name: (sf_if(args, env) if sx_truthy((name == 'if')) else (sf_when(args, env) if sx_truthy((name == 'when')) else (sf_cond(args, env) if sx_truthy((name == 'cond')) else (sf_case(args, env) if sx_truthy((name == 'case')) else (sf_and(args, env) if sx_truthy((name == 'and')) else (sf_or(args, env) if sx_truthy((name == 'or')) else (sf_let(args, env) if sx_truthy((name == 'let')) else (sf_let(args, env) if sx_truthy((name == 'let*')) else (sf_letrec(args, env) if sx_truthy((name == 'letrec')) else (sf_lambda(args, env) if sx_truthy((name == 'lambda')) else (sf_lambda(args, env) if sx_truthy((name == 'fn')) else (sf_define(args, env) if sx_truthy((name == 'define')) else (sf_defcomp(args, env) if sx_truthy((name == 'defcomp')) else (sf_defisland(args, env) if sx_truthy((name == 'defisland')) else (sf_defmacro(args, env) if sx_truthy((name == 'defmacro')) else (sf_defstyle(args, env) if sx_truthy((name == 'defstyle')) else (sf_defhandler(args, env) if sx_truthy((name == 'defhandler')) else (sf_defpage(args, env) if sx_truthy((name == 'defpage')) else (sf_defquery(args, env) if sx_truthy((name == 'defquery')) else (sf_defaction(args, env) if sx_truthy((name == 'defaction')) else (sf_begin(args, env) if sx_truthy((name == 'begin')) else (sf_begin(args, env) if sx_truthy((name == 'do')) else (sf_quote(args, env) if sx_truthy((name == 'quote')) else (sf_quasiquote(args, env) if sx_truthy((name == 'quasiquote')) else (sf_thread_first(args, env) if sx_truthy((name == '->')) else (sf_set_bang(args, env) if sx_truthy((name == 'set!')) else (sf_reset(args, env) if sx_truthy((name == 'reset')) else (sf_shift(args, env) if sx_truthy((name == 'shift')) else (sf_dynamic_wind(args, env) if sx_truthy((name == 'dynamic-wind')) else (ho_map(args, env) if sx_truthy((name == 'map')) else (ho_map_indexed(args, env) if sx_truthy((name == 'map-indexed')) else (ho_filter(args, env) if sx_truthy((name == 'filter')) else (ho_reduce(args, env) if sx_truthy((name == 'reduce')) else (ho_some(args, env) if sx_truthy((name == 'some')) else (ho_every(args, env) if sx_truthy((name == 'every?')) else (ho_for_each(args, env) if sx_truthy((name == 'for-each')) else ((lambda mac: make_thunk(expand_macro(mac, args, env), env))(env_get(env, name)) if sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))) else (render_expr(expr, env) if sx_truthy((render_active_p() if not sx_truthy(render_active_p()) else is_render_expr(expr))) else eval_call(head, args, env))))))))))))))))))))))))))))))))))))))))(symbol_name(head)) if sx_truthy((type_of(head) == 'symbol')) else eval_call(head, args, env))))(rest(expr)))(first(expr))
+def eval_list(expr, env):
+ head = first(expr)
+ args = rest(expr)
+ if sx_truthy((not sx_truthy(((type_of(head) == 'symbol') if sx_truthy((type_of(head) == 'symbol')) else ((type_of(head) == 'lambda') if sx_truthy((type_of(head) == 'lambda')) else (type_of(head) == 'list')))))):
+ return map(lambda x: trampoline(eval_expr(x, env)), expr)
+ else:
+ if sx_truthy((type_of(head) == 'symbol')):
+ name = symbol_name(head)
+ if sx_truthy((name == 'if')):
+ return sf_if(args, env)
+ elif sx_truthy((name == 'when')):
+ return sf_when(args, env)
+ elif sx_truthy((name == 'cond')):
+ return sf_cond(args, env)
+ elif sx_truthy((name == 'case')):
+ return sf_case(args, env)
+ elif sx_truthy((name == 'and')):
+ return sf_and(args, env)
+ elif sx_truthy((name == 'or')):
+ return sf_or(args, env)
+ elif sx_truthy((name == 'let')):
+ return sf_let(args, env)
+ elif sx_truthy((name == 'let*')):
+ return sf_let(args, env)
+ elif sx_truthy((name == 'letrec')):
+ return sf_letrec(args, env)
+ elif sx_truthy((name == 'lambda')):
+ return sf_lambda(args, env)
+ elif sx_truthy((name == 'fn')):
+ return sf_lambda(args, env)
+ elif sx_truthy((name == 'define')):
+ return sf_define(args, env)
+ elif sx_truthy((name == 'defcomp')):
+ return sf_defcomp(args, env)
+ elif sx_truthy((name == 'defisland')):
+ return sf_defisland(args, env)
+ elif sx_truthy((name == 'defmacro')):
+ return sf_defmacro(args, env)
+ elif sx_truthy((name == 'defstyle')):
+ return sf_defstyle(args, env)
+ elif sx_truthy((name == 'defhandler')):
+ return sf_defhandler(args, env)
+ elif sx_truthy((name == 'defpage')):
+ return sf_defpage(args, env)
+ elif sx_truthy((name == 'defquery')):
+ return sf_defquery(args, env)
+ elif sx_truthy((name == 'defaction')):
+ return sf_defaction(args, env)
+ elif sx_truthy((name == 'begin')):
+ return sf_begin(args, env)
+ elif sx_truthy((name == 'do')):
+ return sf_begin(args, env)
+ elif sx_truthy((name == 'quote')):
+ return sf_quote(args, env)
+ elif sx_truthy((name == 'quasiquote')):
+ return sf_quasiquote(args, env)
+ elif sx_truthy((name == '->')):
+ return sf_thread_first(args, env)
+ elif sx_truthy((name == 'set!')):
+ return sf_set_bang(args, env)
+ elif sx_truthy((name == 'reset')):
+ return sf_reset(args, env)
+ elif sx_truthy((name == 'shift')):
+ return sf_shift(args, env)
+ elif sx_truthy((name == 'dynamic-wind')):
+ return sf_dynamic_wind(args, env)
+ elif sx_truthy((name == 'map')):
+ return ho_map(args, env)
+ elif sx_truthy((name == 'map-indexed')):
+ return ho_map_indexed(args, env)
+ elif sx_truthy((name == 'filter')):
+ return ho_filter(args, env)
+ elif sx_truthy((name == 'reduce')):
+ return ho_reduce(args, env)
+ elif sx_truthy((name == 'some')):
+ return ho_some(args, env)
+ elif sx_truthy((name == 'every?')):
+ return ho_every(args, env)
+ elif sx_truthy((name == 'for-each')):
+ return ho_for_each(args, env)
+ elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
+ mac = env_get(env, name)
+ return make_thunk(expand_macro(mac, args, env), env)
+ elif sx_truthy((render_active_p() if not sx_truthy(render_active_p()) else is_render_expr(expr))):
+ return render_expr(expr, env)
+ else:
+ return eval_call(head, args, env)
+ else:
+ return eval_call(head, args, env)
# eval-call
-eval_call = lambda head, args, env: (lambda f: (lambda evaluated_args: (apply(f, evaluated_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else ((not sx_truthy(is_component(f))) if not sx_truthy((not sx_truthy(is_component(f)))) else (not sx_truthy(is_island(f))))))) else (call_lambda(f, evaluated_args, env) if sx_truthy(is_lambda(f)) else (call_component(f, args, env) if sx_truthy(is_component(f)) else (call_component(f, args, env) if sx_truthy(is_island(f)) else error(sx_str('Not callable: ', inspect(f))))))))(map(lambda a: trampoline(eval_expr(a, env)), args)))(trampoline(eval_expr(head, env)))
+def eval_call(head, args, env):
+ f = trampoline(eval_expr(head, env))
+ evaluated_args = map(lambda a: trampoline(eval_expr(a, env)), args)
+ if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else ((not sx_truthy(is_component(f))) if not sx_truthy((not sx_truthy(is_component(f)))) else (not sx_truthy(is_island(f))))))):
+ return apply(f, evaluated_args)
+ elif sx_truthy(is_lambda(f)):
+ return call_lambda(f, evaluated_args, env)
+ elif sx_truthy(is_component(f)):
+ return call_component(f, args, env)
+ elif sx_truthy(is_island(f)):
+ return call_component(f, args, env)
+ else:
+ return error(sx_str('Not callable: ', inspect(f)))
# call-lambda
-call_lambda = lambda f, args, caller_env: (lambda params: (lambda local: (error(sx_str((lambda_name(f) if sx_truthy(lambda_name(f)) else 'lambda'), ' expects ', len(params), ' args, got ', len(args))) if sx_truthy((len(args) > len(params))) else _sx_begin(for_each(lambda pair: _sx_dict_set(local, first(pair), nth(pair, 1)), zip(params, args)), for_each(lambda p: _sx_dict_set(local, p, NIL), slice(params, len(args))), make_thunk(lambda_body(f), local))))(env_merge(lambda_closure(f), caller_env)))(lambda_params(f))
+def call_lambda(f, args, caller_env):
+ params = lambda_params(f)
+ local = env_merge(lambda_closure(f), caller_env)
+ if sx_truthy((len(args) > len(params))):
+ return error(sx_str((lambda_name(f) if sx_truthy(lambda_name(f)) else 'lambda'), ' expects ', len(params), ' args, got ', len(args)))
+ else:
+ for pair in zip(params, args):
+ local[first(pair)] = nth(pair, 1)
+ for p in slice(params, len(args)):
+ local[p] = NIL
+ return make_thunk(lambda_body(f), local)
# call-component
-call_component = lambda comp, raw_args, env: (lambda parsed: (lambda kwargs: (lambda children: (lambda local: _sx_begin(for_each(lambda p: _sx_dict_set(local, p, (dict_get(kwargs, p) if sx_truthy(dict_get(kwargs, p)) else NIL)), component_params(comp)), (_sx_dict_set(local, 'children', children) if sx_truthy(component_has_children(comp)) else NIL), make_thunk(component_body(comp), local)))(env_merge(component_closure(comp), env)))(nth(parsed, 1)))(first(parsed)))(parse_keyword_args(raw_args, env))
+def call_component(comp, raw_args, env):
+ parsed = parse_keyword_args(raw_args, env)
+ kwargs = first(parsed)
+ children = nth(parsed, 1)
+ local = env_merge(component_closure(comp), env)
+ for p in component_params(comp):
+ local[p] = (dict_get(kwargs, p) if sx_truthy(dict_get(kwargs, p)) else NIL)
+ if sx_truthy(component_has_children(comp)):
+ local['children'] = children
+ return make_thunk(component_body(comp), local)
# parse-keyword-args
-parse_keyword_args = lambda raw_args, env: (lambda kwargs: (lambda children: (lambda i: _sx_begin(reduce(lambda state, arg: (lambda idx: (lambda skip: (assoc(state, 'skip', False, 'i', (idx + 1)) if sx_truthy(skip) else (_sx_begin(_sx_dict_set(kwargs, keyword_name(arg), trampoline(eval_expr(nth(raw_args, (idx + 1)), env))), assoc(state, 'skip', True, 'i', (idx + 1))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((idx + 1) < len(raw_args)))) else _sx_begin(_sx_append(children, trampoline(eval_expr(arg, env))), assoc(state, 'i', (idx + 1))))))(get(state, 'skip')))(get(state, 'i')), {'i': 0, 'skip': False}, raw_args), [kwargs, children]))(0))([]))({})
+def parse_keyword_args(raw_args, env):
+ kwargs = {}
+ children = []
+ i = 0
+ reduce(lambda state, arg: (lambda idx: (lambda skip: (assoc(state, 'skip', False, 'i', (idx + 1)) if sx_truthy(skip) else (_sx_begin(_sx_dict_set(kwargs, keyword_name(arg), trampoline(eval_expr(nth(raw_args, (idx + 1)), env))), assoc(state, 'skip', True, 'i', (idx + 1))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((idx + 1) < len(raw_args)))) else _sx_begin(_sx_append(children, trampoline(eval_expr(arg, env))), assoc(state, 'i', (idx + 1))))))(get(state, 'skip')))(get(state, 'i')), {'i': 0, 'skip': False}, raw_args)
+ return [kwargs, children]
# sf-if
-sf_if = lambda args, env: (lambda condition: (make_thunk(nth(args, 1), env) if sx_truthy((condition if not sx_truthy(condition) else (not sx_truthy(is_nil(condition))))) else (make_thunk(nth(args, 2), env) if sx_truthy((len(args) > 2)) else NIL)))(trampoline(eval_expr(first(args), env)))
+def sf_if(args, env):
+ condition = trampoline(eval_expr(first(args), env))
+ if sx_truthy((condition if not sx_truthy(condition) else (not sx_truthy(is_nil(condition))))):
+ return make_thunk(nth(args, 1), env)
+ else:
+ if sx_truthy((len(args) > 2)):
+ return make_thunk(nth(args, 2), env)
+ else:
+ return NIL
# sf-when
-sf_when = lambda args, env: (lambda condition: (_sx_begin(for_each(lambda e: trampoline(eval_expr(e, env)), slice(args, 1, (len(args) - 1))), make_thunk(last(args), env)) if sx_truthy((condition if not sx_truthy(condition) else (not sx_truthy(is_nil(condition))))) else NIL))(trampoline(eval_expr(first(args), env)))
+def sf_when(args, env):
+ condition = trampoline(eval_expr(first(args), env))
+ if sx_truthy((condition if not sx_truthy(condition) else (not sx_truthy(is_nil(condition))))):
+ for e in slice(args, 1, (len(args) - 1)):
+ trampoline(eval_expr(e, env))
+ return make_thunk(last(args), env)
+ else:
+ return NIL
# sf-cond
-sf_cond = lambda args, env: (sf_cond_scheme(args, env) if sx_truthy(((type_of(first(args)) == 'list') if not sx_truthy((type_of(first(args)) == 'list')) else (len(first(args)) == 2))) else sf_cond_clojure(args, env))
+def sf_cond(args, env):
+ if sx_truthy(((type_of(first(args)) == 'list') if not sx_truthy((type_of(first(args)) == 'list')) else (len(first(args)) == 2))):
+ return sf_cond_scheme(args, env)
+ else:
+ return sf_cond_clojure(args, env)
# sf-cond-scheme
-sf_cond_scheme = lambda clauses, env: (NIL if sx_truthy(empty_p(clauses)) else (lambda clause: (lambda test: (lambda body: (make_thunk(body, env) if sx_truthy((((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))) if sx_truthy(((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else')))) else ((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')))) else (make_thunk(body, env) if sx_truthy(trampoline(eval_expr(test, env))) else sf_cond_scheme(rest(clauses), env))))(nth(clause, 1)))(first(clause)))(first(clauses)))
+def sf_cond_scheme(clauses, env):
+ if sx_truthy(empty_p(clauses)):
+ return NIL
+ else:
+ clause = first(clauses)
+ test = first(clause)
+ body = nth(clause, 1)
+ if sx_truthy((((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))) if sx_truthy(((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else')))) else ((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')))):
+ return make_thunk(body, env)
+ else:
+ if sx_truthy(trampoline(eval_expr(test, env))):
+ return make_thunk(body, env)
+ else:
+ return sf_cond_scheme(rest(clauses), env)
# sf-cond-clojure
-sf_cond_clojure = lambda clauses, env: (NIL if sx_truthy((len(clauses) < 2)) else (lambda test: (lambda body: (make_thunk(body, env) if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))) else (make_thunk(body, env) if sx_truthy(trampoline(eval_expr(test, env))) else sf_cond_clojure(slice(clauses, 2), env))))(nth(clauses, 1)))(first(clauses)))
+def sf_cond_clojure(clauses, env):
+ if sx_truthy((len(clauses) < 2)):
+ return NIL
+ else:
+ test = first(clauses)
+ body = nth(clauses, 1)
+ if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))):
+ return make_thunk(body, env)
+ else:
+ if sx_truthy(trampoline(eval_expr(test, env))):
+ return make_thunk(body, env)
+ else:
+ return sf_cond_clojure(slice(clauses, 2), env)
# sf-case
-sf_case = lambda args, env: (lambda match_val: (lambda clauses: sf_case_loop(match_val, clauses, env))(rest(args)))(trampoline(eval_expr(first(args), env)))
+def sf_case(args, env):
+ match_val = trampoline(eval_expr(first(args), env))
+ clauses = rest(args)
+ return sf_case_loop(match_val, clauses, env)
# sf-case-loop
-sf_case_loop = lambda match_val, clauses, env: (NIL if sx_truthy((len(clauses) < 2)) else (lambda test: (lambda body: (make_thunk(body, env) if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))) else (make_thunk(body, env) if sx_truthy((match_val == trampoline(eval_expr(test, env)))) else sf_case_loop(match_val, slice(clauses, 2), env))))(nth(clauses, 1)))(first(clauses)))
+def sf_case_loop(match_val, clauses, env):
+ if sx_truthy((len(clauses) < 2)):
+ return NIL
+ else:
+ test = first(clauses)
+ body = nth(clauses, 1)
+ if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))):
+ return make_thunk(body, env)
+ else:
+ if sx_truthy((match_val == trampoline(eval_expr(test, env)))):
+ return make_thunk(body, env)
+ else:
+ return sf_case_loop(match_val, slice(clauses, 2), env)
# sf-and
-sf_and = lambda args, env: (True if sx_truthy(empty_p(args)) else (lambda val: (val if sx_truthy((not sx_truthy(val))) else (val if sx_truthy((len(args) == 1)) else sf_and(rest(args), env))))(trampoline(eval_expr(first(args), env))))
+def sf_and(args, env):
+ if sx_truthy(empty_p(args)):
+ return True
+ else:
+ val = trampoline(eval_expr(first(args), env))
+ if sx_truthy((not sx_truthy(val))):
+ return val
+ else:
+ if sx_truthy((len(args) == 1)):
+ return val
+ else:
+ return sf_and(rest(args), env)
# sf-or
-sf_or = lambda args, env: (False if sx_truthy(empty_p(args)) else (lambda val: (val if sx_truthy(val) else sf_or(rest(args), env)))(trampoline(eval_expr(first(args), env))))
+def sf_or(args, env):
+ if sx_truthy(empty_p(args)):
+ return False
+ else:
+ val = trampoline(eval_expr(first(args), env))
+ if sx_truthy(val):
+ return val
+ else:
+ return sf_or(rest(args), env)
# sf-let
-sf_let = lambda args, env: (sf_named_let(args, env) if sx_truthy((type_of(first(args)) == 'symbol')) else (lambda bindings: (lambda body: (lambda local: _sx_begin((for_each(lambda binding: (lambda vname: _sx_dict_set(local, vname, trampoline(eval_expr(nth(binding, 1), local))))((symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else (lambda i: reduce(lambda acc, pair_idx: (lambda vname: (lambda val_expr: _sx_dict_set(local, vname, trampoline(eval_expr(val_expr, local))))(nth(bindings, ((pair_idx * 2) + 1))))((symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), NIL, range(0, (len(bindings) / 2))))(0)), for_each(lambda e: trampoline(eval_expr(e, local)), slice(body, 0, (len(body) - 1))), make_thunk(last(body), local)))(env_extend(env)))(rest(args)))(first(args)))
+def sf_let(args, env):
+ if sx_truthy((type_of(first(args)) == 'symbol')):
+ return sf_named_let(args, env)
+ else:
+ bindings = first(args)
+ body = rest(args)
+ local = env_extend(env)
+ (for_each(lambda binding: (lambda vname: _sx_dict_set(local, vname, trampoline(eval_expr(nth(binding, 1), local))))((symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else (lambda i: reduce(lambda acc, pair_idx: (lambda vname: (lambda val_expr: _sx_dict_set(local, vname, trampoline(eval_expr(val_expr, local))))(nth(bindings, ((pair_idx * 2) + 1))))((symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), NIL, range(0, (len(bindings) / 2))))(0))
+ for e in slice(body, 0, (len(body) - 1)):
+ trampoline(eval_expr(e, local))
+ return make_thunk(last(body), local)
# sf-named-let
-sf_named_let = lambda args, env: (lambda loop_name: (lambda bindings: (lambda body: (lambda params: (lambda inits: _sx_begin((for_each(_sx_fn(lambda binding: (
+def sf_named_let(args, env):
+ loop_name = symbol_name(first(args))
+ bindings = nth(args, 1)
+ body = slice(args, 2)
+ params = []
+ inits = []
+ (for_each(_sx_fn(lambda binding: (
_sx_append(params, (symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))),
_sx_append(inits, nth(binding, 1))
-)[-1]), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else reduce(lambda acc, pair_idx: _sx_begin(_sx_append(params, (symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), _sx_append(inits, nth(bindings, ((pair_idx * 2) + 1)))), NIL, range(0, (len(bindings) / 2)))), (lambda loop_body: (lambda loop_fn: _sx_begin(_sx_set_attr(loop_fn, 'name', loop_name), _sx_dict_set(lambda_closure(loop_fn), loop_name, loop_fn), (lambda init_vals: call_lambda(loop_fn, init_vals, env))(map(lambda e: trampoline(eval_expr(e, env)), inits))))(make_lambda(params, loop_body, env)))((first(body) if sx_truthy((len(body) == 1)) else cons(make_symbol('begin'), body)))))([]))([]))(slice(args, 2)))(nth(args, 1)))(symbol_name(first(args)))
+)[-1]), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else reduce(lambda acc, pair_idx: _sx_begin(_sx_append(params, (symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), _sx_append(inits, nth(bindings, ((pair_idx * 2) + 1)))), NIL, range(0, (len(bindings) / 2))))
+ loop_body = (first(body) if sx_truthy((len(body) == 1)) else cons(make_symbol('begin'), body))
+ loop_fn = make_lambda(params, loop_body, env)
+ loop_fn.name = loop_name
+ lambda_closure(loop_fn)[loop_name] = loop_fn
+ init_vals = map(lambda e: trampoline(eval_expr(e, env)), inits)
+ return call_lambda(loop_fn, init_vals, env)
# sf-lambda
-sf_lambda = lambda args, env: (lambda params_expr: (lambda body_exprs: (lambda body: (lambda param_names: make_lambda(param_names, body, env))(map(lambda p: (symbol_name(p) if sx_truthy((type_of(p) == 'symbol')) else p), params_expr)))((first(body_exprs) if sx_truthy((len(body_exprs) == 1)) else cons(make_symbol('begin'), body_exprs))))(rest(args)))(first(args))
+def sf_lambda(args, env):
+ params_expr = first(args)
+ body_exprs = rest(args)
+ body = (first(body_exprs) if sx_truthy((len(body_exprs) == 1)) else cons(make_symbol('begin'), body_exprs))
+ param_names = map(lambda p: (symbol_name(p) if sx_truthy((type_of(p) == 'symbol')) else p), params_expr)
+ return make_lambda(param_names, body, env)
# sf-define
-sf_define = lambda args, env: (lambda name_sym: (lambda value: _sx_begin((_sx_set_attr(value, 'name', symbol_name(name_sym)) if sx_truthy((is_lambda(value) if not sx_truthy(is_lambda(value)) else is_nil(lambda_name(value)))) else NIL), _sx_dict_set(env, symbol_name(name_sym), value), value))(trampoline(eval_expr(nth(args, 1), env))))(first(args))
+def sf_define(args, env):
+ name_sym = first(args)
+ value = trampoline(eval_expr(nth(args, 1), env))
+ if sx_truthy((is_lambda(value) if not sx_truthy(is_lambda(value)) else is_nil(lambda_name(value)))):
+ value.name = symbol_name(name_sym)
+ env[symbol_name(name_sym)] = value
+ return value
# sf-defcomp
-sf_defcomp = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda body: (lambda comp_name: (lambda parsed: (lambda params: (lambda has_children: (lambda affinity: (lambda comp: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), comp), comp))(make_component(comp_name, params, has_children, body, env, affinity)))(defcomp_kwarg(args, 'affinity', 'auto')))(nth(parsed, 1)))(first(parsed)))(parse_comp_params(params_raw)))(strip_prefix(symbol_name(name_sym), '~')))(last(args)))(nth(args, 1)))(first(args))
+def sf_defcomp(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ body = last(args)
+ comp_name = strip_prefix(symbol_name(name_sym), '~')
+ parsed = parse_comp_params(params_raw)
+ params = first(parsed)
+ has_children = nth(parsed, 1)
+ affinity = defcomp_kwarg(args, 'affinity', 'auto')
+ comp = make_component(comp_name, params, has_children, body, env, affinity)
+ env[symbol_name(name_sym)] = comp
+ return comp
# defcomp-kwarg
def defcomp_kwarg(args, key, default_):
@@ -1047,10 +1333,29 @@ def parse_comp_params(params_expr):
return [params, _cells['has_children']]
# sf-defisland
-sf_defisland = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda body: (lambda comp_name: (lambda parsed: (lambda params: (lambda has_children: (lambda island: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), island), island))(make_island(comp_name, params, has_children, body, env)))(nth(parsed, 1)))(first(parsed)))(parse_comp_params(params_raw)))(strip_prefix(symbol_name(name_sym), '~')))(last(args)))(nth(args, 1)))(first(args))
+def sf_defisland(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ body = last(args)
+ comp_name = strip_prefix(symbol_name(name_sym), '~')
+ parsed = parse_comp_params(params_raw)
+ params = first(parsed)
+ has_children = nth(parsed, 1)
+ island = make_island(comp_name, params, has_children, body, env)
+ env[symbol_name(name_sym)] = island
+ return island
# sf-defmacro
-sf_defmacro = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda body: (lambda parsed: (lambda params: (lambda rest_param: (lambda mac: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), mac), mac))(make_macro(params, rest_param, body, env, symbol_name(name_sym))))(nth(parsed, 1)))(first(parsed)))(parse_macro_params(params_raw)))(nth(args, 2)))(nth(args, 1)))(first(args))
+def sf_defmacro(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ body = nth(args, 2)
+ parsed = parse_macro_params(params_raw)
+ params = first(parsed)
+ rest_param = nth(parsed, 1)
+ mac = make_macro(params, rest_param, body, env, symbol_name(name_sym))
+ env[symbol_name(name_sym)] = mac
+ return mac
# parse-macro-params
def parse_macro_params(params_expr):
@@ -1061,58 +1366,149 @@ def parse_macro_params(params_expr):
return [params, _cells['rest_param']]
# sf-defstyle
-sf_defstyle = lambda args, env: (lambda name_sym: (lambda value: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), value), value))(trampoline(eval_expr(nth(args, 1), env))))(first(args))
+def sf_defstyle(args, env):
+ name_sym = first(args)
+ value = trampoline(eval_expr(nth(args, 1), env))
+ env[symbol_name(name_sym)] = value
+ return value
# sf-begin
-sf_begin = lambda args, env: (NIL if sx_truthy(empty_p(args)) else _sx_begin(for_each(lambda e: trampoline(eval_expr(e, env)), slice(args, 0, (len(args) - 1))), make_thunk(last(args), env)))
+def sf_begin(args, env):
+ if sx_truthy(empty_p(args)):
+ return NIL
+ else:
+ for e in slice(args, 0, (len(args) - 1)):
+ trampoline(eval_expr(e, env))
+ return make_thunk(last(args), env)
# sf-quote
-sf_quote = lambda args, env: (NIL if sx_truthy(empty_p(args)) else first(args))
+def sf_quote(args, env):
+ if sx_truthy(empty_p(args)):
+ return NIL
+ else:
+ return first(args)
# sf-quasiquote
-sf_quasiquote = lambda args, env: qq_expand(first(args), env)
+def sf_quasiquote(args, env):
+ return qq_expand(first(args), env)
# qq-expand
-qq_expand = lambda template, env: (template if sx_truthy((not sx_truthy((type_of(template) == 'list')))) else ([] if sx_truthy(empty_p(template)) else (lambda head: (trampoline(eval_expr(nth(template, 1), env)) if sx_truthy(((type_of(head) == 'symbol') if not sx_truthy((type_of(head) == 'symbol')) else (symbol_name(head) == 'unquote'))) else reduce(lambda result, item: ((lambda spliced: (concat(result, spliced) if sx_truthy((type_of(spliced) == 'list')) else (result if sx_truthy(is_nil(spliced)) else append(result, spliced))))(trampoline(eval_expr(nth(item, 1), env))) if sx_truthy(((type_of(item) == 'list') if not sx_truthy((type_of(item) == 'list')) else ((len(item) == 2) if not sx_truthy((len(item) == 2)) else ((type_of(first(item)) == 'symbol') if not sx_truthy((type_of(first(item)) == 'symbol')) else (symbol_name(first(item)) == 'splice-unquote'))))) else append(result, qq_expand(item, env))), [], template)))(first(template))))
+def qq_expand(template, env):
+ if sx_truthy((not sx_truthy((type_of(template) == 'list')))):
+ return template
+ else:
+ if sx_truthy(empty_p(template)):
+ return []
+ else:
+ head = first(template)
+ if sx_truthy(((type_of(head) == 'symbol') if not sx_truthy((type_of(head) == 'symbol')) else (symbol_name(head) == 'unquote'))):
+ return trampoline(eval_expr(nth(template, 1), env))
+ else:
+ return reduce(lambda result, item: ((lambda spliced: (concat(result, spliced) if sx_truthy((type_of(spliced) == 'list')) else (result if sx_truthy(is_nil(spliced)) else concat(result, [spliced]))))(trampoline(eval_expr(nth(item, 1), env))) if sx_truthy(((type_of(item) == 'list') if not sx_truthy((type_of(item) == 'list')) else ((len(item) == 2) if not sx_truthy((len(item) == 2)) else ((type_of(first(item)) == 'symbol') if not sx_truthy((type_of(first(item)) == 'symbol')) else (symbol_name(first(item)) == 'splice-unquote'))))) else concat(result, [qq_expand(item, env)])), [], template)
# sf-thread-first
-sf_thread_first = lambda args, env: (lambda val: reduce(lambda result, form: ((lambda f: (lambda rest_args: (lambda all_args: (apply(f, all_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, all_args, env)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(cons(result, rest_args)))(map(lambda a: trampoline(eval_expr(a, env)), rest(form))))(trampoline(eval_expr(first(form), env))) if sx_truthy((type_of(form) == 'list')) else (lambda f: (f(result) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, [result], env)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(trampoline(eval_expr(form, env)))), val, rest(args)))(trampoline(eval_expr(first(args), env)))
+def sf_thread_first(args, env):
+ val = trampoline(eval_expr(first(args), env))
+ return reduce(lambda result, form: ((lambda f: (lambda rest_args: (lambda all_args: (apply(f, all_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, all_args, env)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(cons(result, rest_args)))(map(lambda a: trampoline(eval_expr(a, env)), rest(form))))(trampoline(eval_expr(first(form), env))) if sx_truthy((type_of(form) == 'list')) else (lambda f: (f(result) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, [result], env)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(trampoline(eval_expr(form, env)))), val, rest(args))
# sf-set!
-sf_set_bang = lambda args, env: (lambda name: (lambda value: _sx_begin(_sx_dict_set(env, name, value), value))(trampoline(eval_expr(nth(args, 1), env))))(symbol_name(first(args)))
+def sf_set_bang(args, env):
+ name = symbol_name(first(args))
+ value = trampoline(eval_expr(nth(args, 1), env))
+ env[name] = value
+ return value
# sf-letrec
-sf_letrec = lambda args, env: (lambda bindings: (lambda body: (lambda local: (lambda names: (lambda val_exprs: _sx_begin((for_each(lambda binding: (lambda vname: _sx_begin(_sx_append(names, vname), _sx_append(val_exprs, nth(binding, 1)), _sx_dict_set(local, vname, NIL)))((symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else reduce(lambda acc, pair_idx: (lambda vname: (lambda val_expr: _sx_begin(_sx_append(names, vname), _sx_append(val_exprs, val_expr), _sx_dict_set(local, vname, NIL)))(nth(bindings, ((pair_idx * 2) + 1))))((symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), NIL, range(0, (len(bindings) / 2)))), (lambda values: _sx_begin(for_each(lambda pair: _sx_dict_set(local, first(pair), nth(pair, 1)), zip(names, values)), for_each(lambda val: (for_each(lambda n: _sx_dict_set(lambda_closure(val), n, env_get(local, n)), names) if sx_truthy(is_lambda(val)) else NIL), values)))(map(lambda e: trampoline(eval_expr(e, local)), val_exprs)), for_each(lambda e: trampoline(eval_expr(e, local)), slice(body, 0, (len(body) - 1))), make_thunk(last(body), local)))([]))([]))(env_extend(env)))(rest(args)))(first(args))
+def sf_letrec(args, env):
+ bindings = first(args)
+ body = rest(args)
+ local = env_extend(env)
+ names = []
+ val_exprs = []
+ (for_each(lambda binding: (lambda vname: _sx_begin(_sx_append(names, vname), _sx_append(val_exprs, nth(binding, 1)), _sx_dict_set(local, vname, NIL)))((symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))), bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else reduce(lambda acc, pair_idx: (lambda vname: (lambda val_expr: _sx_begin(_sx_append(names, vname), _sx_append(val_exprs, val_expr), _sx_dict_set(local, vname, NIL)))(nth(bindings, ((pair_idx * 2) + 1))))((symbol_name(nth(bindings, (pair_idx * 2))) if sx_truthy((type_of(nth(bindings, (pair_idx * 2))) == 'symbol')) else nth(bindings, (pair_idx * 2)))), NIL, range(0, (len(bindings) / 2))))
+ values = map(lambda e: trampoline(eval_expr(e, local)), val_exprs)
+ for pair in zip(names, values):
+ local[first(pair)] = nth(pair, 1)
+ for val in values:
+ if sx_truthy(is_lambda(val)):
+ for n in names:
+ lambda_closure(val)[n] = env_get(local, n)
+ for e in slice(body, 0, (len(body) - 1)):
+ trampoline(eval_expr(e, local))
+ return make_thunk(last(body), local)
# sf-dynamic-wind
-sf_dynamic_wind = lambda args, env: (lambda before: (lambda body: (lambda after: _sx_begin(call_thunk(before, env), push_wind_b(before, after), (lambda result: _sx_begin(pop_wind_b(), call_thunk(after, env), result))(call_thunk(body, env))))(trampoline(eval_expr(nth(args, 2), env))))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def sf_dynamic_wind(args, env):
+ before = trampoline(eval_expr(first(args), env))
+ body = trampoline(eval_expr(nth(args, 1), env))
+ after = trampoline(eval_expr(nth(args, 2), env))
+ call_thunk(before, env)
+ push_wind_b(before, after)
+ result = call_thunk(body, env)
+ pop_wind_b()
+ call_thunk(after, env)
+ return result
# expand-macro
-expand_macro = lambda mac, raw_args, env: (lambda local: _sx_begin(for_each(lambda pair: _sx_dict_set(local, first(pair), (nth(raw_args, nth(pair, 1)) if sx_truthy((nth(pair, 1) < len(raw_args))) else NIL)), map_indexed(lambda i, p: [p, i], macro_params(mac))), (_sx_dict_set(local, macro_rest_param(mac), slice(raw_args, len(macro_params(mac)))) if sx_truthy(macro_rest_param(mac)) else NIL), trampoline(eval_expr(macro_body(mac), local))))(env_merge(macro_closure(mac), env))
+def expand_macro(mac, raw_args, env):
+ local = env_merge(macro_closure(mac), env)
+ for pair in map_indexed(lambda i, p: [p, i], macro_params(mac)):
+ local[first(pair)] = (nth(raw_args, nth(pair, 1)) if sx_truthy((nth(pair, 1) < len(raw_args))) else NIL)
+ if sx_truthy(macro_rest_param(mac)):
+ local[macro_rest_param(mac)] = slice(raw_args, len(macro_params(mac)))
+ return trampoline(eval_expr(macro_body(mac), local))
# call-fn
-call_fn = lambda f, args, env: (trampoline(call_lambda(f, args, env)) if sx_truthy(is_lambda(f)) else (apply(f, args) if sx_truthy(is_callable(f)) else error(sx_str('Not callable in HO form: ', inspect(f)))))
+def call_fn(f, args, env):
+ if sx_truthy(is_lambda(f)):
+ return trampoline(call_lambda(f, args, env))
+ elif sx_truthy(is_callable(f)):
+ return apply(f, args)
+ else:
+ return error(sx_str('Not callable in HO form: ', inspect(f)))
# ho-map
-ho_map = lambda args, env: (lambda f: (lambda coll: map(lambda item: call_fn(f, [item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_map(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return map(lambda item: call_fn(f, [item], env), coll)
# ho-map-indexed
-ho_map_indexed = lambda args, env: (lambda f: (lambda coll: map_indexed(lambda i, item: call_fn(f, [i, item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_map_indexed(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return map_indexed(lambda i, item: call_fn(f, [i, item], env), coll)
# ho-filter
-ho_filter = lambda args, env: (lambda f: (lambda coll: filter(lambda item: call_fn(f, [item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_filter(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return filter(lambda item: call_fn(f, [item], env), coll)
# ho-reduce
-ho_reduce = lambda args, env: (lambda f: (lambda init: (lambda coll: reduce(lambda acc, item: call_fn(f, [acc, item], env), init, coll))(trampoline(eval_expr(nth(args, 2), env))))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_reduce(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ init = trampoline(eval_expr(nth(args, 1), env))
+ coll = trampoline(eval_expr(nth(args, 2), env))
+ return reduce(lambda acc, item: call_fn(f, [acc, item], env), init, coll)
# ho-some
-ho_some = lambda args, env: (lambda f: (lambda coll: some(lambda item: call_fn(f, [item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_some(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return some(lambda item: call_fn(f, [item], env), coll)
# ho-every
-ho_every = lambda args, env: (lambda f: (lambda coll: every_p(lambda item: call_fn(f, [item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_every(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return every_p(lambda item: call_fn(f, [item], env), coll)
# ho-for-each
-ho_for_each = lambda args, env: (lambda f: (lambda coll: for_each(lambda item: call_fn(f, [item], env), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env)))
+def ho_for_each(args, env):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return for_each(lambda item: call_fn(f, [item], env), coll)
# === Transpiled from forms (server definition forms) ===
@@ -1134,31 +1530,83 @@ def parse_key_params(params_expr):
return params
# sf-defhandler
-sf_defhandler = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda body: (lambda name: (lambda params: (lambda hdef: _sx_begin(_sx_dict_set(env, sx_str('handler:', name), hdef), hdef))(make_handler_def(name, params, body, env)))(parse_key_params(params_raw)))(symbol_name(name_sym)))(nth(args, 2)))(nth(args, 1)))(first(args))
+def sf_defhandler(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ body = nth(args, 2)
+ name = symbol_name(name_sym)
+ params = parse_key_params(params_raw)
+ hdef = make_handler_def(name, params, body, env)
+ env[sx_str('handler:', name)] = hdef
+ return hdef
# sf-defquery
-sf_defquery = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda name: (lambda params: (lambda has_doc: (lambda doc: (lambda body: (lambda qdef: _sx_begin(_sx_dict_set(env, sx_str('query:', name), qdef), qdef))(make_query_def(name, params, doc, body, env)))((nth(args, 3) if sx_truthy(has_doc) else nth(args, 2))))((nth(args, 2) if sx_truthy(has_doc) else '')))(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else (type_of(nth(args, 2)) == 'string'))))(parse_key_params(params_raw)))(symbol_name(name_sym)))(nth(args, 1)))(first(args))
+def sf_defquery(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ name = symbol_name(name_sym)
+ params = parse_key_params(params_raw)
+ has_doc = ((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else (type_of(nth(args, 2)) == 'string'))
+ doc = (nth(args, 2) if sx_truthy(has_doc) else '')
+ body = (nth(args, 3) if sx_truthy(has_doc) else nth(args, 2))
+ qdef = make_query_def(name, params, doc, body, env)
+ env[sx_str('query:', name)] = qdef
+ return qdef
# sf-defaction
-sf_defaction = lambda args, env: (lambda name_sym: (lambda params_raw: (lambda name: (lambda params: (lambda has_doc: (lambda doc: (lambda body: (lambda adef: _sx_begin(_sx_dict_set(env, sx_str('action:', name), adef), adef))(make_action_def(name, params, doc, body, env)))((nth(args, 3) if sx_truthy(has_doc) else nth(args, 2))))((nth(args, 2) if sx_truthy(has_doc) else '')))(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else (type_of(nth(args, 2)) == 'string'))))(parse_key_params(params_raw)))(symbol_name(name_sym)))(nth(args, 1)))(first(args))
+def sf_defaction(args, env):
+ name_sym = first(args)
+ params_raw = nth(args, 1)
+ name = symbol_name(name_sym)
+ params = parse_key_params(params_raw)
+ has_doc = ((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else (type_of(nth(args, 2)) == 'string'))
+ doc = (nth(args, 2) if sx_truthy(has_doc) else '')
+ body = (nth(args, 3) if sx_truthy(has_doc) else nth(args, 2))
+ adef = make_action_def(name, params, doc, body, env)
+ env[sx_str('action:', name)] = adef
+ return adef
# sf-defpage
-sf_defpage = lambda args, env: (lambda name_sym: (lambda name: (lambda slots: _sx_begin((lambda i: (lambda max_i: for_each(lambda idx: ((_sx_dict_set(slots, keyword_name(nth(args, idx)), nth(args, (idx + 1))) if sx_truthy(((idx + 1) < max_i)) else NIL) if sx_truthy(((idx < max_i) if not sx_truthy((idx < max_i)) else (type_of(nth(args, idx)) == 'keyword'))) else NIL), range(1, max_i, 2)))(len(args)))(1), (lambda pdef: _sx_begin(_sx_dict_set(env, sx_str('page:', name), pdef), pdef))(make_page_def(name, slots, env))))({}))(symbol_name(name_sym)))(first(args))
+def sf_defpage(args, env):
+ name_sym = first(args)
+ name = symbol_name(name_sym)
+ slots = {}
+ i = 1
+ max_i = len(args)
+ for idx in range(1, max_i, 2):
+ if sx_truthy(((idx < max_i) if not sx_truthy((idx < max_i)) else (type_of(nth(args, idx)) == 'keyword'))):
+ if sx_truthy(((idx + 1) < max_i)):
+ slots[keyword_name(nth(args, idx))] = nth(args, (idx + 1))
+ pdef = make_page_def(name, slots, env)
+ env[sx_str('page:', name)] = pdef
+ return pdef
# stream-chunk-id
-stream_chunk_id = lambda chunk: (get(chunk, 'stream-id') if sx_truthy(has_key_p(chunk, 'stream-id')) else 'stream-content')
+def stream_chunk_id(chunk):
+ if sx_truthy(has_key_p(chunk, 'stream-id')):
+ return get(chunk, 'stream-id')
+ else:
+ return 'stream-content'
# stream-chunk-bindings
-stream_chunk_bindings = lambda chunk: dissoc(chunk, 'stream-id')
+def stream_chunk_bindings(chunk):
+ return dissoc(chunk, 'stream-id')
# normalize-binding-key
-normalize_binding_key = lambda key: replace(key, '_', '-')
+def normalize_binding_key(key):
+ return replace(key, '_', '-')
# bind-stream-chunk
-bind_stream_chunk = lambda chunk, base_env: (lambda env: (lambda bindings: _sx_begin(for_each(lambda key: _sx_dict_set(env, normalize_binding_key(key), get(bindings, key)), keys(bindings)), env))(stream_chunk_bindings(chunk)))(merge({}, base_env))
+def bind_stream_chunk(chunk, base_env):
+ env = merge({}, base_env)
+ bindings = stream_chunk_bindings(chunk)
+ for key in keys(bindings):
+ env[normalize_binding_key(key)] = get(bindings, key)
+ return env
# validate-stream-data
-validate_stream_data = lambda data: ((type_of(data) == 'list') if not sx_truthy((type_of(data) == 'list')) else every_p(lambda item: (type_of(item) == 'dict'), data))
+def validate_stream_data(data):
+ return ((type_of(data) == 'list') if not sx_truthy((type_of(data) == 'list')) else every_p(lambda item: (type_of(item) == 'dict'), data))
# === Transpiled from render (core) ===
@@ -1173,61 +1621,251 @@ VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'li
BOOLEAN_ATTRS = ['async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer', 'disabled', 'formnovalidate', 'hidden', 'inert', 'ismap', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected']
# definition-form?
-is_definition_form = lambda name: ((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defisland') if sx_truthy((name == 'defisland')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else (name == 'defhandler'))))))
+def is_definition_form(name):
+ return ((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defisland') if sx_truthy((name == 'defisland')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else (name == 'defhandler'))))))
# parse-element-args
-parse_element_args = lambda args, env: (lambda attrs: (lambda children: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(attrs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), [attrs, children]))([]))({})
+def parse_element_args(args, env):
+ attrs = {}
+ children = []
+ reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(attrs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
+ return [attrs, children]
# render-attrs
-render_attrs = lambda attrs: join('', map(lambda key: (lambda val: (sx_str(' ', key) if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else val)) else ('' if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else (not sx_truthy(val)))) else ('' if sx_truthy(is_nil(val)) else sx_str(' ', key, '="', escape_attr(sx_str(val)), '"')))))(dict_get(attrs, key)), keys(attrs)))
+def render_attrs(attrs):
+ return join('', map(lambda key: (lambda val: (sx_str(' ', key) if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else val)) else ('' if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else (not sx_truthy(val)))) else ('' if sx_truthy(is_nil(val)) else sx_str(' ', key, '="', escape_attr(sx_str(val)), '"')))))(dict_get(attrs, key)), keys(attrs)))
# eval-cond
-eval_cond = lambda clauses, env: (eval_cond_scheme(clauses, env) if sx_truthy(((not sx_truthy(empty_p(clauses))) if not sx_truthy((not sx_truthy(empty_p(clauses)))) else ((type_of(first(clauses)) == 'list') if not sx_truthy((type_of(first(clauses)) == 'list')) else (len(first(clauses)) == 2)))) else eval_cond_clojure(clauses, env))
+def eval_cond(clauses, env):
+ if sx_truthy(((not sx_truthy(empty_p(clauses))) if not sx_truthy((not sx_truthy(empty_p(clauses)))) else ((type_of(first(clauses)) == 'list') if not sx_truthy((type_of(first(clauses)) == 'list')) else (len(first(clauses)) == 2)))):
+ return eval_cond_scheme(clauses, env)
+ else:
+ return eval_cond_clojure(clauses, env)
# eval-cond-scheme
-eval_cond_scheme = lambda clauses, env: (NIL if sx_truthy(empty_p(clauses)) else (lambda clause: (lambda test: (lambda body: (body if sx_truthy((((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))) if sx_truthy(((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else')))) else ((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')))) else (body if sx_truthy(trampoline(eval_expr(test, env))) else eval_cond_scheme(rest(clauses), env))))(nth(clause, 1)))(first(clause)))(first(clauses)))
+def eval_cond_scheme(clauses, env):
+ if sx_truthy(empty_p(clauses)):
+ return NIL
+ else:
+ clause = first(clauses)
+ test = first(clause)
+ body = nth(clause, 1)
+ if sx_truthy((((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))) if sx_truthy(((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else')))) else ((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')))):
+ return body
+ else:
+ if sx_truthy(trampoline(eval_expr(test, env))):
+ return body
+ else:
+ return eval_cond_scheme(rest(clauses), env)
# eval-cond-clojure
-eval_cond_clojure = lambda clauses, env: (NIL if sx_truthy((len(clauses) < 2)) else (lambda test: (lambda body: (body if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))) else (body if sx_truthy(trampoline(eval_expr(test, env))) else eval_cond_clojure(slice(clauses, 2), env))))(nth(clauses, 1)))(first(clauses)))
+def eval_cond_clojure(clauses, env):
+ if sx_truthy((len(clauses) < 2)):
+ return NIL
+ else:
+ test = first(clauses)
+ body = nth(clauses, 1)
+ if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))):
+ return body
+ else:
+ if sx_truthy(trampoline(eval_expr(test, env))):
+ return body
+ else:
+ return eval_cond_clojure(slice(clauses, 2), env)
# process-bindings
-process_bindings = lambda bindings, env: (lambda local: _sx_begin(for_each(lambda pair: ((lambda name: _sx_dict_set(local, name, trampoline(eval_expr(nth(pair, 1), local))))((symbol_name(first(pair)) if sx_truthy((type_of(first(pair)) == 'symbol')) else sx_str(first(pair)))) if sx_truthy(((type_of(pair) == 'list') if not sx_truthy((type_of(pair) == 'list')) else (len(pair) >= 2))) else NIL), bindings), local))(merge(env))
+def process_bindings(bindings, env):
+ local = merge(env)
+ for pair in bindings:
+ if sx_truthy(((type_of(pair) == 'list') if not sx_truthy((type_of(pair) == 'list')) else (len(pair) >= 2))):
+ name = (symbol_name(first(pair)) if sx_truthy((type_of(first(pair)) == 'symbol')) else sx_str(first(pair)))
+ local[name] = trampoline(eval_expr(nth(pair, 1), local))
+ return local
# is-render-expr?
-is_render_expr = lambda expr: (False if sx_truthy(((not sx_truthy((type_of(expr) == 'list'))) if sx_truthy((not sx_truthy((type_of(expr) == 'list')))) else empty_p(expr))) else (lambda h: (False if sx_truthy((not sx_truthy((type_of(h) == 'symbol')))) else (lambda n: ((n == '<>') if sx_truthy((n == '<>')) else ((n == 'raw!') if sx_truthy((n == 'raw!')) else (starts_with_p(n, '~') if sx_truthy(starts_with_p(n, '~')) else (starts_with_p(n, 'html:') if sx_truthy(starts_with_p(n, 'html:')) else (contains_p(HTML_TAGS, n) if sx_truthy(contains_p(HTML_TAGS, n)) else ((index_of(n, '-') > 0) if not sx_truthy((index_of(n, '-') > 0)) else ((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword')))))))))(symbol_name(h))))(first(expr)))
+def is_render_expr(expr):
+ if sx_truthy(((not sx_truthy((type_of(expr) == 'list'))) if sx_truthy((not sx_truthy((type_of(expr) == 'list')))) else empty_p(expr))):
+ return False
+ else:
+ h = first(expr)
+ if sx_truthy((not sx_truthy((type_of(h) == 'symbol')))):
+ return False
+ else:
+ n = symbol_name(h)
+ return ((n == '<>') if sx_truthy((n == '<>')) else ((n == 'raw!') if sx_truthy((n == 'raw!')) else (starts_with_p(n, '~') if sx_truthy(starts_with_p(n, '~')) else (starts_with_p(n, 'html:') if sx_truthy(starts_with_p(n, 'html:')) else (contains_p(HTML_TAGS, n) if sx_truthy(contains_p(HTML_TAGS, n)) else ((index_of(n, '-') > 0) if not sx_truthy((index_of(n, '-') > 0)) else ((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword'))))))))
# === Transpiled from adapter-html ===
# render-to-html
-render_to_html = _sx_fn(lambda expr, env: (
- set_render_active_b(True),
- _sx_case(type_of(expr), [('nil', lambda: ''), ('string', lambda: escape_html(expr)), ('number', lambda: sx_str(expr)), ('boolean', lambda: ('true' if sx_truthy(expr) else 'false')), ('list', lambda: ('' if sx_truthy(empty_p(expr)) else render_list_to_html(expr, env))), ('symbol', lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env)), ('keyword', lambda: escape_html(keyword_name(expr))), ('raw-html', lambda: raw_html_content(expr)), (None, lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env))])
-)[-1])
+def render_to_html(expr, env):
+ set_render_active_b(True)
+ _match = type_of(expr)
+ if _match == 'nil':
+ return ''
+ elif _match == 'string':
+ return escape_html(expr)
+ elif _match == 'number':
+ return sx_str(expr)
+ elif _match == 'boolean':
+ if sx_truthy(expr):
+ return 'true'
+ else:
+ return 'false'
+ elif _match == 'list':
+ if sx_truthy(empty_p(expr)):
+ return ''
+ else:
+ return render_list_to_html(expr, env)
+ elif _match == 'symbol':
+ return render_value_to_html(trampoline(eval_expr(expr, env)), env)
+ elif _match == 'keyword':
+ return escape_html(keyword_name(expr))
+ elif _match == 'raw-html':
+ return raw_html_content(expr)
+ else:
+ return render_value_to_html(trampoline(eval_expr(expr, env)), env)
# render-value-to-html
-render_value_to_html = lambda val, env: _sx_case(type_of(val), [('nil', lambda: ''), ('string', lambda: escape_html(val)), ('number', lambda: sx_str(val)), ('boolean', lambda: ('true' if sx_truthy(val) else 'false')), ('list', lambda: render_list_to_html(val, env)), ('raw-html', lambda: raw_html_content(val)), (None, lambda: escape_html(sx_str(val)))])
+def render_value_to_html(val, env):
+ _match = type_of(val)
+ if _match == 'nil':
+ return ''
+ elif _match == 'string':
+ return escape_html(val)
+ elif _match == 'number':
+ return sx_str(val)
+ elif _match == 'boolean':
+ if sx_truthy(val):
+ return 'true'
+ else:
+ return 'false'
+ elif _match == 'list':
+ return render_list_to_html(val, env)
+ elif _match == 'raw-html':
+ return raw_html_content(val)
+ else:
+ return escape_html(sx_str(val))
# RENDER_HTML_FORMS
RENDER_HTML_FORMS = ['if', 'when', 'cond', 'case', 'let', 'let*', 'begin', 'do', 'define', 'defcomp', 'defisland', 'defmacro', 'defstyle', 'defhandler', 'map', 'map-indexed', 'filter', 'for-each']
# render-html-form?
-is_render_html_form = lambda name: contains_p(RENDER_HTML_FORMS, name)
+def is_render_html_form(name):
+ return contains_p(RENDER_HTML_FORMS, name)
# render-list-to-html
-render_list_to_html = lambda expr, env: ('' if sx_truthy(empty_p(expr)) else (lambda head: (join('', map(lambda x: render_value_to_html(x, env), expr)) if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))) else (lambda name: (lambda args: (join('', map(lambda x: render_to_html(x, env), args)) if sx_truthy((name == '<>')) else (join('', map(lambda x: sx_str(trampoline(eval_expr(x, env))), args)) if sx_truthy((name == 'raw!')) else (render_html_lake(args, env) if sx_truthy((name == 'lake')) else (render_html_marsh(args, env) if sx_truthy((name == 'marsh')) else (render_html_element(name, args, env) if sx_truthy(contains_p(HTML_TAGS, name)) else (render_html_island(env_get(env, name), args, env) if sx_truthy((starts_with_p(name, '~') if not sx_truthy(starts_with_p(name, '~')) else (env_has(env, name) if not sx_truthy(env_has(env, name)) else is_island(env_get(env, name))))) else ((lambda val: (render_html_component(val, args, env) if sx_truthy(is_component(val)) else (render_to_html(expand_macro(val, args, env), env) if sx_truthy(is_macro(val)) else error(sx_str('Unknown component: ', name)))))(env_get(env, name)) if sx_truthy(starts_with_p(name, '~')) else (dispatch_html_form(name, expr, env) if sx_truthy(is_render_html_form(name)) else (render_to_html(expand_macro(env_get(env, name), args, env), env) if sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))) else render_value_to_html(trampoline(eval_expr(expr, env)), env)))))))))))(rest(expr)))(symbol_name(head))))(first(expr)))
+def render_list_to_html(expr, env):
+ if sx_truthy(empty_p(expr)):
+ return ''
+ else:
+ head = first(expr)
+ if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
+ return join('', map(lambda x: render_value_to_html(x, env), expr))
+ else:
+ name = symbol_name(head)
+ args = rest(expr)
+ if sx_truthy((name == '<>')):
+ return join('', map(lambda x: render_to_html(x, env), args))
+ elif sx_truthy((name == 'raw!')):
+ return join('', map(lambda x: sx_str(trampoline(eval_expr(x, env))), args))
+ elif sx_truthy((name == 'lake')):
+ return render_html_lake(args, env)
+ elif sx_truthy((name == 'marsh')):
+ return render_html_marsh(args, env)
+ elif sx_truthy(contains_p(HTML_TAGS, name)):
+ return render_html_element(name, args, env)
+ elif sx_truthy((starts_with_p(name, '~') if not sx_truthy(starts_with_p(name, '~')) else (env_has(env, name) if not sx_truthy(env_has(env, name)) else is_island(env_get(env, name))))):
+ return render_html_island(env_get(env, name), args, env)
+ elif sx_truthy(starts_with_p(name, '~')):
+ val = env_get(env, name)
+ if sx_truthy(is_component(val)):
+ return render_html_component(val, args, env)
+ elif sx_truthy(is_macro(val)):
+ return render_to_html(expand_macro(val, args, env), env)
+ else:
+ return error(sx_str('Unknown component: ', name))
+ elif sx_truthy(is_render_html_form(name)):
+ return dispatch_html_form(name, expr, env)
+ elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
+ return render_to_html(expand_macro(env_get(env, name), args, env), env)
+ else:
+ return render_value_to_html(trampoline(eval_expr(expr, env)), env)
# dispatch-html-form
-dispatch_html_form = lambda name, expr, env: ((lambda cond_val: (render_to_html(nth(expr, 2), env) if sx_truthy(cond_val) else (render_to_html(nth(expr, 3), env) if sx_truthy((len(expr) > 3)) else '')))(trampoline(eval_expr(nth(expr, 1), env))) if sx_truthy((name == 'if')) else (('' if sx_truthy((not sx_truthy(trampoline(eval_expr(nth(expr, 1), env))))) else join('', map(lambda i: render_to_html(nth(expr, i), env), range(2, len(expr))))) if sx_truthy((name == 'when')) else ((lambda branch: (render_to_html(branch, env) if sx_truthy(branch) else ''))(eval_cond(rest(expr), env)) if sx_truthy((name == 'cond')) else (render_to_html(trampoline(eval_expr(expr, env)), env) if sx_truthy((name == 'case')) else ((lambda local: join('', map(lambda i: render_to_html(nth(expr, i), local), range(2, len(expr)))))(process_bindings(nth(expr, 1), env)) if sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))) else (join('', map(lambda i: render_to_html(nth(expr, i), env), range(1, len(expr)))) if sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))) else (_sx_begin(trampoline(eval_expr(expr, env)), '') if sx_truthy(is_definition_form(name)) else ((lambda f: (lambda coll: join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll)))(trampoline(eval_expr(nth(expr, 2), env))))(trampoline(eval_expr(nth(expr, 1), env))) if sx_truthy((name == 'map')) else ((lambda f: (lambda coll: join('', map_indexed(lambda i, item: (render_lambda_html(f, [i, item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [i, item]), env)), coll)))(trampoline(eval_expr(nth(expr, 2), env))))(trampoline(eval_expr(nth(expr, 1), env))) if sx_truthy((name == 'map-indexed')) else (render_to_html(trampoline(eval_expr(expr, env)), env) if sx_truthy((name == 'filter')) else ((lambda f: (lambda coll: join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll)))(trampoline(eval_expr(nth(expr, 2), env))))(trampoline(eval_expr(nth(expr, 1), env))) if sx_truthy((name == 'for-each')) else render_value_to_html(trampoline(eval_expr(expr, env)), env))))))))))))
+def dispatch_html_form(name, expr, env):
+ if sx_truthy((name == 'if')):
+ cond_val = trampoline(eval_expr(nth(expr, 1), env))
+ if sx_truthy(cond_val):
+ return render_to_html(nth(expr, 2), env)
+ else:
+ if sx_truthy((len(expr) > 3)):
+ return render_to_html(nth(expr, 3), env)
+ else:
+ return ''
+ elif sx_truthy((name == 'when')):
+ if sx_truthy((not sx_truthy(trampoline(eval_expr(nth(expr, 1), env))))):
+ return ''
+ else:
+ return join('', map(lambda i: render_to_html(nth(expr, i), env), range(2, len(expr))))
+ elif sx_truthy((name == 'cond')):
+ branch = eval_cond(rest(expr), env)
+ if sx_truthy(branch):
+ return render_to_html(branch, env)
+ else:
+ return ''
+ elif sx_truthy((name == 'case')):
+ return render_to_html(trampoline(eval_expr(expr, env)), env)
+ elif sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))):
+ local = process_bindings(nth(expr, 1), env)
+ return join('', map(lambda i: render_to_html(nth(expr, i), local), range(2, len(expr))))
+ elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
+ return join('', map(lambda i: render_to_html(nth(expr, i), env), range(1, len(expr))))
+ elif sx_truthy(is_definition_form(name)):
+ trampoline(eval_expr(expr, env))
+ return ''
+ elif sx_truthy((name == 'map')):
+ f = trampoline(eval_expr(nth(expr, 1), env))
+ coll = trampoline(eval_expr(nth(expr, 2), env))
+ return join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll))
+ elif sx_truthy((name == 'map-indexed')):
+ f = trampoline(eval_expr(nth(expr, 1), env))
+ coll = trampoline(eval_expr(nth(expr, 2), env))
+ return join('', map_indexed(lambda i, item: (render_lambda_html(f, [i, item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [i, item]), env)), coll))
+ elif sx_truthy((name == 'filter')):
+ return render_to_html(trampoline(eval_expr(expr, env)), env)
+ elif sx_truthy((name == 'for-each')):
+ f = trampoline(eval_expr(nth(expr, 1), env))
+ coll = trampoline(eval_expr(nth(expr, 2), env))
+ return join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll))
+ else:
+ return render_value_to_html(trampoline(eval_expr(expr, env)), env)
# render-lambda-html
-render_lambda_html = lambda f, args, env: (lambda local: _sx_begin(for_each_indexed(lambda i, p: _sx_dict_set(local, p, nth(args, i)), lambda_params(f)), render_to_html(lambda_body(f), local)))(env_merge(lambda_closure(f), env))
+def render_lambda_html(f, args, env):
+ local = env_merge(lambda_closure(f), env)
+ for_each_indexed(lambda i, p: _sx_dict_set(local, p, nth(args, i)), lambda_params(f))
+ return render_to_html(lambda_body(f), local)
# render-html-component
-render_html_component = lambda comp, args, env: (lambda kwargs: (lambda children: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(kwargs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), (lambda local: _sx_begin(for_each(lambda p: _sx_dict_set(local, p, (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)), component_params(comp)), (_sx_dict_set(local, 'children', make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))) if sx_truthy(component_has_children(comp)) else NIL), render_to_html(component_body(comp), local)))(env_merge(component_closure(comp), env))))([]))({})
+def render_html_component(comp, args, env):
+ kwargs = {}
+ children = []
+ reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(kwargs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
+ local = env_merge(component_closure(comp), env)
+ for p in component_params(comp):
+ local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
+ if sx_truthy(component_has_children(comp)):
+ local['children'] = make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))
+ return render_to_html(component_body(comp), local)
# render-html-element
-render_html_element = lambda tag, args, env: (lambda parsed: (lambda attrs: (lambda children: (lambda is_void: sx_str('<', tag, render_attrs(attrs), (' />' if sx_truthy(is_void) else sx_str('>', join('', map(lambda c: render_to_html(c, env), children)), '', tag, '>'))))(contains_p(VOID_ELEMENTS, tag)))(nth(parsed, 1)))(first(parsed)))(parse_element_args(args, env))
+def render_html_element(tag, args, env):
+ parsed = parse_element_args(args, env)
+ attrs = first(parsed)
+ children = nth(parsed, 1)
+ is_void = contains_p(VOID_ELEMENTS, tag)
+ return sx_str('<', tag, render_attrs(attrs), (' />' if sx_truthy(is_void) else sx_str('>', join('', map(lambda c: render_to_html(c, env), children)), '', tag, '>')))
# render-html-lake
def render_html_lake(args, env):
@@ -1248,31 +1886,123 @@ def render_html_marsh(args, env):
return sx_str('<', _cells['marsh_tag'], ' data-sx-marsh="', escape_attr((_cells['marsh_id'] if sx_truthy(_cells['marsh_id']) else '')), '">', join('', map(lambda c: render_to_html(c, env), children)), '', _cells['marsh_tag'], '>')
# render-html-island
-render_html_island = lambda island, args, env: (lambda kwargs: (lambda children: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(kwargs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), (lambda local: (lambda island_name: _sx_begin(for_each(lambda p: _sx_dict_set(local, p, (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)), component_params(island)), (_sx_dict_set(local, 'children', make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))) if sx_truthy(component_has_children(island)) else NIL), (lambda body_html: (lambda state_json: sx_str('', body_html, ''))(serialize_island_state(kwargs)))(render_to_html(component_body(island), local))))(component_name(island)))(env_merge(component_closure(island), env))))([]))({})
+def render_html_island(island, args, env):
+ kwargs = {}
+ children = []
+ reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(kwargs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
+ local = env_merge(component_closure(island), env)
+ island_name = component_name(island)
+ for p in component_params(island):
+ local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
+ if sx_truthy(component_has_children(island)):
+ local['children'] = make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))
+ body_html = render_to_html(component_body(island), local)
+ state_json = serialize_island_state(kwargs)
+ return sx_str('', body_html, '')
# serialize-island-state
-serialize_island_state = lambda kwargs: (NIL if sx_truthy(is_empty_dict(kwargs)) else json_serialize(kwargs))
+def serialize_island_state(kwargs):
+ if sx_truthy(is_empty_dict(kwargs)):
+ return NIL
+ else:
+ return json_serialize(kwargs)
# === Transpiled from adapter-sx ===
# render-to-sx
-render_to_sx = lambda expr, env: (lambda result: (result if sx_truthy((type_of(result) == 'string')) else serialize(result)))(aser(expr, env))
+def render_to_sx(expr, env):
+ result = aser(expr, env)
+ if sx_truthy((type_of(result) == 'string')):
+ return result
+ else:
+ return serialize(result)
# aser
-aser = _sx_fn(lambda expr, env: (
- set_render_active_b(True),
- _sx_case(type_of(expr), [('number', lambda: expr), ('string', lambda: expr), ('boolean', lambda: expr), ('nil', lambda: NIL), ('symbol', lambda: (lambda name: (env_get(env, name) if sx_truthy(env_has(env, name)) else (get_primitive(name) if sx_truthy(is_primitive(name)) else (True if sx_truthy((name == 'true')) else (False if sx_truthy((name == 'false')) else (NIL if sx_truthy((name == 'nil')) else error(sx_str('Undefined symbol: ', name))))))))(symbol_name(expr))), ('keyword', lambda: keyword_name(expr)), ('list', lambda: ([] if sx_truthy(empty_p(expr)) else aser_list(expr, env))), (None, lambda: expr)])
-)[-1])
+def aser(expr, env):
+ set_render_active_b(True)
+ _match = type_of(expr)
+ if _match == 'number':
+ return expr
+ elif _match == 'string':
+ return expr
+ elif _match == 'boolean':
+ return expr
+ elif _match == 'nil':
+ return NIL
+ elif _match == 'symbol':
+ name = symbol_name(expr)
+ if sx_truthy(env_has(env, name)):
+ return env_get(env, name)
+ elif sx_truthy(is_primitive(name)):
+ return get_primitive(name)
+ elif sx_truthy((name == 'true')):
+ return True
+ elif sx_truthy((name == 'false')):
+ return False
+ elif sx_truthy((name == 'nil')):
+ return NIL
+ else:
+ return error(sx_str('Undefined symbol: ', name))
+ elif _match == 'keyword':
+ return keyword_name(expr)
+ elif _match == 'list':
+ if sx_truthy(empty_p(expr)):
+ return []
+ else:
+ return aser_list(expr, env)
+ else:
+ return expr
# aser-list
-aser_list = lambda expr, env: (lambda head: (lambda args: (map(lambda x: aser(x, env), expr) if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))) else (lambda name: (aser_fragment(args, env) if sx_truthy((name == '<>')) else (aser_call(name, args, env) if sx_truthy(starts_with_p(name, '~')) else (aser_call(name, args, env) if sx_truthy((name == 'lake')) else (aser_call(name, args, env) if sx_truthy((name == 'marsh')) else (aser_call(name, args, env) if sx_truthy(contains_p(HTML_TAGS, name)) else (aser_special(name, expr, env) if sx_truthy((is_special_form(name) if sx_truthy(is_special_form(name)) else is_ho_form(name))) else (aser(expand_macro(env_get(env, name), args, env), env) if sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))) else (lambda f: (lambda evaled_args: (apply(f, evaled_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else ((not sx_truthy(is_component(f))) if not sx_truthy((not sx_truthy(is_component(f)))) else (not sx_truthy(is_island(f))))))) else (trampoline(call_lambda(f, evaled_args, env)) if sx_truthy(is_lambda(f)) else (aser_call(sx_str('~', component_name(f)), args, env) if sx_truthy(is_component(f)) else (aser_call(sx_str('~', component_name(f)), args, env) if sx_truthy(is_island(f)) else error(sx_str('Not callable: ', inspect(f))))))))(map(lambda a: trampoline(eval_expr(a, env)), args)))(trampoline(eval_expr(head, env)))))))))))(symbol_name(head))))(rest(expr)))(first(expr))
+def aser_list(expr, env):
+ head = first(expr)
+ args = rest(expr)
+ if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
+ return map(lambda x: aser(x, env), expr)
+ else:
+ name = symbol_name(head)
+ if sx_truthy((name == '<>')):
+ return aser_fragment(args, env)
+ elif sx_truthy(starts_with_p(name, '~')):
+ return aser_call(name, args, env)
+ elif sx_truthy((name == 'lake')):
+ return aser_call(name, args, env)
+ elif sx_truthy((name == 'marsh')):
+ return aser_call(name, args, env)
+ elif sx_truthy(contains_p(HTML_TAGS, name)):
+ return aser_call(name, args, env)
+ elif sx_truthy((is_special_form(name) if sx_truthy(is_special_form(name)) else is_ho_form(name))):
+ return aser_special(name, expr, env)
+ elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
+ return aser(expand_macro(env_get(env, name), args, env), env)
+ else:
+ f = trampoline(eval_expr(head, env))
+ evaled_args = map(lambda a: trampoline(eval_expr(a, env)), args)
+ if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else ((not sx_truthy(is_component(f))) if not sx_truthy((not sx_truthy(is_component(f)))) else (not sx_truthy(is_island(f))))))):
+ return apply(f, evaled_args)
+ elif sx_truthy(is_lambda(f)):
+ return trampoline(call_lambda(f, evaled_args, env))
+ elif sx_truthy(is_component(f)):
+ return aser_call(sx_str('~', component_name(f)), args, env)
+ elif sx_truthy(is_island(f)):
+ return aser_call(sx_str('~', component_name(f)), args, env)
+ else:
+ return error(sx_str('Not callable: ', inspect(f)))
# aser-fragment
-aser_fragment = lambda children, env: (lambda parts: ('' if sx_truthy(empty_p(parts)) else sx_str('(<> ', join(' ', map(serialize, parts)), ')')))(filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda c: aser(c, env), children)))
+def aser_fragment(children, env):
+ parts = filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda c: aser(c, env), children))
+ if sx_truthy(empty_p(parts)):
+ return ''
+ else:
+ return sx_str('(<> ', join(' ', map(serialize, parts)), ')')
# aser-call
-aser_call = lambda name, args, env: (lambda parts: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin((_sx_begin(_sx_append(parts, sx_str(':', keyword_name(arg))), _sx_append(parts, serialize(val))) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(aser(nth(args, (get(state, 'i') + 1)), env)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else (lambda val: _sx_begin((_sx_append(parts, serialize(val)) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'i', (get(state, 'i') + 1))))(aser(arg, env)))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), sx_str('(', join(' ', parts), ')')))([name])
+def aser_call(name, args, env):
+ parts = [name]
+ reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin((_sx_begin(_sx_append(parts, sx_str(':', keyword_name(arg))), _sx_append(parts, serialize(val))) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(aser(nth(args, (get(state, 'i') + 1)), env)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else (lambda val: _sx_begin((_sx_append(parts, serialize(val)) if sx_truthy((not sx_truthy(is_nil(val)))) else NIL), assoc(state, 'i', (get(state, 'i') + 1))))(aser(arg, env)))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
+ return sx_str('(', join(' ', parts), ')')
# SPECIAL_FORM_NAMES
SPECIAL_FORM_NAMES = ['if', 'when', 'cond', 'case', 'and', 'or', 'let', 'let*', 'lambda', 'fn', 'define', 'defcomp', 'defmacro', 'defstyle', 'defhandler', 'defpage', 'defquery', 'defaction', 'defrelation', 'begin', 'do', 'quote', 'quasiquote', '->', 'set!', 'letrec', 'dynamic-wind', 'defisland']
@@ -1281,88 +2011,312 @@ SPECIAL_FORM_NAMES = ['if', 'when', 'cond', 'case', 'and', 'or', 'let', 'let*',
HO_FORM_NAMES = ['map', 'map-indexed', 'filter', 'reduce', 'some', 'every?', 'for-each']
# special-form?
-is_special_form = lambda name: contains_p(SPECIAL_FORM_NAMES, name)
+def is_special_form(name):
+ return contains_p(SPECIAL_FORM_NAMES, name)
# ho-form?
-is_ho_form = lambda name: contains_p(HO_FORM_NAMES, name)
+def is_ho_form(name):
+ return contains_p(HO_FORM_NAMES, name)
# aser-special
def aser_special(name, expr, env):
_cells = {}
args = rest(expr)
- return ((aser(nth(args, 1), env) if sx_truthy(trampoline(eval_expr(first(args), env))) else (aser(nth(args, 2), env) if sx_truthy((len(args) > 2)) else NIL)) if sx_truthy((name == 'if')) else ((NIL if sx_truthy((not sx_truthy(trampoline(eval_expr(first(args), env))))) else _sx_begin(_sx_cell_set(_cells, 'result', NIL), _sx_begin(for_each(lambda body: _sx_cell_set(_cells, 'result', aser(body, env)), rest(args)), _cells['result']))) if sx_truthy((name == 'when')) else ((lambda branch: (aser(branch, env) if sx_truthy(branch) else NIL))(eval_cond(args, env)) if sx_truthy((name == 'cond')) else ((lambda match_val: (lambda clauses: eval_case_aser(match_val, clauses, env))(rest(args)))(trampoline(eval_expr(first(args), env))) if sx_truthy((name == 'case')) else ((lambda local: _sx_begin(_sx_cell_set(_cells, 'result', NIL), _sx_begin(for_each(lambda body: _sx_cell_set(_cells, 'result', aser(body, local)), rest(args)), _cells['result'])))(process_bindings(first(args), env)) if sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))) else (_sx_begin(_sx_cell_set(_cells, 'result', NIL), _sx_begin(for_each(lambda body: _sx_cell_set(_cells, 'result', aser(body, env)), args), _cells['result'])) if sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))) else (_sx_begin(_sx_cell_set(_cells, 'result', True), _sx_begin(some(_sx_fn(lambda arg: (
+ if sx_truthy((name == 'if')):
+ if sx_truthy(trampoline(eval_expr(first(args), env))):
+ return aser(nth(args, 1), env)
+ else:
+ if sx_truthy((len(args) > 2)):
+ return aser(nth(args, 2), env)
+ else:
+ return NIL
+ elif sx_truthy((name == 'when')):
+ if sx_truthy((not sx_truthy(trampoline(eval_expr(first(args), env))))):
+ return NIL
+ else:
+ _cells['result'] = NIL
+ for body in rest(args):
+ _cells['result'] = aser(body, env)
+ return _cells['result']
+ elif sx_truthy((name == 'cond')):
+ branch = eval_cond(args, env)
+ if sx_truthy(branch):
+ return aser(branch, env)
+ else:
+ return NIL
+ elif sx_truthy((name == 'case')):
+ match_val = trampoline(eval_expr(first(args), env))
+ clauses = rest(args)
+ return eval_case_aser(match_val, clauses, env)
+ elif sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))):
+ local = process_bindings(first(args), env)
+ _cells['result'] = NIL
+ for body in rest(args):
+ _cells['result'] = aser(body, local)
+ return _cells['result']
+ elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
+ _cells['result'] = NIL
+ for body in args:
+ _cells['result'] = aser(body, env)
+ return _cells['result']
+ elif sx_truthy((name == 'and')):
+ _cells['result'] = True
+ some(_sx_fn(lambda arg: (
_sx_cell_set(_cells, 'result', trampoline(eval_expr(arg, env))),
(not sx_truthy(_cells['result']))
-)[-1]), args), _cells['result'])) if sx_truthy((name == 'and')) else (_sx_begin(_sx_cell_set(_cells, 'result', False), _sx_begin(some(_sx_fn(lambda arg: (
+)[-1]), args)
+ return _cells['result']
+ elif sx_truthy((name == 'or')):
+ _cells['result'] = False
+ some(_sx_fn(lambda arg: (
_sx_cell_set(_cells, 'result', trampoline(eval_expr(arg, env))),
_cells['result']
-)[-1]), args), _cells['result'])) if sx_truthy((name == 'or')) else ((lambda f: (lambda coll: map(lambda item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), item), aser(lambda_body(f), local)))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, item)), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env))) if sx_truthy((name == 'map')) else ((lambda f: (lambda coll: map_indexed(lambda i, item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), i), _sx_dict_set(local, nth(lambda_params(f), 1), item), aser(lambda_body(f), local)))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, i, item)), coll))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env))) if sx_truthy((name == 'map-indexed')) else ((lambda f: (lambda coll: (lambda results: _sx_begin(for_each(lambda item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), item), _sx_append(results, aser(lambda_body(f), local))))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, item)), coll), (NIL if sx_truthy(empty_p(results)) else results)))([]))(trampoline(eval_expr(nth(args, 1), env))))(trampoline(eval_expr(first(args), env))) if sx_truthy((name == 'for-each')) else (_sx_begin(trampoline(eval_expr(expr, env)), serialize(expr)) if sx_truthy((name == 'defisland')) else (_sx_begin(trampoline(eval_expr(expr, env)), NIL) if sx_truthy(((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else ((name == 'defhandler') if sx_truthy((name == 'defhandler')) else ((name == 'defpage') if sx_truthy((name == 'defpage')) else ((name == 'defquery') if sx_truthy((name == 'defquery')) else ((name == 'defaction') if sx_truthy((name == 'defaction')) else (name == 'defrelation')))))))))) else trampoline(eval_expr(expr, env)))))))))))))))
+)[-1]), args)
+ return _cells['result']
+ elif sx_truthy((name == 'map')):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return map(lambda item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), item), aser(lambda_body(f), local)))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, item)), coll)
+ elif sx_truthy((name == 'map-indexed')):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ return map_indexed(lambda i, item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), i), _sx_dict_set(local, nth(lambda_params(f), 1), item), aser(lambda_body(f), local)))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, i, item)), coll)
+ elif sx_truthy((name == 'for-each')):
+ f = trampoline(eval_expr(first(args), env))
+ coll = trampoline(eval_expr(nth(args, 1), env))
+ results = []
+ for item in coll:
+ if sx_truthy(is_lambda(f)):
+ local = env_merge(lambda_closure(f), env)
+ local[first(lambda_params(f))] = item
+ results.append(aser(lambda_body(f), local))
+ else:
+ invoke(f, item)
+ if sx_truthy(empty_p(results)):
+ return NIL
+ else:
+ return results
+ elif sx_truthy((name == 'defisland')):
+ trampoline(eval_expr(expr, env))
+ return serialize(expr)
+ elif sx_truthy(((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else ((name == 'defhandler') if sx_truthy((name == 'defhandler')) else ((name == 'defpage') if sx_truthy((name == 'defpage')) else ((name == 'defquery') if sx_truthy((name == 'defquery')) else ((name == 'defaction') if sx_truthy((name == 'defaction')) else (name == 'defrelation')))))))))):
+ trampoline(eval_expr(expr, env))
+ return NIL
+ else:
+ return trampoline(eval_expr(expr, env))
# eval-case-aser
-eval_case_aser = lambda match_val, clauses, env: (NIL if sx_truthy((len(clauses) < 2)) else (lambda test: (lambda body: (aser(body, env) if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == ':else') if sx_truthy((symbol_name(test) == ':else')) else (symbol_name(test) == 'else'))))) else (aser(body, env) if sx_truthy((match_val == trampoline(eval_expr(test, env)))) else eval_case_aser(match_val, slice(clauses, 2), env))))(nth(clauses, 1)))(first(clauses)))
+def eval_case_aser(match_val, clauses, env):
+ if sx_truthy((len(clauses) < 2)):
+ return NIL
+ else:
+ test = first(clauses)
+ body = nth(clauses, 1)
+ if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == ':else') if sx_truthy((symbol_name(test) == ':else')) else (symbol_name(test) == 'else'))))):
+ return aser(body, env)
+ else:
+ if sx_truthy((match_val == trampoline(eval_expr(test, env)))):
+ return aser(body, env)
+ else:
+ return eval_case_aser(match_val, slice(clauses, 2), env)
# === Transpiled from deps (component dependency analysis) ===
# scan-refs
-scan_refs = lambda node: (lambda refs: _sx_begin(scan_refs_walk(node, refs), refs))([])
+def scan_refs(node):
+ refs = []
+ scan_refs_walk(node, refs)
+ return refs
# scan-refs-walk
-scan_refs_walk = lambda node, refs: ((lambda name: ((_sx_append(refs, name) if sx_truthy((not sx_truthy(contains_p(refs, name)))) else NIL) if sx_truthy(starts_with_p(name, '~')) else NIL))(symbol_name(node)) if sx_truthy((type_of(node) == 'symbol')) else (for_each(lambda item: scan_refs_walk(item, refs), node) if sx_truthy((type_of(node) == 'list')) else (for_each(lambda key: scan_refs_walk(dict_get(node, key), refs), keys(node)) if sx_truthy((type_of(node) == 'dict')) else NIL)))
+def scan_refs_walk(node, refs):
+ if sx_truthy((type_of(node) == 'symbol')):
+ name = symbol_name(node)
+ if sx_truthy(starts_with_p(name, '~')):
+ if sx_truthy((not sx_truthy(contains_p(refs, name)))):
+ return _sx_append(refs, name)
+ return NIL
+ return NIL
+ elif sx_truthy((type_of(node) == 'list')):
+ return for_each(lambda item: scan_refs_walk(item, refs), node)
+ elif sx_truthy((type_of(node) == 'dict')):
+ return for_each(lambda key: scan_refs_walk(dict_get(node, key), refs), keys(node))
+ else:
+ return NIL
# transitive-deps-walk
-transitive_deps_walk = lambda n, seen, env: (_sx_begin(_sx_append(seen, n), (lambda val: (for_each(lambda ref: transitive_deps_walk(ref, seen, env), scan_refs(component_body(val))) if sx_truthy((type_of(val) == 'component')) else (for_each(lambda ref: transitive_deps_walk(ref, seen, env), scan_refs(macro_body(val))) if sx_truthy((type_of(val) == 'macro')) else NIL)))(env_get(env, n))) if sx_truthy((not sx_truthy(contains_p(seen, n)))) else NIL)
+def transitive_deps_walk(n, seen, env):
+ if sx_truthy((not sx_truthy(contains_p(seen, n)))):
+ seen.append(n)
+ val = env_get(env, n)
+ if sx_truthy((type_of(val) == 'component')):
+ return for_each(lambda ref: transitive_deps_walk(ref, seen, env), scan_refs(component_body(val)))
+ elif sx_truthy((type_of(val) == 'macro')):
+ return for_each(lambda ref: transitive_deps_walk(ref, seen, env), scan_refs(macro_body(val)))
+ else:
+ return NIL
+ return NIL
# transitive-deps
-transitive_deps = lambda name, env: (lambda seen: (lambda key: _sx_begin(transitive_deps_walk(key, seen, env), filter(lambda x: (not sx_truthy((x == key))), seen)))((name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))))([])
+def transitive_deps(name, env):
+ seen = []
+ key = (name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))
+ transitive_deps_walk(key, seen, env)
+ return filter(lambda x: (not sx_truthy((x == key))), seen)
# compute-all-deps
-compute_all_deps = lambda env: for_each(lambda name: (lambda val: (component_set_deps(val, transitive_deps(name, env)) if sx_truthy((type_of(val) == 'component')) else NIL))(env_get(env, name)), env_components(env))
+def compute_all_deps(env):
+ return for_each(lambda name: (lambda val: (component_set_deps(val, transitive_deps(name, env)) if sx_truthy((type_of(val) == 'component')) else NIL))(env_get(env, name)), env_components(env))
# scan-components-from-source
-scan_components_from_source = lambda source: (lambda matches: map(lambda m: sx_str('~', m), matches))(regex_find_all('\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)', source))
+def scan_components_from_source(source):
+ matches = regex_find_all('\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)', source)
+ return map(lambda m: sx_str('~', m), matches)
# components-needed
-components_needed = lambda page_source, env: (lambda direct: (lambda all_needed: _sx_begin(for_each(_sx_fn(lambda name: (
- (_sx_append(all_needed, name) if sx_truthy((not sx_truthy(contains_p(all_needed, name)))) else NIL),
- (lambda val: (lambda deps: for_each(lambda dep: (_sx_append(all_needed, dep) if sx_truthy((not sx_truthy(contains_p(all_needed, dep)))) else NIL), deps))((component_deps(val) if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else (not sx_truthy(empty_p(component_deps(val)))))) else transitive_deps(name, env))))(env_get(env, name))
-)[-1]), direct), all_needed))([]))(scan_components_from_source(page_source))
+def components_needed(page_source, env):
+ direct = scan_components_from_source(page_source)
+ all_needed = []
+ for name in direct:
+ if sx_truthy((not sx_truthy(contains_p(all_needed, name)))):
+ all_needed.append(name)
+ val = env_get(env, name)
+ deps = (component_deps(val) if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else (not sx_truthy(empty_p(component_deps(val)))))) else transitive_deps(name, env))
+ for dep in deps:
+ if sx_truthy((not sx_truthy(contains_p(all_needed, dep)))):
+ all_needed.append(dep)
+ return all_needed
# page-component-bundle
-page_component_bundle = lambda page_source, env: components_needed(page_source, env)
+def page_component_bundle(page_source, env):
+ return components_needed(page_source, env)
# page-css-classes
-page_css_classes = lambda page_source, env: (lambda needed: (lambda classes: _sx_begin(for_each(lambda name: (lambda val: (for_each(lambda cls: (_sx_append(classes, cls) if sx_truthy((not sx_truthy(contains_p(classes, cls)))) else NIL), component_css_classes(val)) if sx_truthy((type_of(val) == 'component')) else NIL))(env_get(env, name)), needed), for_each(lambda cls: (_sx_append(classes, cls) if sx_truthy((not sx_truthy(contains_p(classes, cls)))) else NIL), scan_css_classes(page_source)), classes))([]))(components_needed(page_source, env))
+def page_css_classes(page_source, env):
+ needed = components_needed(page_source, env)
+ classes = []
+ for name in needed:
+ val = env_get(env, name)
+ if sx_truthy((type_of(val) == 'component')):
+ for cls in component_css_classes(val):
+ if sx_truthy((not sx_truthy(contains_p(classes, cls)))):
+ classes.append(cls)
+ for cls in scan_css_classes(page_source):
+ if sx_truthy((not sx_truthy(contains_p(classes, cls)))):
+ classes.append(cls)
+ return classes
# scan-io-refs-walk
-scan_io_refs_walk = lambda node, io_names, refs: ((lambda name: ((_sx_append(refs, name) if sx_truthy((not sx_truthy(contains_p(refs, name)))) else NIL) if sx_truthy(contains_p(io_names, name)) else NIL))(symbol_name(node)) if sx_truthy((type_of(node) == 'symbol')) else (for_each(lambda item: scan_io_refs_walk(item, io_names, refs), node) if sx_truthy((type_of(node) == 'list')) else (for_each(lambda key: scan_io_refs_walk(dict_get(node, key), io_names, refs), keys(node)) if sx_truthy((type_of(node) == 'dict')) else NIL)))
+def scan_io_refs_walk(node, io_names, refs):
+ if sx_truthy((type_of(node) == 'symbol')):
+ name = symbol_name(node)
+ if sx_truthy(contains_p(io_names, name)):
+ if sx_truthy((not sx_truthy(contains_p(refs, name)))):
+ return _sx_append(refs, name)
+ return NIL
+ return NIL
+ elif sx_truthy((type_of(node) == 'list')):
+ return for_each(lambda item: scan_io_refs_walk(item, io_names, refs), node)
+ elif sx_truthy((type_of(node) == 'dict')):
+ return for_each(lambda key: scan_io_refs_walk(dict_get(node, key), io_names, refs), keys(node))
+ else:
+ return NIL
# scan-io-refs
-scan_io_refs = lambda node, io_names: (lambda refs: _sx_begin(scan_io_refs_walk(node, io_names, refs), refs))([])
+def scan_io_refs(node, io_names):
+ refs = []
+ scan_io_refs_walk(node, io_names, refs)
+ return refs
# transitive-io-refs-walk
-transitive_io_refs_walk = lambda n, seen, all_refs, env, io_names: (_sx_begin(_sx_append(seen, n), (lambda val: (_sx_begin(for_each(lambda ref: (_sx_append(all_refs, ref) if sx_truthy((not sx_truthy(contains_p(all_refs, ref)))) else NIL), scan_io_refs(component_body(val), io_names)), for_each(lambda dep: transitive_io_refs_walk(dep, seen, all_refs, env, io_names), scan_refs(component_body(val)))) if sx_truthy((type_of(val) == 'component')) else (_sx_begin(for_each(lambda ref: (_sx_append(all_refs, ref) if sx_truthy((not sx_truthy(contains_p(all_refs, ref)))) else NIL), scan_io_refs(macro_body(val), io_names)), for_each(lambda dep: transitive_io_refs_walk(dep, seen, all_refs, env, io_names), scan_refs(macro_body(val)))) if sx_truthy((type_of(val) == 'macro')) else NIL)))(env_get(env, n))) if sx_truthy((not sx_truthy(contains_p(seen, n)))) else NIL)
+def transitive_io_refs_walk(n, seen, all_refs, env, io_names):
+ if sx_truthy((not sx_truthy(contains_p(seen, n)))):
+ seen.append(n)
+ val = env_get(env, n)
+ if sx_truthy((type_of(val) == 'component')):
+ for ref in scan_io_refs(component_body(val), io_names):
+ if sx_truthy((not sx_truthy(contains_p(all_refs, ref)))):
+ all_refs.append(ref)
+ return for_each(lambda dep: transitive_io_refs_walk(dep, seen, all_refs, env, io_names), scan_refs(component_body(val)))
+ elif sx_truthy((type_of(val) == 'macro')):
+ for ref in scan_io_refs(macro_body(val), io_names):
+ if sx_truthy((not sx_truthy(contains_p(all_refs, ref)))):
+ all_refs.append(ref)
+ return for_each(lambda dep: transitive_io_refs_walk(dep, seen, all_refs, env, io_names), scan_refs(macro_body(val)))
+ else:
+ return NIL
+ return NIL
# transitive-io-refs
-transitive_io_refs = lambda name, env, io_names: (lambda all_refs: (lambda seen: (lambda key: _sx_begin(transitive_io_refs_walk(key, seen, all_refs, env, io_names), all_refs))((name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))))([]))([])
+def transitive_io_refs(name, env, io_names):
+ all_refs = []
+ seen = []
+ key = (name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))
+ transitive_io_refs_walk(key, seen, all_refs, env, io_names)
+ return all_refs
# compute-all-io-refs
-compute_all_io_refs = lambda env, io_names: for_each(lambda name: (lambda val: (component_set_io_refs(val, transitive_io_refs(name, env, io_names)) if sx_truthy((type_of(val) == 'component')) else NIL))(env_get(env, name)), env_components(env))
+def compute_all_io_refs(env, io_names):
+ return for_each(lambda name: (lambda val: (component_set_io_refs(val, transitive_io_refs(name, env, io_names)) if sx_truthy((type_of(val) == 'component')) else NIL))(env_get(env, name)), env_components(env))
# component-io-refs-cached
-component_io_refs_cached = lambda name, env, io_names: (lambda key: (lambda val: (component_io_refs(val) if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else ((not sx_truthy(is_nil(component_io_refs(val)))) if not sx_truthy((not sx_truthy(is_nil(component_io_refs(val))))) else (not sx_truthy(empty_p(component_io_refs(val))))))) else transitive_io_refs(name, env, io_names)))(env_get(env, key)))((name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name)))
+def component_io_refs_cached(name, env, io_names):
+ key = (name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))
+ val = env_get(env, key)
+ if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else ((not sx_truthy(is_nil(component_io_refs(val)))) if not sx_truthy((not sx_truthy(is_nil(component_io_refs(val))))) else (not sx_truthy(empty_p(component_io_refs(val))))))):
+ return component_io_refs(val)
+ else:
+ return transitive_io_refs(name, env, io_names)
# component-pure?
-component_pure_p = lambda name, env, io_names: (lambda key: (lambda val: (empty_p(component_io_refs(val)) if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else (not sx_truthy(is_nil(component_io_refs(val)))))) else empty_p(transitive_io_refs(name, env, io_names))))(env_get(env, key)))((name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name)))
+def component_pure_p(name, env, io_names):
+ key = (name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))
+ val = env_get(env, key)
+ if sx_truthy(((type_of(val) == 'component') if not sx_truthy((type_of(val) == 'component')) else (not sx_truthy(is_nil(component_io_refs(val)))))):
+ return empty_p(component_io_refs(val))
+ else:
+ return empty_p(transitive_io_refs(name, env, io_names))
# render-target
-render_target = lambda name, env, io_names: (lambda key: (lambda val: ('server' if sx_truthy((not sx_truthy((type_of(val) == 'component')))) else (lambda affinity: ('server' if sx_truthy((affinity == 'server')) else ('client' if sx_truthy((affinity == 'client')) else ('server' if sx_truthy((not sx_truthy(component_pure_p(name, env, io_names)))) else 'client'))))(component_affinity(val))))(env_get(env, key)))((name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name)))
+def render_target(name, env, io_names):
+ key = (name if sx_truthy(starts_with_p(name, '~')) else sx_str('~', name))
+ val = env_get(env, key)
+ if sx_truthy((not sx_truthy((type_of(val) == 'component')))):
+ return 'server'
+ else:
+ affinity = component_affinity(val)
+ if sx_truthy((affinity == 'server')):
+ return 'server'
+ elif sx_truthy((affinity == 'client')):
+ return 'client'
+ elif sx_truthy((not sx_truthy(component_pure_p(name, env, io_names)))):
+ return 'server'
+ else:
+ return 'client'
# page-render-plan
-page_render_plan = lambda page_source, env, io_names: (lambda needed: (lambda comp_targets: (lambda server_list: (lambda client_list: (lambda io_deps: _sx_begin(for_each(lambda name: (lambda target: _sx_begin(_sx_dict_set(comp_targets, name, target), (_sx_begin(_sx_append(server_list, name), for_each(lambda io_ref: (_sx_append(io_deps, io_ref) if sx_truthy((not sx_truthy(contains_p(io_deps, io_ref)))) else NIL), component_io_refs_cached(name, env, io_names))) if sx_truthy((target == 'server')) else _sx_append(client_list, name))))(render_target(name, env, io_names)), needed), {'components': comp_targets, 'server': server_list, 'client': client_list, 'io-deps': io_deps}))([]))([]))([]))({}))(components_needed(page_source, env))
+def page_render_plan(page_source, env, io_names):
+ needed = components_needed(page_source, env)
+ comp_targets = {}
+ server_list = []
+ client_list = []
+ io_deps = []
+ for name in needed:
+ target = render_target(name, env, io_names)
+ comp_targets[name] = target
+ if sx_truthy((target == 'server')):
+ server_list.append(name)
+ for io_ref in component_io_refs_cached(name, env, io_names):
+ if sx_truthy((not sx_truthy(contains_p(io_deps, io_ref)))):
+ io_deps.append(io_ref)
+ else:
+ client_list.append(name)
+ return {'components': comp_targets, 'server': server_list, 'client': client_list, 'io-deps': io_deps}
# env-components
-env_components = lambda env: filter(lambda k: (lambda v: (is_component(v) if sx_truthy(is_component(v)) else is_macro(v)))(env_get(env, k)), keys(env))
+def env_components(env):
+ return filter(lambda k: (lambda v: (is_component(v) if sx_truthy(is_component(v)) else is_macro(v)))(env_get(env, k)), keys(env))
# === Transpiled from engine (fetch/swap/trigger pure logic) ===
@@ -1374,22 +2328,58 @@ ENGINE_VERBS = ['get', 'post', 'put', 'delete', 'patch']
DEFAULT_SWAP = 'outerHTML'
# parse-time
-parse_time = lambda s: (0 if sx_truthy(is_nil(s)) else (parse_int(s, 0) if sx_truthy(ends_with_p(s, 'ms')) else ((parse_int(replace(s, 's', ''), 0) * 1000) if sx_truthy(ends_with_p(s, 's')) else parse_int(s, 0))))
+def parse_time(s):
+ if sx_truthy(is_nil(s)):
+ return 0
+ elif sx_truthy(ends_with_p(s, 'ms')):
+ return parse_int(s, 0)
+ elif sx_truthy(ends_with_p(s, 's')):
+ return (parse_int(replace(s, 's', ''), 0) * 1000)
+ else:
+ return parse_int(s, 0)
# parse-trigger-spec
-parse_trigger_spec = lambda spec: (NIL if sx_truthy(is_nil(spec)) else (lambda raw_parts: filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda part: (lambda tokens: (NIL if sx_truthy(empty_p(tokens)) else ({'event': 'every', 'modifiers': {'interval': parse_time(nth(tokens, 1))}} if sx_truthy(((first(tokens) == 'every') if not sx_truthy((first(tokens) == 'every')) else (len(tokens) >= 2))) else (lambda mods: _sx_begin(for_each(lambda tok: (_sx_dict_set(mods, 'once', True) if sx_truthy((tok == 'once')) else (_sx_dict_set(mods, 'changed', True) if sx_truthy((tok == 'changed')) else (_sx_dict_set(mods, 'delay', parse_time(slice(tok, 6))) if sx_truthy(starts_with_p(tok, 'delay:')) else (_sx_dict_set(mods, 'from', slice(tok, 5)) if sx_truthy(starts_with_p(tok, 'from:')) else NIL)))), rest(tokens)), {'event': first(tokens), 'modifiers': mods}))({}))))(split(trim(part), ' ')), raw_parts)))(split(spec, ',')))
+def parse_trigger_spec(spec):
+ if sx_truthy(is_nil(spec)):
+ return NIL
+ else:
+ raw_parts = split(spec, ',')
+ return filter(lambda x: (not sx_truthy(is_nil(x))), map(lambda part: (lambda tokens: (NIL if sx_truthy(empty_p(tokens)) else ({'event': 'every', 'modifiers': {'interval': parse_time(nth(tokens, 1))}} if sx_truthy(((first(tokens) == 'every') if not sx_truthy((first(tokens) == 'every')) else (len(tokens) >= 2))) else (lambda mods: _sx_begin(for_each(lambda tok: (_sx_dict_set(mods, 'once', True) if sx_truthy((tok == 'once')) else (_sx_dict_set(mods, 'changed', True) if sx_truthy((tok == 'changed')) else (_sx_dict_set(mods, 'delay', parse_time(slice(tok, 6))) if sx_truthy(starts_with_p(tok, 'delay:')) else (_sx_dict_set(mods, 'from', slice(tok, 5)) if sx_truthy(starts_with_p(tok, 'from:')) else NIL)))), rest(tokens)), {'event': first(tokens), 'modifiers': mods}))({}))))(split(trim(part), ' ')), raw_parts))
# default-trigger
-default_trigger = lambda tag_name: ([{'event': 'submit', 'modifiers': {}}] if sx_truthy((tag_name == 'FORM')) else ([{'event': 'change', 'modifiers': {}}] if sx_truthy(((tag_name == 'INPUT') if sx_truthy((tag_name == 'INPUT')) else ((tag_name == 'SELECT') if sx_truthy((tag_name == 'SELECT')) else (tag_name == 'TEXTAREA')))) else [{'event': 'click', 'modifiers': {}}]))
+def default_trigger(tag_name):
+ if sx_truthy((tag_name == 'FORM')):
+ return [{'event': 'submit', 'modifiers': {}}]
+ elif sx_truthy(((tag_name == 'INPUT') if sx_truthy((tag_name == 'INPUT')) else ((tag_name == 'SELECT') if sx_truthy((tag_name == 'SELECT')) else (tag_name == 'TEXTAREA')))):
+ return [{'event': 'change', 'modifiers': {}}]
+ else:
+ return [{'event': 'click', 'modifiers': {}}]
# get-verb-info
-get_verb_info = lambda el: some(lambda verb: (lambda url: ({'method': upper(verb), 'url': url} if sx_truthy(url) else NIL))(dom_get_attr(el, sx_str('sx-', verb))), ENGINE_VERBS)
+def get_verb_info(el):
+ return some(lambda verb: (lambda url: ({'method': upper(verb), 'url': url} if sx_truthy(url) else NIL))(dom_get_attr(el, sx_str('sx-', verb))), ENGINE_VERBS)
# build-request-headers
-build_request_headers = lambda el, loaded_components, css_hash: (lambda headers: _sx_begin((lambda target_sel: (_sx_dict_set(headers, 'SX-Target', target_sel) if sx_truthy(target_sel) else NIL))(dom_get_attr(el, 'sx-target')), (_sx_dict_set(headers, 'SX-Components', join(',', loaded_components)) if sx_truthy((not sx_truthy(empty_p(loaded_components)))) else NIL), (_sx_dict_set(headers, 'SX-Css', css_hash) if sx_truthy(css_hash) else NIL), (lambda extra_h: ((lambda parsed: (for_each(lambda key: _sx_dict_set(headers, key, sx_str(get(parsed, key))), keys(parsed)) if sx_truthy(parsed) else NIL))(parse_header_value(extra_h)) if sx_truthy(extra_h) else NIL))(dom_get_attr(el, 'sx-headers')), headers))({'SX-Request': 'true', 'SX-Current-URL': browser_location_href()})
+def build_request_headers(el, loaded_components, css_hash):
+ headers = {'SX-Request': 'true', 'SX-Current-URL': browser_location_href()}
+ target_sel = dom_get_attr(el, 'sx-target')
+ if sx_truthy(target_sel):
+ headers['SX-Target'] = target_sel
+ if sx_truthy((not sx_truthy(empty_p(loaded_components)))):
+ headers['SX-Components'] = join(',', loaded_components)
+ if sx_truthy(css_hash):
+ headers['SX-Css'] = css_hash
+ extra_h = dom_get_attr(el, 'sx-headers')
+ if sx_truthy(extra_h):
+ parsed = parse_header_value(extra_h)
+ if sx_truthy(parsed):
+ for key in keys(parsed):
+ headers[key] = sx_str(get(parsed, key))
+ return headers
# process-response-headers
-process_response_headers = lambda get_header: {'redirect': get_header('SX-Redirect'), 'refresh': get_header('SX-Refresh'), 'trigger': get_header('SX-Trigger'), 'retarget': get_header('SX-Retarget'), 'reswap': get_header('SX-Reswap'), 'location': get_header('SX-Location'), 'replace-url': get_header('SX-Replace-Url'), 'css-hash': get_header('SX-Css-Hash'), 'trigger-swap': get_header('SX-Trigger-After-Swap'), 'trigger-settle': get_header('SX-Trigger-After-Settle'), 'content-type': get_header('Content-Type'), 'cache-invalidate': get_header('SX-Cache-Invalidate'), 'cache-update': get_header('SX-Cache-Update')}
+def process_response_headers(get_header):
+ return {'redirect': get_header('SX-Redirect'), 'refresh': get_header('SX-Refresh'), 'trigger': get_header('SX-Trigger'), 'retarget': get_header('SX-Retarget'), 'reswap': get_header('SX-Reswap'), 'location': get_header('SX-Location'), 'replace-url': get_header('SX-Replace-Url'), 'css-hash': get_header('SX-Css-Hash'), 'trigger-swap': get_header('SX-Trigger-After-Swap'), 'trigger-settle': get_header('SX-Trigger-After-Settle'), 'content-type': get_header('Content-Type'), 'cache-invalidate': get_header('SX-Cache-Invalidate'), 'cache-update': get_header('SX-Cache-Update')}
# parse-swap-spec
def parse_swap_spec(raw_swap, global_transitions_p):
@@ -1405,31 +2395,110 @@ def parse_swap_spec(raw_swap, global_transitions_p):
return {'style': style, 'transition': _cells['use_transition']}
# parse-retry-spec
-parse_retry_spec = lambda retry_attr: (NIL if sx_truthy(is_nil(retry_attr)) else (lambda parts: {'strategy': first(parts), 'start-ms': parse_int(nth(parts, 1), 1000), 'cap-ms': parse_int(nth(parts, 2), 30000)})(split(retry_attr, ':')))
+def parse_retry_spec(retry_attr):
+ if sx_truthy(is_nil(retry_attr)):
+ return NIL
+ else:
+ parts = split(retry_attr, ':')
+ return {'strategy': first(parts), 'start-ms': parse_int(nth(parts, 1), 1000), 'cap-ms': parse_int(nth(parts, 2), 30000)}
# next-retry-ms
-next_retry_ms = lambda current_ms, cap_ms: min((current_ms * 2), cap_ms)
+def next_retry_ms(current_ms, cap_ms):
+ return min((current_ms * 2), cap_ms)
# filter-params
-filter_params = lambda params_spec, all_params: (all_params if sx_truthy(is_nil(params_spec)) else ([] if sx_truthy((params_spec == 'none')) else (all_params if sx_truthy((params_spec == '*')) else ((lambda excluded: filter(lambda p: (not sx_truthy(contains_p(excluded, first(p)))), all_params))(map(trim, split(slice(params_spec, 4), ','))) if sx_truthy(starts_with_p(params_spec, 'not ')) else (lambda allowed: filter(lambda p: contains_p(allowed, first(p)), all_params))(map(trim, split(params_spec, ',')))))))
+def filter_params(params_spec, all_params):
+ if sx_truthy(is_nil(params_spec)):
+ return all_params
+ elif sx_truthy((params_spec == 'none')):
+ return []
+ elif sx_truthy((params_spec == '*')):
+ return all_params
+ elif sx_truthy(starts_with_p(params_spec, 'not ')):
+ excluded = map(trim, split(slice(params_spec, 4), ','))
+ return filter(lambda p: (not sx_truthy(contains_p(excluded, first(p)))), all_params)
+ else:
+ allowed = map(trim, split(params_spec, ','))
+ return filter(lambda p: contains_p(allowed, first(p)), all_params)
# resolve-target
-resolve_target = lambda el: (lambda sel: (el if sx_truthy((is_nil(sel) if sx_truthy(is_nil(sel)) else (sel == 'this'))) else (dom_parent(el) if sx_truthy((sel == 'closest')) else dom_query(sel))))(dom_get_attr(el, 'sx-target'))
+def resolve_target(el):
+ sel = dom_get_attr(el, 'sx-target')
+ if sx_truthy((is_nil(sel) if sx_truthy(is_nil(sel)) else (sel == 'this'))):
+ return el
+ elif sx_truthy((sel == 'closest')):
+ return dom_parent(el)
+ else:
+ return dom_query(sel)
# apply-optimistic
-apply_optimistic = lambda el: (lambda directive: (NIL if sx_truthy(is_nil(directive)) else (lambda target: (lambda state: _sx_begin((_sx_begin(_sx_dict_set(state, 'opacity', dom_get_style(target, 'opacity')), dom_set_style(target, 'opacity', '0'), dom_set_style(target, 'pointer-events', 'none')) if sx_truthy((directive == 'remove')) else (_sx_begin(_sx_dict_set(state, 'disabled', dom_get_prop(target, 'disabled')), dom_set_prop(target, 'disabled', True)) if sx_truthy((directive == 'disable')) else ((lambda cls: _sx_begin(_sx_dict_set(state, 'add-class', cls), dom_add_class(target, cls)))(slice(directive, 10)) if sx_truthy(starts_with_p(directive, 'add-class:')) else NIL))), state))({'target': target, 'directive': directive}))((resolve_target(el) if sx_truthy(resolve_target(el)) else el))))(dom_get_attr(el, 'sx-optimistic'))
+def apply_optimistic(el):
+ directive = dom_get_attr(el, 'sx-optimistic')
+ if sx_truthy(is_nil(directive)):
+ return NIL
+ else:
+ target = (resolve_target(el) if sx_truthy(resolve_target(el)) else el)
+ state = {'target': target, 'directive': directive}
+ (_sx_begin(_sx_dict_set(state, 'opacity', dom_get_style(target, 'opacity')), dom_set_style(target, 'opacity', '0'), dom_set_style(target, 'pointer-events', 'none')) if sx_truthy((directive == 'remove')) else (_sx_begin(_sx_dict_set(state, 'disabled', dom_get_prop(target, 'disabled')), dom_set_prop(target, 'disabled', True)) if sx_truthy((directive == 'disable')) else ((lambda cls: _sx_begin(_sx_dict_set(state, 'add-class', cls), dom_add_class(target, cls)))(slice(directive, 10)) if sx_truthy(starts_with_p(directive, 'add-class:')) else NIL)))
+ return state
# revert-optimistic
-revert_optimistic = lambda state: ((lambda target: (lambda directive: (_sx_begin(dom_set_style(target, 'opacity', (get(state, 'opacity') if sx_truthy(get(state, 'opacity')) else '')), dom_set_style(target, 'pointer-events', '')) if sx_truthy((directive == 'remove')) else (dom_set_prop(target, 'disabled', (get(state, 'disabled') if sx_truthy(get(state, 'disabled')) else False)) if sx_truthy((directive == 'disable')) else (dom_remove_class(target, get(state, 'add-class')) if sx_truthy(get(state, 'add-class')) else NIL))))(get(state, 'directive')))(get(state, 'target')) if sx_truthy(state) else NIL)
+def revert_optimistic(state):
+ if sx_truthy(state):
+ target = get(state, 'target')
+ directive = get(state, 'directive')
+ if sx_truthy((directive == 'remove')):
+ dom_set_style(target, 'opacity', (get(state, 'opacity') if sx_truthy(get(state, 'opacity')) else ''))
+ return dom_set_style(target, 'pointer-events', '')
+ elif sx_truthy((directive == 'disable')):
+ return dom_set_prop(target, 'disabled', (get(state, 'disabled') if sx_truthy(get(state, 'disabled')) else False))
+ elif sx_truthy(get(state, 'add-class')):
+ return dom_remove_class(target, get(state, 'add-class'))
+ return NIL
+ return NIL
# find-oob-swaps
-find_oob_swaps = lambda container: (lambda results: _sx_begin(for_each(lambda attr: (lambda oob_els: for_each(lambda oob: (lambda swap_type: (lambda target_id: _sx_begin(dom_remove_attr(oob, attr), (_sx_append(results, {'element': oob, 'swap-type': swap_type, 'target-id': target_id}) if sx_truthy(target_id) else NIL)))(dom_id(oob)))((dom_get_attr(oob, attr) if sx_truthy(dom_get_attr(oob, attr)) else 'outerHTML')), oob_els))(dom_query_all(container, sx_str('[', attr, ']'))), ['sx-swap-oob', 'hx-swap-oob']), results))([])
+def find_oob_swaps(container):
+ results = []
+ for attr in ['sx-swap-oob', 'hx-swap-oob']:
+ oob_els = dom_query_all(container, sx_str('[', attr, ']'))
+ for oob in oob_els:
+ swap_type = (dom_get_attr(oob, attr) if sx_truthy(dom_get_attr(oob, attr)) else 'outerHTML')
+ target_id = dom_id(oob)
+ dom_remove_attr(oob, attr)
+ if sx_truthy(target_id):
+ results.append({'element': oob, 'swap-type': swap_type, 'target-id': target_id})
+ return results
# morph-node
-morph_node = lambda old_node, new_node: (NIL if sx_truthy((dom_has_attr_p(old_node, 'sx-preserve') if sx_truthy(dom_has_attr_p(old_node, 'sx-preserve')) else dom_has_attr_p(old_node, 'sx-ignore'))) else (morph_island_children(old_node, new_node) if sx_truthy((dom_has_attr_p(old_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(old_node, 'data-sx-island')) else (is_processed_p(old_node, 'island-hydrated') if not sx_truthy(is_processed_p(old_node, 'island-hydrated')) else (dom_has_attr_p(new_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(new_node, 'data-sx-island')) else (dom_get_attr(old_node, 'data-sx-island') == dom_get_attr(new_node, 'data-sx-island')))))) else (dom_replace_child(dom_parent(old_node), dom_clone(new_node), old_node) if sx_truthy(((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node)))) if sx_truthy((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node))))) else (not sx_truthy((dom_node_name(old_node) == dom_node_name(new_node)))))) else ((dom_set_text_content(old_node, dom_text_content(new_node)) if sx_truthy((not sx_truthy((dom_text_content(old_node) == dom_text_content(new_node))))) else NIL) if sx_truthy(((dom_node_type(old_node) == 3) if sx_truthy((dom_node_type(old_node) == 3)) else (dom_node_type(old_node) == 8))) else (_sx_begin(sync_attrs(old_node, new_node), (morph_children(old_node, new_node) if sx_truthy((not sx_truthy((dom_is_active_element_p(old_node) if not sx_truthy(dom_is_active_element_p(old_node)) else dom_is_input_element_p(old_node))))) else NIL)) if sx_truthy((dom_node_type(old_node) == 1)) else NIL)))))
+def morph_node(old_node, new_node):
+ if sx_truthy((dom_has_attr_p(old_node, 'sx-preserve') if sx_truthy(dom_has_attr_p(old_node, 'sx-preserve')) else dom_has_attr_p(old_node, 'sx-ignore'))):
+ return NIL
+ elif sx_truthy((dom_has_attr_p(old_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(old_node, 'data-sx-island')) else (is_processed_p(old_node, 'island-hydrated') if not sx_truthy(is_processed_p(old_node, 'island-hydrated')) else (dom_has_attr_p(new_node, 'data-sx-island') if not sx_truthy(dom_has_attr_p(new_node, 'data-sx-island')) else (dom_get_attr(old_node, 'data-sx-island') == dom_get_attr(new_node, 'data-sx-island')))))):
+ return morph_island_children(old_node, new_node)
+ elif sx_truthy(((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node)))) if sx_truthy((not sx_truthy((dom_node_type(old_node) == dom_node_type(new_node))))) else (not sx_truthy((dom_node_name(old_node) == dom_node_name(new_node)))))):
+ return dom_replace_child(dom_parent(old_node), dom_clone(new_node), old_node)
+ elif sx_truthy(((dom_node_type(old_node) == 3) if sx_truthy((dom_node_type(old_node) == 3)) else (dom_node_type(old_node) == 8))):
+ if sx_truthy((not sx_truthy((dom_text_content(old_node) == dom_text_content(new_node))))):
+ return dom_set_text_content(old_node, dom_text_content(new_node))
+ return NIL
+ elif sx_truthy((dom_node_type(old_node) == 1)):
+ sync_attrs(old_node, new_node)
+ if sx_truthy((not sx_truthy((dom_is_active_element_p(old_node) if not sx_truthy(dom_is_active_element_p(old_node)) else dom_is_input_element_p(old_node))))):
+ return morph_children(old_node, new_node)
+ return NIL
+ return NIL
# sync-attrs
-sync_attrs = lambda old_el, new_el: (lambda ra_str: (lambda reactive_attrs: _sx_begin(for_each(lambda attr: (lambda name: (lambda val: (dom_set_attr(old_el, name, val) if sx_truthy(((not sx_truthy((dom_get_attr(old_el, name) == val))) if not sx_truthy((not sx_truthy((dom_get_attr(old_el, name) == val)))) else (not sx_truthy(contains_p(reactive_attrs, name))))) else NIL))(nth(attr, 1)))(first(attr)), dom_attr_list(new_el)), for_each(lambda attr: (lambda aname: (dom_remove_attr(old_el, aname) if sx_truthy(((not sx_truthy(dom_has_attr_p(new_el, aname))) if not sx_truthy((not sx_truthy(dom_has_attr_p(new_el, aname)))) else ((not sx_truthy(contains_p(reactive_attrs, aname))) if not sx_truthy((not sx_truthy(contains_p(reactive_attrs, aname)))) else (not sx_truthy((aname == 'data-sx-reactive-attrs')))))) else NIL))(first(attr)), dom_attr_list(old_el))))(([] if sx_truthy(empty_p(ra_str)) else split(ra_str, ','))))((dom_get_attr(old_el, 'data-sx-reactive-attrs') if sx_truthy(dom_get_attr(old_el, 'data-sx-reactive-attrs')) else ''))
+def sync_attrs(old_el, new_el):
+ ra_str = (dom_get_attr(old_el, 'data-sx-reactive-attrs') if sx_truthy(dom_get_attr(old_el, 'data-sx-reactive-attrs')) else '')
+ reactive_attrs = ([] if sx_truthy(empty_p(ra_str)) else split(ra_str, ','))
+ for attr in dom_attr_list(new_el):
+ name = first(attr)
+ val = nth(attr, 1)
+ if sx_truthy(((not sx_truthy((dom_get_attr(old_el, name) == val))) if not sx_truthy((not sx_truthy((dom_get_attr(old_el, name) == val)))) else (not sx_truthy(contains_p(reactive_attrs, name))))):
+ dom_set_attr(old_el, name, val)
+ return for_each(lambda attr: (lambda aname: (dom_remove_attr(old_el, aname) if sx_truthy(((not sx_truthy(dom_has_attr_p(new_el, aname))) if not sx_truthy((not sx_truthy(dom_has_attr_p(new_el, aname)))) else ((not sx_truthy(contains_p(reactive_attrs, aname))) if not sx_truthy((not sx_truthy(contains_p(reactive_attrs, aname)))) else (not sx_truthy((aname == 'data-sx-reactive-attrs')))))) else NIL))(first(attr)), dom_attr_list(old_el))
# morph-children
def morph_children(old_parent, new_parent):
@@ -1458,66 +2527,230 @@ def morph_children(old_parent, new_parent):
return for_each(lambda i: ((lambda leftover: (dom_remove_child(old_parent, leftover) if sx_truthy((dom_is_child_of_p(leftover, old_parent) if not sx_truthy(dom_is_child_of_p(leftover, old_parent)) else ((not sx_truthy(dom_has_attr_p(leftover, 'sx-preserve'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(leftover, 'sx-preserve')))) else (not sx_truthy(dom_has_attr_p(leftover, 'sx-ignore')))))) else NIL))(nth(old_kids, i)) if sx_truthy((i >= _cells['oi'])) else NIL), range(_cells['oi'], len(old_kids)))
# morph-island-children
-morph_island_children = lambda old_island, new_island: (lambda old_lakes: (lambda new_lakes: (lambda old_marshes: (lambda new_marshes: (lambda new_lake_map: (lambda new_marsh_map: _sx_begin(for_each(lambda lake: (lambda id_: (_sx_dict_set(new_lake_map, id_, lake) if sx_truthy(id_) else NIL))(dom_get_attr(lake, 'data-sx-lake')), new_lakes), for_each(lambda marsh: (lambda id_: (_sx_dict_set(new_marsh_map, id_, marsh) if sx_truthy(id_) else NIL))(dom_get_attr(marsh, 'data-sx-marsh')), new_marshes), for_each(lambda old_lake: (lambda id_: (lambda new_lake: (_sx_begin(sync_attrs(old_lake, new_lake), morph_children(old_lake, new_lake)) if sx_truthy(new_lake) else NIL))(dict_get(new_lake_map, id_)))(dom_get_attr(old_lake, 'data-sx-lake')), old_lakes), for_each(lambda old_marsh: (lambda id_: (lambda new_marsh: (morph_marsh(old_marsh, new_marsh, old_island) if sx_truthy(new_marsh) else NIL))(dict_get(new_marsh_map, id_)))(dom_get_attr(old_marsh, 'data-sx-marsh')), old_marshes), process_signal_updates(new_island)))({}))({}))(dom_query_all(new_island, '[data-sx-marsh]')))(dom_query_all(old_island, '[data-sx-marsh]')))(dom_query_all(new_island, '[data-sx-lake]')))(dom_query_all(old_island, '[data-sx-lake]'))
+def morph_island_children(old_island, new_island):
+ old_lakes = dom_query_all(old_island, '[data-sx-lake]')
+ new_lakes = dom_query_all(new_island, '[data-sx-lake]')
+ old_marshes = dom_query_all(old_island, '[data-sx-marsh]')
+ new_marshes = dom_query_all(new_island, '[data-sx-marsh]')
+ new_lake_map = {}
+ new_marsh_map = {}
+ for lake in new_lakes:
+ id_ = dom_get_attr(lake, 'data-sx-lake')
+ if sx_truthy(id_):
+ new_lake_map[id_] = lake
+ for marsh in new_marshes:
+ id_ = dom_get_attr(marsh, 'data-sx-marsh')
+ if sx_truthy(id_):
+ new_marsh_map[id_] = marsh
+ for old_lake in old_lakes:
+ id_ = dom_get_attr(old_lake, 'data-sx-lake')
+ new_lake = dict_get(new_lake_map, id_)
+ if sx_truthy(new_lake):
+ sync_attrs(old_lake, new_lake)
+ morph_children(old_lake, new_lake)
+ for old_marsh in old_marshes:
+ id_ = dom_get_attr(old_marsh, 'data-sx-marsh')
+ new_marsh = dict_get(new_marsh_map, id_)
+ if sx_truthy(new_marsh):
+ morph_marsh(old_marsh, new_marsh, old_island)
+ return process_signal_updates(new_island)
# morph-marsh
-morph_marsh = lambda old_marsh, new_marsh, island_el: (lambda transform: (lambda env: (lambda new_html: ((lambda parsed: (lambda sx_content: _sx_begin(dispose_marsh_scope(old_marsh), with_marsh_scope(old_marsh, lambda : (lambda new_dom: _sx_begin(dom_remove_children_after(old_marsh, NIL), dom_append(old_marsh, new_dom)))(render_to_dom(sx_content, env, NIL)))))((invoke(transform, parsed) if sx_truthy(transform) else parsed)))(parse(new_html)) if sx_truthy((env if not sx_truthy(env) else (new_html if not sx_truthy(new_html) else (not sx_truthy(empty_p(new_html)))))) else _sx_begin(sync_attrs(old_marsh, new_marsh), morph_children(old_marsh, new_marsh))))(dom_inner_html(new_marsh)))(dom_get_data(old_marsh, 'sx-marsh-env')))(dom_get_data(old_marsh, 'sx-marsh-transform'))
+def morph_marsh(old_marsh, new_marsh, island_el):
+ transform = dom_get_data(old_marsh, 'sx-marsh-transform')
+ env = dom_get_data(old_marsh, 'sx-marsh-env')
+ new_html = dom_inner_html(new_marsh)
+ if sx_truthy((env if not sx_truthy(env) else (new_html if not sx_truthy(new_html) else (not sx_truthy(empty_p(new_html)))))):
+ parsed = parse(new_html)
+ sx_content = (invoke(transform, parsed) if sx_truthy(transform) else parsed)
+ dispose_marsh_scope(old_marsh)
+ return with_marsh_scope(old_marsh, lambda : (lambda new_dom: _sx_begin(dom_remove_children_after(old_marsh, NIL), dom_append(old_marsh, new_dom)))(render_to_dom(sx_content, env, NIL)))
+ else:
+ sync_attrs(old_marsh, new_marsh)
+ return morph_children(old_marsh, new_marsh)
# process-signal-updates
-process_signal_updates = lambda root: (lambda signal_els: for_each(lambda el: (lambda spec: ((lambda colon_idx: ((lambda store_name: (lambda raw_value: _sx_begin((lambda parsed: reset_b(use_store(store_name), parsed))(json_parse(raw_value)), dom_remove_attr(el, 'data-sx-signal')))(slice(spec, (colon_idx + 1))))(slice(spec, 0, colon_idx)) if sx_truthy((colon_idx > 0)) else NIL))(index_of(spec, ':')) if sx_truthy(spec) else NIL))(dom_get_attr(el, 'data-sx-signal')), signal_els))(dom_query_all(root, '[data-sx-signal]'))
+def process_signal_updates(root):
+ signal_els = dom_query_all(root, '[data-sx-signal]')
+ return for_each(lambda el: (lambda spec: ((lambda colon_idx: ((lambda store_name: (lambda raw_value: _sx_begin((lambda parsed: reset_b(use_store(store_name), parsed))(json_parse(raw_value)), dom_remove_attr(el, 'data-sx-signal')))(slice(spec, (colon_idx + 1))))(slice(spec, 0, colon_idx)) if sx_truthy((colon_idx > 0)) else NIL))(index_of(spec, ':')) if sx_truthy(spec) else NIL))(dom_get_attr(el, 'data-sx-signal')), signal_els)
# swap-dom-nodes
-swap_dom_nodes = lambda target, new_nodes, strategy: _sx_case(strategy, [('innerHTML', lambda: (morph_children(target, new_nodes) if sx_truthy(dom_is_fragment_p(new_nodes)) else (lambda wrapper: _sx_begin(dom_append(wrapper, new_nodes), morph_children(target, wrapper)))(dom_create_element('div', NIL)))), ('outerHTML', lambda: (lambda parent: _sx_begin(((lambda fc: (_sx_begin(morph_node(target, fc), (lambda sib: insert_remaining_siblings(parent, target, sib))(dom_next_sibling(fc))) if sx_truthy(fc) else dom_remove_child(parent, target)))(dom_first_child(new_nodes)) if sx_truthy(dom_is_fragment_p(new_nodes)) else morph_node(target, new_nodes)), parent))(dom_parent(target))), ('afterend', lambda: dom_insert_after(target, new_nodes)), ('beforeend', lambda: dom_append(target, new_nodes)), ('afterbegin', lambda: dom_prepend(target, new_nodes)), ('beforebegin', lambda: dom_insert_before(dom_parent(target), new_nodes, target)), ('delete', lambda: dom_remove_child(dom_parent(target), target)), ('none', lambda: NIL), (None, lambda: (morph_children(target, new_nodes) if sx_truthy(dom_is_fragment_p(new_nodes)) else (lambda wrapper: _sx_begin(dom_append(wrapper, new_nodes), morph_children(target, wrapper)))(dom_create_element('div', NIL))))])
+def swap_dom_nodes(target, new_nodes, strategy):
+ _match = strategy
+ if _match == 'innerHTML':
+ if sx_truthy(dom_is_fragment_p(new_nodes)):
+ return morph_children(target, new_nodes)
+ else:
+ wrapper = dom_create_element('div', NIL)
+ dom_append(wrapper, new_nodes)
+ return morph_children(target, wrapper)
+ elif _match == 'outerHTML':
+ parent = dom_parent(target)
+ ((lambda fc: (_sx_begin(morph_node(target, fc), (lambda sib: insert_remaining_siblings(parent, target, sib))(dom_next_sibling(fc))) if sx_truthy(fc) else dom_remove_child(parent, target)))(dom_first_child(new_nodes)) if sx_truthy(dom_is_fragment_p(new_nodes)) else morph_node(target, new_nodes))
+ return parent
+ elif _match == 'afterend':
+ return dom_insert_after(target, new_nodes)
+ elif _match == 'beforeend':
+ return dom_append(target, new_nodes)
+ elif _match == 'afterbegin':
+ return dom_prepend(target, new_nodes)
+ elif _match == 'beforebegin':
+ return dom_insert_before(dom_parent(target), new_nodes, target)
+ elif _match == 'delete':
+ return dom_remove_child(dom_parent(target), target)
+ elif _match == 'none':
+ return NIL
+ else:
+ if sx_truthy(dom_is_fragment_p(new_nodes)):
+ return morph_children(target, new_nodes)
+ else:
+ wrapper = dom_create_element('div', NIL)
+ dom_append(wrapper, new_nodes)
+ return morph_children(target, wrapper)
# insert-remaining-siblings
-insert_remaining_siblings = lambda parent, ref_node, sib: ((lambda next: _sx_begin(dom_insert_after(ref_node, sib), insert_remaining_siblings(parent, sib, next)))(dom_next_sibling(sib)) if sx_truthy(sib) else NIL)
+def insert_remaining_siblings(parent, ref_node, sib):
+ if sx_truthy(sib):
+ next = dom_next_sibling(sib)
+ dom_insert_after(ref_node, sib)
+ return insert_remaining_siblings(parent, sib, next)
+ return NIL
# swap-html-string
-swap_html_string = lambda target, html, strategy: _sx_case(strategy, [('innerHTML', lambda: dom_set_inner_html(target, html)), ('outerHTML', lambda: (lambda parent: _sx_begin(dom_insert_adjacent_html(target, 'afterend', html), dom_remove_child(parent, target), parent))(dom_parent(target))), ('afterend', lambda: dom_insert_adjacent_html(target, 'afterend', html)), ('beforeend', lambda: dom_insert_adjacent_html(target, 'beforeend', html)), ('afterbegin', lambda: dom_insert_adjacent_html(target, 'afterbegin', html)), ('beforebegin', lambda: dom_insert_adjacent_html(target, 'beforebegin', html)), ('delete', lambda: dom_remove_child(dom_parent(target), target)), ('none', lambda: NIL), (None, lambda: dom_set_inner_html(target, html))])
+def swap_html_string(target, html, strategy):
+ _match = strategy
+ if _match == 'innerHTML':
+ return dom_set_inner_html(target, html)
+ elif _match == 'outerHTML':
+ parent = dom_parent(target)
+ dom_insert_adjacent_html(target, 'afterend', html)
+ dom_remove_child(parent, target)
+ return parent
+ elif _match == 'afterend':
+ return dom_insert_adjacent_html(target, 'afterend', html)
+ elif _match == 'beforeend':
+ return dom_insert_adjacent_html(target, 'beforeend', html)
+ elif _match == 'afterbegin':
+ return dom_insert_adjacent_html(target, 'afterbegin', html)
+ elif _match == 'beforebegin':
+ return dom_insert_adjacent_html(target, 'beforebegin', html)
+ elif _match == 'delete':
+ return dom_remove_child(dom_parent(target), target)
+ elif _match == 'none':
+ return NIL
+ else:
+ return dom_set_inner_html(target, html)
# handle-history
-handle_history = lambda el, url, resp_headers: (lambda push_url: (lambda replace_url: (lambda hdr_replace: (browser_replace_state(hdr_replace) if sx_truthy(hdr_replace) else (browser_push_state((url if sx_truthy((push_url == 'true')) else push_url)) if sx_truthy((push_url if not sx_truthy(push_url) else (not sx_truthy((push_url == 'false'))))) else (browser_replace_state((url if sx_truthy((replace_url == 'true')) else replace_url)) if sx_truthy((replace_url if not sx_truthy(replace_url) else (not sx_truthy((replace_url == 'false'))))) else NIL))))(get(resp_headers, 'replace-url')))(dom_get_attr(el, 'sx-replace-url')))(dom_get_attr(el, 'sx-push-url'))
+def handle_history(el, url, resp_headers):
+ push_url = dom_get_attr(el, 'sx-push-url')
+ replace_url = dom_get_attr(el, 'sx-replace-url')
+ hdr_replace = get(resp_headers, 'replace-url')
+ if sx_truthy(hdr_replace):
+ return browser_replace_state(hdr_replace)
+ elif sx_truthy((push_url if not sx_truthy(push_url) else (not sx_truthy((push_url == 'false'))))):
+ return browser_push_state((url if sx_truthy((push_url == 'true')) else push_url))
+ elif sx_truthy((replace_url if not sx_truthy(replace_url) else (not sx_truthy((replace_url == 'false'))))):
+ return browser_replace_state((url if sx_truthy((replace_url == 'true')) else replace_url))
+ return NIL
# PRELOAD_TTL
PRELOAD_TTL = 30000
# preload-cache-get
-preload_cache_get = lambda cache, url: (lambda entry: (NIL if sx_truthy(is_nil(entry)) else (_sx_begin(dict_delete(cache, url), NIL) if sx_truthy(((now_ms() - get(entry, 'timestamp')) > PRELOAD_TTL)) else _sx_begin(dict_delete(cache, url), entry))))(dict_get(cache, url))
+def preload_cache_get(cache, url):
+ entry = dict_get(cache, url)
+ if sx_truthy(is_nil(entry)):
+ return NIL
+ else:
+ if sx_truthy(((now_ms() - get(entry, 'timestamp')) > PRELOAD_TTL)):
+ dict_delete(cache, url)
+ return NIL
+ else:
+ dict_delete(cache, url)
+ return entry
# preload-cache-set
-preload_cache_set = lambda cache, url, text, content_type: _sx_dict_set(cache, url, {'text': text, 'content-type': content_type, 'timestamp': now_ms()})
+def preload_cache_set(cache, url, text, content_type):
+ return _sx_dict_set(cache, url, {'text': text, 'content-type': content_type, 'timestamp': now_ms()})
# classify-trigger
-classify_trigger = lambda trigger: (lambda event: ('poll' if sx_truthy((event == 'every')) else ('intersect' if sx_truthy((event == 'intersect')) else ('load' if sx_truthy((event == 'load')) else ('revealed' if sx_truthy((event == 'revealed')) else 'event')))))(get(trigger, 'event'))
+def classify_trigger(trigger):
+ event = get(trigger, 'event')
+ if sx_truthy((event == 'every')):
+ return 'poll'
+ elif sx_truthy((event == 'intersect')):
+ return 'intersect'
+ elif sx_truthy((event == 'load')):
+ return 'load'
+ elif sx_truthy((event == 'revealed')):
+ return 'revealed'
+ else:
+ return 'event'
# should-boost-link?
-should_boost_link_p = lambda link: (lambda href: (href if not sx_truthy(href) else ((not sx_truthy(starts_with_p(href, '#'))) if not sx_truthy((not sx_truthy(starts_with_p(href, '#')))) else ((not sx_truthy(starts_with_p(href, 'javascript:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'javascript:')))) else ((not sx_truthy(starts_with_p(href, 'mailto:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'mailto:')))) else (browser_same_origin_p(href) if not sx_truthy(browser_same_origin_p(href)) else ((not sx_truthy(dom_has_attr_p(link, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(link, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(link, 'sx-disable')))))))))))(dom_get_attr(link, 'href'))
+def should_boost_link_p(link):
+ href = dom_get_attr(link, 'href')
+ return (href if not sx_truthy(href) else ((not sx_truthy(starts_with_p(href, '#'))) if not sx_truthy((not sx_truthy(starts_with_p(href, '#')))) else ((not sx_truthy(starts_with_p(href, 'javascript:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'javascript:')))) else ((not sx_truthy(starts_with_p(href, 'mailto:'))) if not sx_truthy((not sx_truthy(starts_with_p(href, 'mailto:')))) else (browser_same_origin_p(href) if not sx_truthy(browser_same_origin_p(href)) else ((not sx_truthy(dom_has_attr_p(link, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(link, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(link, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(link, 'sx-disable'))))))))))
# should-boost-form?
-should_boost_form_p = lambda form: ((not sx_truthy(dom_has_attr_p(form, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(form, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(form, 'sx-disable')))))
+def should_boost_form_p(form):
+ return ((not sx_truthy(dom_has_attr_p(form, 'sx-get'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-get')))) else ((not sx_truthy(dom_has_attr_p(form, 'sx-post'))) if not sx_truthy((not sx_truthy(dom_has_attr_p(form, 'sx-post')))) else (not sx_truthy(dom_has_attr_p(form, 'sx-disable')))))
# parse-sse-swap
-parse_sse_swap = lambda el: (dom_get_attr(el, 'sx-sse-swap') if sx_truthy(dom_get_attr(el, 'sx-sse-swap')) else 'message')
+def parse_sse_swap(el):
+ return (dom_get_attr(el, 'sx-sse-swap') if sx_truthy(dom_get_attr(el, 'sx-sse-swap')) else 'message')
# === Transpiled from router (client-side route matching) ===
# split-path-segments
-split_path_segments = lambda path: (lambda trimmed: (lambda trimmed2: ([] if sx_truthy(empty_p(trimmed2)) else split(trimmed2, '/')))((slice(trimmed, 0, (len(trimmed) - 1)) if sx_truthy(((not sx_truthy(empty_p(trimmed))) if not sx_truthy((not sx_truthy(empty_p(trimmed)))) else ends_with_p(trimmed, '/'))) else trimmed)))((slice(path, 1) if sx_truthy(starts_with_p(path, '/')) else path))
+def split_path_segments(path):
+ trimmed = (slice(path, 1) if sx_truthy(starts_with_p(path, '/')) else path)
+ trimmed2 = (slice(trimmed, 0, (len(trimmed) - 1)) if sx_truthy(((not sx_truthy(empty_p(trimmed))) if not sx_truthy((not sx_truthy(empty_p(trimmed)))) else ends_with_p(trimmed, '/'))) else trimmed)
+ if sx_truthy(empty_p(trimmed2)):
+ return []
+ else:
+ return split(trimmed2, '/')
# make-route-segment
-make_route_segment = lambda seg: ((lambda param_name: (lambda d: _sx_begin(_sx_dict_set(d, 'type', 'param'), _sx_dict_set(d, 'value', param_name), d))({}))(slice(seg, 1, (len(seg) - 1))) if sx_truthy((starts_with_p(seg, '<') if not sx_truthy(starts_with_p(seg, '<')) else ends_with_p(seg, '>'))) else (lambda d: _sx_begin(_sx_dict_set(d, 'type', 'literal'), _sx_dict_set(d, 'value', seg), d))({}))
+def make_route_segment(seg):
+ if sx_truthy((starts_with_p(seg, '<') if not sx_truthy(starts_with_p(seg, '<')) else ends_with_p(seg, '>'))):
+ param_name = slice(seg, 1, (len(seg) - 1))
+ d = {}
+ d['type'] = 'param'
+ d['value'] = param_name
+ return d
+ else:
+ d = {}
+ d['type'] = 'literal'
+ d['value'] = seg
+ return d
# parse-route-pattern
-parse_route_pattern = lambda pattern: (lambda segments: map(make_route_segment, segments))(split_path_segments(pattern))
+def parse_route_pattern(pattern):
+ segments = split_path_segments(pattern)
+ return map(make_route_segment, segments)
# match-route-segments
def match_route_segments(path_segs, parsed_segs):
_cells = {}
- return (NIL if sx_truthy((not sx_truthy((len(path_segs) == len(parsed_segs))))) else (lambda params: _sx_begin(_sx_cell_set(_cells, 'matched', True), _sx_begin(for_each_indexed(lambda i, parsed_seg: ((lambda path_seg: (lambda seg_type: ((_sx_cell_set(_cells, 'matched', False) if sx_truthy((not sx_truthy((path_seg == get(parsed_seg, 'value'))))) else NIL) if sx_truthy((seg_type == 'literal')) else (_sx_dict_set(params, get(parsed_seg, 'value'), path_seg) if sx_truthy((seg_type == 'param')) else _sx_cell_set(_cells, 'matched', False))))(get(parsed_seg, 'type')))(nth(path_segs, i)) if sx_truthy(_cells['matched']) else NIL), parsed_segs), (params if sx_truthy(_cells['matched']) else NIL))))({}))
+ if sx_truthy((not sx_truthy((len(path_segs) == len(parsed_segs))))):
+ return NIL
+ else:
+ params = {}
+ _cells['matched'] = True
+ for_each_indexed(lambda i, parsed_seg: ((lambda path_seg: (lambda seg_type: ((_sx_cell_set(_cells, 'matched', False) if sx_truthy((not sx_truthy((path_seg == get(parsed_seg, 'value'))))) else NIL) if sx_truthy((seg_type == 'literal')) else (_sx_dict_set(params, get(parsed_seg, 'value'), path_seg) if sx_truthy((seg_type == 'param')) else _sx_cell_set(_cells, 'matched', False))))(get(parsed_seg, 'type')))(nth(path_segs, i)) if sx_truthy(_cells['matched']) else NIL), parsed_segs)
+ if sx_truthy(_cells['matched']):
+ return params
+ else:
+ return NIL
# match-route
-match_route = lambda path, pattern: (lambda path_segs: (lambda parsed_segs: match_route_segments(path_segs, parsed_segs))(parse_route_pattern(pattern)))(split_path_segments(path))
+def match_route(path, pattern):
+ path_segs = split_path_segments(path)
+ parsed_segs = parse_route_pattern(pattern)
+ return match_route_segments(path_segs, parsed_segs)
# find-matching-route
def find_matching_route(path, routes):
@@ -1537,23 +2770,54 @@ def find_matching_route(path, routes):
# === Transpiled from signals (reactive signal runtime) ===
# signal
-signal = lambda initial_value: make_signal(initial_value)
+def signal(initial_value):
+ return make_signal(initial_value)
# deref
-deref = lambda s: (s if sx_truthy((not sx_truthy(is_signal(s)))) else (lambda ctx: _sx_begin((_sx_begin(tracking_context_add_dep(ctx, s), signal_add_sub(s, tracking_context_notify_fn(ctx))) if sx_truthy(ctx) else NIL), signal_value(s)))(get_tracking_context()))
+def deref(s):
+ if sx_truthy((not sx_truthy(is_signal(s)))):
+ return s
+ else:
+ ctx = get_tracking_context()
+ if sx_truthy(ctx):
+ tracking_context_add_dep(ctx, s)
+ signal_add_sub(s, tracking_context_notify_fn(ctx))
+ return signal_value(s)
# reset!
-reset_b = lambda s, value: ((lambda old: (_sx_begin(signal_set_value(s, value), notify_subscribers(s)) if sx_truthy((not sx_truthy(is_identical(old, value)))) else NIL))(signal_value(s)) if sx_truthy(is_signal(s)) else NIL)
+def reset_b(s, value):
+ if sx_truthy(is_signal(s)):
+ old = signal_value(s)
+ if sx_truthy((not sx_truthy(is_identical(old, value)))):
+ signal_set_value(s, value)
+ return notify_subscribers(s)
+ return NIL
+ return NIL
# swap!
-swap_b = lambda s, f, *args: ((lambda old: (lambda new_val: (_sx_begin(signal_set_value(s, new_val), notify_subscribers(s)) if sx_truthy((not sx_truthy(is_identical(old, new_val)))) else NIL))(apply(f, cons(old, args))))(signal_value(s)) if sx_truthy(is_signal(s)) else NIL)
+def swap_b(s, f, *args):
+ if sx_truthy(is_signal(s)):
+ old = signal_value(s)
+ new_val = apply(f, cons(old, args))
+ if sx_truthy((not sx_truthy(is_identical(old, new_val)))):
+ signal_set_value(s, new_val)
+ return notify_subscribers(s)
+ return NIL
+ return NIL
# computed
-computed = lambda compute_fn: (lambda s: (lambda deps: (lambda compute_ctx: (lambda recompute: _sx_begin(recompute(), register_in_scope(lambda : dispose_computed(s)), s))(_sx_fn(lambda : (
+def computed(compute_fn):
+ s = make_signal(NIL)
+ deps = []
+ compute_ctx = NIL
+ recompute = _sx_fn(lambda : (
for_each(lambda dep: signal_remove_sub(dep, recompute), signal_deps(s)),
signal_set_deps(s, []),
(lambda ctx: (lambda prev: _sx_begin(set_tracking_context(ctx), (lambda new_val: _sx_begin(set_tracking_context(prev), signal_set_deps(s, tracking_context_deps(ctx)), (lambda old: _sx_begin(signal_set_value(s, new_val), (notify_subscribers(s) if sx_truthy((not sx_truthy(is_identical(old, new_val)))) else NIL)))(signal_value(s))))(invoke(compute_fn))))(get_tracking_context()))(make_tracking_context(recompute))
-)[-1])))(NIL))([]))(make_signal(NIL))
+)[-1])
+ recompute()
+ register_in_scope(lambda : dispose_computed(s))
+ return s
# effect
def effect(effect_fn):
@@ -1583,16 +2847,39 @@ def batch(thunk):
_batch_depth = (_batch_depth + 1)
invoke(thunk)
_batch_depth = (_batch_depth - 1)
- return ((lambda queue: _sx_begin(_sx_cell_set(_cells, '_batch_queue', []), (lambda seen: (lambda pending: _sx_begin(for_each(lambda s: for_each(lambda sub: (_sx_begin(_sx_append(seen, sub), _sx_append(pending, sub)) if sx_truthy((not sx_truthy(contains_p(seen, sub)))) else NIL), signal_subscribers(s)), queue), for_each(lambda sub: sub(), pending)))([]))([])))(_batch_queue) if sx_truthy((_batch_depth == 0)) else NIL)
+ if sx_truthy((_batch_depth == 0)):
+ queue = _batch_queue
+ _batch_queue = []
+ seen = []
+ pending = []
+ for s in queue:
+ for sub in signal_subscribers(s):
+ if sx_truthy((not sx_truthy(contains_p(seen, sub)))):
+ seen.append(sub)
+ pending.append(sub)
+ return for_each(lambda sub: sub(), pending)
+ return NIL
# notify-subscribers
-notify_subscribers = lambda s: ((_sx_append(_batch_queue, s) if sx_truthy((not sx_truthy(contains_p(_batch_queue, s)))) else NIL) if sx_truthy((_batch_depth > 0)) else flush_subscribers(s))
+def notify_subscribers(s):
+ if sx_truthy((_batch_depth > 0)):
+ if sx_truthy((not sx_truthy(contains_p(_batch_queue, s)))):
+ return _sx_append(_batch_queue, s)
+ return NIL
+ else:
+ return flush_subscribers(s)
# flush-subscribers
-flush_subscribers = lambda s: for_each(lambda sub: sub(), signal_subscribers(s))
+def flush_subscribers(s):
+ return for_each(lambda sub: sub(), signal_subscribers(s))
# dispose-computed
-dispose_computed = lambda s: (_sx_begin(for_each(lambda dep: signal_remove_sub(dep, NIL), signal_deps(s)), signal_set_deps(s, [])) if sx_truthy(is_signal(s)) else NIL)
+def dispose_computed(s):
+ if sx_truthy(is_signal(s)):
+ for dep in signal_deps(s):
+ signal_remove_sub(dep, NIL)
+ return signal_set_deps(s, [])
+ return NIL
# *island-scope*
_island_scope = NIL
@@ -1606,13 +2893,25 @@ def with_island_scope(scope_fn, body_fn):
return result
# register-in-scope
-register_in_scope = lambda disposable: (_island_scope(disposable) if sx_truthy(_island_scope) else NIL)
+def register_in_scope(disposable):
+ if sx_truthy(_island_scope):
+ return _island_scope(disposable)
+ return NIL
# with-marsh-scope
-with_marsh_scope = lambda marsh_el, body_fn: (lambda disposers: _sx_begin(with_island_scope(lambda d: _sx_append(disposers, d), body_fn), dom_set_data(marsh_el, 'sx-marsh-disposers', disposers)))([])
+def with_marsh_scope(marsh_el, body_fn):
+ disposers = []
+ with_island_scope(lambda d: _sx_append(disposers, d), body_fn)
+ return dom_set_data(marsh_el, 'sx-marsh-disposers', disposers)
# dispose-marsh-scope
-dispose_marsh_scope = lambda marsh_el: (lambda disposers: (_sx_begin(for_each(lambda d: invoke(d), disposers), dom_set_data(marsh_el, 'sx-marsh-disposers', NIL)) if sx_truthy(disposers) else NIL))(dom_get_data(marsh_el, 'sx-marsh-disposers'))
+def dispose_marsh_scope(marsh_el):
+ disposers = dom_get_data(marsh_el, 'sx-marsh-disposers')
+ if sx_truthy(disposers):
+ for d in disposers:
+ invoke(d)
+ return dom_set_data(marsh_el, 'sx-marsh-disposers', NIL)
+ return NIL
# *store-registry*
_store_registry = {}
@@ -1625,23 +2924,33 @@ def def_store(name, init_fn):
return get(_store_registry, name)
# use-store
-use_store = lambda name: (get(_store_registry, name) if sx_truthy(has_key_p(_store_registry, name)) else error(sx_str('Store not found: ', name, '. Call (def-store ...) before (use-store ...).')))
+def use_store(name):
+ if sx_truthy(has_key_p(_store_registry, name)):
+ return get(_store_registry, name)
+ else:
+ return error(sx_str('Store not found: ', name, '. Call (def-store ...) before (use-store ...).'))
# clear-stores
def clear_stores():
return _sx_cell_set(_cells, '_store_registry', {})
# emit-event
-emit_event = lambda el, event_name, detail: dom_dispatch(el, event_name, detail)
+def emit_event(el, event_name, detail):
+ return dom_dispatch(el, event_name, detail)
# on-event
-on_event = lambda el, event_name, handler: dom_listen(el, event_name, handler)
+def on_event(el, event_name, handler):
+ return dom_listen(el, event_name, handler)
# bridge-event
-bridge_event = lambda el, event_name, target_signal, transform_fn: effect(lambda : (lambda remove: remove)(dom_listen(el, event_name, lambda e: (lambda detail: (lambda new_val: reset_b(target_signal, new_val))((invoke(transform_fn, detail) if sx_truthy(transform_fn) else detail)))(event_detail(e)))))
+def bridge_event(el, event_name, target_signal, transform_fn):
+ return effect(lambda : (lambda remove: remove)(dom_listen(el, event_name, lambda e: (lambda detail: (lambda new_val: reset_b(target_signal, new_val))((invoke(transform_fn, detail) if sx_truthy(transform_fn) else detail)))(event_detail(e)))))
# resource
-resource = lambda fetch_fn: (lambda state: _sx_begin(promise_then(invoke(fetch_fn), lambda data: reset_b(state, {'loading': False, 'data': data, 'error': NIL}), lambda err: reset_b(state, {'loading': False, 'data': NIL, 'error': err})), state))(signal({'loading': True, 'data': NIL, 'error': NIL}))
+def resource(fetch_fn):
+ state = signal({'loading': True, 'data': NIL, 'error': NIL})
+ promise_then(invoke(fetch_fn), lambda data: reset_b(state, {'loading': False, 'data': data, 'error': NIL}), lambda err: reset_b(state, {'loading': False, 'data': NIL, 'error': err}))
+ return state
# =========================================================================
diff --git a/shared/sx/tests/run.py b/shared/sx/tests/run.py
index fd88522..9e23d49 100644
--- a/shared/sx/tests/run.py
+++ b/shared/sx/tests/run.py
@@ -771,6 +771,15 @@ def main():
print(f"# --- {spec_name} ---")
eval_file(spec["file"], env)
+ # Reset render state after render tests to avoid leaking
+ # into subsequent specs (bootstrapped evaluator checks render_active)
+ if spec_name == "render":
+ try:
+ from shared.sx.ref.sx_ref import set_render_active_b
+ set_render_active_b(False)
+ except ImportError:
+ pass
+
# Summary
print()
print(f"1..{test_num}")