Files
rose-ash/shared/sx/ref/sx_ref.py
giles a759f4da3b
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Add Freeze/Thaw page under CEK Machine with live demo
Documents and demonstrates serializable CEK state. Type an expression,
step to any point, click Freeze to see the frozen SX. Click Thaw to
resume from the frozen state and get the result.

- New page at /sx/(geography.(cek.freeze))
- Nav entry under CEK Machine
- Interactive island demo with step/run/freeze/thaw buttons
- Documentation: the idea, freeze format, thaw/resume, what it enables

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:31:34 +00:00

5996 lines
251 KiB
Python

"""
sx_ref.py -- Generated from reference SX evaluator specification.
Bootstrap-compiled from shared/sx/ref/{eval,render,adapter-html,adapter-sx}.sx
Compare against hand-written evaluator.py / html.py for correctness verification.
DO NOT EDIT -- regenerate with: python run_py_sx.py
"""
from __future__ import annotations
import math
from typing import Any
# =========================================================================
# Types (reuse existing types)
# =========================================================================
from shared.sx.types import (
NIL, Symbol, Keyword, Lambda, Component, Island, Continuation, Macro,
HandlerDef, QueryDef, ActionDef, PageDef, _ShiftSignal,
)
from shared.sx.parser import SxExpr
from shared.sx.env import Env as _Env, MergedEnv as _MergedEnv
# =========================================================================
# Platform interface -- Python implementation
# =========================================================================
class _Thunk:
"""Deferred evaluation for TCO."""
__slots__ = ("expr", "env")
def __init__(self, expr, env):
self.expr = expr
self.env = env
class _RawHTML:
"""Marker for pre-rendered HTML that should not be escaped."""
__slots__ = ("html",)
def __init__(self, html: str):
self.html = html
class _Spread:
"""Attribute injection value — merges attrs onto parent element."""
__slots__ = ("attrs",)
def __init__(self, attrs: dict):
self.attrs = dict(attrs) if attrs else {}
# Unified scope stacks — backing store for provide/context/emit!/collect!
# Each entry: {"value": v, "emitted": [], "dedup": bool}
_scope_stacks: dict[str, list[dict]] = {}
def _collect_reset():
"""Reset all scope stacks (call at start of each render pass)."""
global _scope_stacks
_scope_stacks = {}
def scope_push(name, value=None):
"""Push a scope with name, value, and empty accumulator."""
_scope_stacks.setdefault(name, []).append({"value": value, "emitted": [], "dedup": False})
def scope_pop(name):
"""Pop the most recent scope for name."""
if name in _scope_stacks and _scope_stacks[name]:
_scope_stacks[name].pop()
# Aliases — provide-push!/provide-pop! map to scope-push!/scope-pop!
provide_push = scope_push
provide_pop = scope_pop
def sx_context(name, *default):
"""Read value from nearest enclosing scope. Error if no scope and no default."""
if name in _scope_stacks and _scope_stacks[name]:
return _scope_stacks[name][-1]["value"]
if default:
return default[0]
raise RuntimeError(f"No provider for: {name}")
def sx_emit(name, value):
"""Append value to nearest enclosing scope's accumulator. Respects dedup flag."""
if name in _scope_stacks and _scope_stacks[name]:
entry = _scope_stacks[name][-1]
if entry["dedup"] and value in entry["emitted"]:
return NIL
entry["emitted"].append(value)
return NIL
def sx_emitted(name):
"""Return list of values emitted into nearest matching scope."""
if name in _scope_stacks and _scope_stacks[name]:
return list(_scope_stacks[name][-1]["emitted"])
return []
def sx_truthy(x):
"""SX truthiness: everything is truthy except False, None, and NIL."""
if x is False:
return False
if x is None or x is NIL:
return False
return True
def sx_str(*args):
"""SX str: concatenate string representations, skipping nil."""
parts = []
for a in args:
if a is None or a is NIL:
continue
parts.append(str(a))
return "".join(parts)
def sx_and(*args):
"""SX and: return last truthy value or first falsy."""
result = True
for a in args:
if not sx_truthy(a):
return a
result = a
return result
def sx_or(*args):
"""SX or: return first truthy value or last value."""
for a in args:
if sx_truthy(a):
return a
return args[-1] if args else False
def _sx_begin(*args):
"""Evaluate all args (for side effects), return last."""
return args[-1] if args else NIL
def _sx_case(match_val, pairs):
"""Case dispatch: pairs is [(test_val, body_fn), ...]. None test = else."""
for test, body_fn in pairs:
if test is None: # :else clause
return body_fn()
if match_val == test:
return body_fn()
return NIL
def _sx_fn(f):
"""Identity wrapper for multi-expression lambda bodies."""
return f
def type_of(x):
if x is None or x is NIL:
return "nil"
if isinstance(x, bool):
return "boolean"
if isinstance(x, (int, float)):
return "number"
if isinstance(x, SxExpr):
return "sx-expr"
if isinstance(x, str):
return "string"
if isinstance(x, Symbol):
return "symbol"
if isinstance(x, Keyword):
return "keyword"
if isinstance(x, _Thunk):
return "thunk"
if isinstance(x, Lambda):
return "lambda"
if isinstance(x, Component):
return "component"
if isinstance(x, Island):
return "island"
if isinstance(x, _Spread):
return "spread"
if isinstance(x, Macro):
return "macro"
if isinstance(x, _RawHTML):
return "raw-html"
if isinstance(x, Continuation):
return "continuation"
if isinstance(x, list):
return "list"
if isinstance(x, dict):
return "dict"
return "unknown"
def symbol_name(s):
return s.name
def keyword_name(k):
return k.name
def make_symbol(n):
return Symbol(n)
def make_keyword(n):
return Keyword(n)
def _ensure_env(env):
"""Wrap plain dict in Env if needed."""
if isinstance(env, _Env):
return env
return _Env(env if isinstance(env, dict) else {})
def make_lambda(params, body, env):
return Lambda(params=list(params), body=body, closure=_ensure_env(env))
def make_component(name, params, has_children, body, env, affinity="auto"):
return Component(name=name, params=list(params), has_children=has_children,
body=body, closure=dict(env), affinity=str(affinity) if affinity else "auto")
def make_island(name, params, has_children, body, env):
return Island(name=name, params=list(params), has_children=has_children,
body=body, closure=dict(env))
def make_macro(params, rest_param, body, env, name=None):
return Macro(params=list(params), rest_param=rest_param, body=body,
closure=dict(env), name=name)
def make_handler_def(name, params, body, env, opts=None):
path = opts.get('path') if opts else None
method = str(opts.get('method', 'get')) if opts else 'get'
csrf = opts.get('csrf', True) if opts else True
returns = str(opts.get('returns', 'element')) if opts else 'element'
if isinstance(csrf, str):
csrf = csrf.lower() not in ('false', 'nil', 'no')
return HandlerDef(name=name, params=list(params), body=body, closure=dict(env),
path=path, method=method.lower(), csrf=csrf, returns=returns)
def make_query_def(name, params, doc, body, env):
return QueryDef(name=name, params=list(params), doc=doc, body=body, closure=dict(env))
def make_action_def(name, params, doc, body, env):
return ActionDef(name=name, params=list(params), doc=doc, body=body, closure=dict(env))
def make_page_def(name, slots, env):
path = slots.get("path", "")
auth_val = slots.get("auth", "public")
if isinstance(auth_val, Keyword):
auth = auth_val.name
elif isinstance(auth_val, list):
auth = [item.name if isinstance(item, Keyword) else str(item) for item in auth_val]
else:
auth = str(auth_val) if auth_val else "public"
layout = slots.get("layout")
if isinstance(layout, Keyword):
layout = layout.name
cache = None
stream_val = slots.get("stream")
stream = bool(trampoline(eval_expr(stream_val, env))) if stream_val is not None else False
return PageDef(
name=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),
)
def make_thunk(expr, env):
return _Thunk(expr, env)
def make_spread(attrs):
return _Spread(attrs if isinstance(attrs, dict) else {})
def is_spread(x):
return isinstance(x, _Spread)
def spread_attrs(s):
return s.attrs if isinstance(s, _Spread) else {}
def sx_collect(bucket, value):
"""Add value to named scope accumulator (deduplicated). Lazily creates root scope."""
if bucket not in _scope_stacks or not _scope_stacks[bucket]:
_scope_stacks.setdefault(bucket, []).append({"value": None, "emitted": [], "dedup": True})
entry = _scope_stacks[bucket][-1]
if value not in entry["emitted"]:
entry["emitted"].append(value)
def sx_collected(bucket):
"""Return all values collected in named scope accumulator."""
return sx_emitted(bucket)
def sx_clear_collected(bucket):
"""Clear nearest scope's accumulator for name."""
if bucket in _scope_stacks and _scope_stacks[bucket]:
_scope_stacks[bucket][-1]["emitted"] = []
def lambda_params(f):
return f.params
def lambda_body(f):
return f.body
def lambda_closure(f):
return f.closure
def lambda_name(f):
return f.name
def set_lambda_name(f, n):
f.name = n
def component_params(c):
return c.params
def component_body(c):
return c.body
def component_closure(c):
return c.closure
def component_has_children(c):
return c.has_children
def component_name(c):
return c.name
def component_affinity(c):
return getattr(c, 'affinity', 'auto')
def component_param_types(c):
return getattr(c, 'param_types', None)
def component_set_param_types(c, d):
c.param_types = d
def macro_params(m):
return m.params
def macro_rest_param(m):
return m.rest_param
def macro_body(m):
return m.body
def macro_closure(m):
return m.closure
def is_thunk(x):
return isinstance(x, _Thunk)
def thunk_expr(t):
return t.expr
def thunk_env(t):
return t.env
def is_callable(x):
return callable(x) or isinstance(x, Lambda)
def is_lambda(x):
return isinstance(x, Lambda)
def is_component(x):
return isinstance(x, Component)
def is_macro(x):
return isinstance(x, Macro)
def is_island(x):
return isinstance(x, Island)
def is_identical(a, b):
return a is b
def json_serialize(obj):
import json
return json.dumps(obj)
def is_empty_dict(d):
if not isinstance(d, dict):
return True
return len(d) == 0
# DOM event primitives — no-ops on server (browser-only).
def dom_listen(el, name, handler):
return lambda: None
def dom_dispatch(el, name, detail=None):
return False
def event_detail(e):
return None
def env_has(env, name):
return name in env
def env_get(env, name):
return env.get(name, NIL)
def env_set(env, name, val):
env[name] = val
def env_extend(env):
return _ensure_env(env).extend()
def env_merge(base, overlay):
base = _ensure_env(base)
overlay = _ensure_env(overlay)
if base is overlay:
# Same env — just extend with empty local scope for params
return base.extend()
# Check if base is an ancestor of overlay — if so, no need to merge
# (common for self-recursive calls where closure == caller's ancestor)
p = overlay
depth = 0
while p is not None and depth < 100:
if p is base:
return base.extend()
p = getattr(p, '_parent', None)
depth += 1
# MergedEnv: reads walk base then overlay; set! walks base only
return _MergedEnv({}, primary=base, secondary=overlay)
def dict_set(d, k, v):
d[k] = v
def dict_get(d, k):
v = d.get(k)
return v if v is not None else NIL
def dict_has(d, k):
return k in d
def dict_delete(d, k):
d.pop(k, None)
def is_render_expr(expr):
"""Placeholder — overridden by transpiled version from render.sx."""
return False
# Render dispatch -- set by adapter
_render_expr_fn = None
# Render mode flag -- set by render-to-html/aser, checked by eval-list
# When false, render expressions (HTML tags, components) fall through to eval-call
_render_mode = False
def render_active_p():
return _render_mode
def set_render_active_b(val):
global _render_mode
_render_mode = bool(val)
def render_expr(expr, env):
if _render_expr_fn:
return _render_expr_fn(expr, env)
# No adapter — fall through to eval_call so components still evaluate
return eval_call(first(expr), rest(expr), env)
def strip_prefix(s, prefix):
return s[len(prefix):] if s.startswith(prefix) else s
def debug_log(*args):
import sys
print(*args, file=sys.stderr)
def error(msg):
raise EvalError(msg)
def inspect(x):
return repr(x)
def escape_html(s):
s = str(s)
return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
def escape_attr(s):
return escape_html(s)
def raw_html_content(x):
return x.html
def make_raw_html(s):
return _RawHTML(s)
def sx_expr_source(x):
return x.source if isinstance(x, SxExpr) else str(x)
try:
from shared.sx.types import EvalError
except ImportError:
class EvalError(Exception):
pass
def _sx_append(lst, item):
"""Append item to list, return the item (for expression context)."""
lst.append(item)
return item
def _sx_dict_set(d, k, v):
"""Set key in dict, return the value (for expression context)."""
d[k] = v
return v
def _sx_set_attr(obj, attr, val):
"""Set attribute on object, return the value."""
setattr(obj, attr, val)
return val
def _sx_cell_set(cells, name, val):
"""Set a mutable cell value. Returns the value."""
cells[name] = val
return val
def escape_string(s):
"""Escape a string for SX serialization."""
return (str(s)
.replace("\\", "\\\\")
.replace('"', '\\"')
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("</script", "<\\/script"))
_SPECIAL_FORM_NAMES = frozenset() # Placeholder — overridden by transpiled adapter-sx.sx
_HO_FORM_NAMES = frozenset()
def is_special_form(name):
"""Placeholder — overridden by transpiled version from adapter-sx.sx."""
return False
def is_ho_form(name):
"""Placeholder — overridden by transpiled version from adapter-sx.sx."""
return False
def aser_special(name, expr, env):
"""Placeholder — overridden by transpiled version from adapter-sx.sx."""
return trampoline(eval_expr(expr, env))
# =========================================================================
# Primitives
# =========================================================================
# Save builtins before shadowing
_b_len = len
_b_map = map
_b_filter = filter
_b_range = range
_b_list = list
_b_dict = dict
_b_max = max
_b_min = min
_b_round = round
_b_abs = abs
_b_sum = sum
_b_zip = zip
_b_int = int
PRIMITIVES = {}
# core.arithmetic
PRIMITIVES["+"] = lambda *args: _b_sum(args)
PRIMITIVES["-"] = lambda a, b=None: -a if b is None else a - b
PRIMITIVES["*"] = lambda *args: _sx_mul(*args)
PRIMITIVES["/"] = lambda a, b: a / b
PRIMITIVES["mod"] = lambda a, b: a % b
PRIMITIVES["inc"] = lambda n: n + 1
PRIMITIVES["dec"] = lambda n: n - 1
PRIMITIVES["abs"] = _b_abs
PRIMITIVES["floor"] = math.floor
PRIMITIVES["ceil"] = math.ceil
PRIMITIVES["round"] = _b_round
PRIMITIVES["min"] = _b_min
PRIMITIVES["max"] = _b_max
PRIMITIVES["sqrt"] = math.sqrt
PRIMITIVES["pow"] = lambda x, n: x ** n
PRIMITIVES["clamp"] = lambda x, lo, hi: _b_max(lo, _b_min(hi, x))
def _sx_mul(*args):
r = 1
for a in args:
r *= a
return r
# core.comparison
PRIMITIVES["="] = lambda a, b: a == b
PRIMITIVES["!="] = lambda a, b: a != b
PRIMITIVES["<"] = lambda a, b: a < b
PRIMITIVES[">"] = lambda a, b: a > b
PRIMITIVES["<="] = lambda a, b: a <= b
PRIMITIVES[">="] = lambda a, b: a >= b
# core.logic
PRIMITIVES["not"] = lambda x: not sx_truthy(x)
# core.predicates
PRIMITIVES["nil?"] = lambda x: x is None or x is NIL
PRIMITIVES["number?"] = lambda x: isinstance(x, (int, float)) and not isinstance(x, bool)
PRIMITIVES["string?"] = lambda x: isinstance(x, str)
PRIMITIVES["list?"] = lambda x: isinstance(x, _b_list)
PRIMITIVES["dict?"] = lambda x: isinstance(x, _b_dict)
PRIMITIVES["boolean?"] = lambda x: isinstance(x, bool)
PRIMITIVES["symbol?"] = lambda x: isinstance(x, Symbol)
PRIMITIVES["keyword?"] = lambda x: isinstance(x, Keyword)
PRIMITIVES["continuation?"] = lambda x: isinstance(x, Continuation)
PRIMITIVES["empty?"] = lambda c: (
c is None or c is NIL or
(isinstance(c, (_b_list, str, _b_dict)) and _b_len(c) == 0)
)
PRIMITIVES["contains?"] = lambda c, k: (
str(k) in c if isinstance(c, str) else
k in c
)
PRIMITIVES["odd?"] = lambda n: n % 2 != 0
PRIMITIVES["even?"] = lambda n: n % 2 == 0
PRIMITIVES["zero?"] = lambda n: n == 0
# core.strings
PRIMITIVES["str"] = sx_str
PRIMITIVES["char-from-code"] = lambda n: chr(_b_int(n))
PRIMITIVES["upper"] = lambda s: str(s).upper()
PRIMITIVES["lower"] = lambda s: str(s).lower()
PRIMITIVES["trim"] = lambda s: str(s).strip()
PRIMITIVES["split"] = lambda s, sep=" ": str(s).split(sep)
PRIMITIVES["join"] = lambda sep, coll: sep.join(str(x) for x in coll)
PRIMITIVES["replace"] = lambda s, old, new: s.replace(old, new)
PRIMITIVES["index-of"] = lambda s, needle, start=0: str(s).find(needle, start)
PRIMITIVES["starts-with?"] = lambda s, p: str(s).startswith(p)
PRIMITIVES["ends-with?"] = lambda s, p: str(s).endswith(p)
PRIMITIVES["slice"] = lambda c, a, b=None: c[int(a):] if (b is None or b is NIL) else c[int(a):int(b)]
PRIMITIVES["concat"] = lambda *args: _b_sum((a for a in args if a), [])
# core.collections
PRIMITIVES["list"] = lambda *args: _b_list(args)
PRIMITIVES["dict"] = lambda *args: {args[i]: args[i+1] for i in _b_range(0, _b_len(args)-1, 2)}
PRIMITIVES["range"] = lambda a, b, step=1: _b_list(_b_range(_b_int(a), _b_int(b), _b_int(step)))
PRIMITIVES["get"] = lambda c, k, default=NIL: c.get(k, default) if isinstance(c, _b_dict) else (c[k] if isinstance(c, (_b_list, str)) and isinstance(k, _b_int) and 0 <= k < _b_len(c) else (c.get(k, default) if hasattr(c, 'get') else default))
PRIMITIVES["len"] = lambda c: _b_len(c) if c is not None and c is not NIL else 0
PRIMITIVES["first"] = lambda c: c[0] if c and _b_len(c) > 0 else NIL
PRIMITIVES["last"] = lambda c: c[-1] if c and _b_len(c) > 0 else NIL
PRIMITIVES["rest"] = lambda c: c[1:] if c else []
PRIMITIVES["nth"] = lambda c, n: c[n] if c and 0 <= n < _b_len(c) else NIL
PRIMITIVES["cons"] = lambda x, c: [x] + (c or [])
PRIMITIVES["append"] = lambda c, x: (c or []) + (x if isinstance(x, list) else [x])
PRIMITIVES["chunk-every"] = lambda c, n: [c[i:i+n] for i in _b_range(0, _b_len(c), n)]
PRIMITIVES["zip-pairs"] = lambda c: [[c[i], c[i+1]] for i in _b_range(_b_len(c)-1)]
# core.dict
PRIMITIVES["keys"] = lambda d: _b_list((d or {}).keys())
PRIMITIVES["vals"] = lambda d: _b_list((d or {}).values())
PRIMITIVES["merge"] = lambda *args: _sx_merge_dicts(*args)
PRIMITIVES["has-key?"] = lambda d, k: isinstance(d, _b_dict) and k in d
PRIMITIVES["assoc"] = lambda d, *kvs: _sx_assoc(d, *kvs)
PRIMITIVES["dissoc"] = lambda d, *ks: {k: v for k, v in d.items() if k not in ks}
PRIMITIVES["into"] = lambda target, coll: (_b_list(coll) if isinstance(target, _b_list) else {p[0]: p[1] for p in coll if isinstance(p, _b_list) and _b_len(p) >= 2})
PRIMITIVES["zip"] = lambda *colls: [_b_list(t) for t in _b_zip(*colls)]
def _sx_merge_dicts(*args):
out = {}
for d in args:
if d and d is not NIL and isinstance(d, _b_dict):
out.update(d)
return out
def _sx_assoc(d, *kvs):
out = _b_dict(d) if d and d is not NIL else {}
for i in _b_range(0, _b_len(kvs) - 1, 2):
out[kvs[i]] = kvs[i + 1]
return out
# stdlib.format
PRIMITIVES["format-decimal"] = lambda v, p=2: f"{float(v):.{p}f}"
PRIMITIVES["parse-int"] = lambda v, d=0: _sx_parse_int(v, d)
PRIMITIVES["parse-datetime"] = lambda s: str(s) if s else NIL
def _sx_parse_int(v, default=0):
if v is None or v is NIL:
return default
s = str(v).strip()
# Match JS parseInt: extract leading integer portion
import re as _re
m = _re.match(r'^[+-]?\d+', s)
if m:
return _b_int(m.group())
return default
# stdlib.text
PRIMITIVES["pluralize"] = lambda n, s="", p="s": s if n == 1 else p
PRIMITIVES["escape"] = escape_html
PRIMITIVES["strip-tags"] = lambda s: _strip_tags(str(s))
import re as _re
def _strip_tags(s):
return _re.sub(r"<[^>]+>", "", s)
# stdlib.style — stubs (CSSX needs full runtime)
# stdlib.debug
PRIMITIVES["assert"] = lambda cond, msg="Assertion failed": (_ for _ in ()).throw(RuntimeError(f"Assertion error: {msg}")) if not sx_truthy(cond) else True
# stdlib.spread — spread + collect + scope primitives
PRIMITIVES["make-spread"] = make_spread
PRIMITIVES["spread?"] = is_spread
PRIMITIVES["spread-attrs"] = spread_attrs
PRIMITIVES["collect!"] = sx_collect
PRIMITIVES["collected"] = sx_collected
PRIMITIVES["clear-collected!"] = sx_clear_collected
# scope — unified render-time dynamic scope
PRIMITIVES["scope-push!"] = scope_push
PRIMITIVES["scope-pop!"] = scope_pop
# provide-push!/provide-pop! — aliases for scope-push!/scope-pop!
PRIMITIVES["provide-push!"] = provide_push
PRIMITIVES["provide-pop!"] = provide_pop
PRIMITIVES["context"] = sx_context
PRIMITIVES["emit!"] = sx_emit
PRIMITIVES["emitted"] = sx_emitted
def is_primitive(name):
if name in PRIMITIVES:
return True
from shared.sx.primitives import get_primitive as _ext_get
return _ext_get(name) is not None
def get_primitive(name):
p = PRIMITIVES.get(name)
if p is not None:
return p
from shared.sx.primitives import get_primitive as _ext_get
return _ext_get(name)
# Higher-order helpers used by transpiled code
def map(fn, coll):
return [fn(x) for x in coll]
def map_indexed(fn, coll):
return [fn(i, item) for i, item in enumerate(coll)]
def filter(fn, coll):
return [x for x in coll if sx_truthy(fn(x))]
def reduce(fn, init, coll):
acc = init
for item in coll:
acc = fn(acc, item)
return acc
def some(fn, coll):
for item in coll:
r = fn(item)
if sx_truthy(r):
return r
return NIL
def every_p(fn, coll):
for item in coll:
if not sx_truthy(fn(item)):
return False
return True
def for_each(fn, coll):
for item in coll:
fn(item)
return NIL
def for_each_indexed(fn, coll):
for i, item in enumerate(coll):
fn(i, item)
return NIL
def map_dict(fn, d):
return {k: fn(k, v) for k, v in d.items()}
# Dynamic wind support (used by sf-dynamic-wind in eval.sx)
_wind_stack = []
def push_wind_b(before, after):
_wind_stack.append((before, after))
return NIL
def pop_wind_b():
if _wind_stack:
_wind_stack.pop()
return NIL
def call_thunk(f, env):
"""Call a zero-arg function/lambda."""
if is_callable(f) and not is_lambda(f):
return f()
if is_lambda(f):
return trampoline(call_lambda(f, [], env))
return trampoline(eval_expr([f], env))
def dynamic_wind_call(before, body, after, env):
"""Execute dynamic-wind with try/finally for error safety."""
call_thunk(before, env)
push_wind_b(before, after)
try:
result = call_thunk(body, env)
finally:
pop_wind_b()
call_thunk(after, env)
return result
# Aliases used directly by transpiled code
first = PRIMITIVES["first"]
last = PRIMITIVES["last"]
rest = PRIMITIVES["rest"]
nth = PRIMITIVES["nth"]
len = PRIMITIVES["len"]
is_nil = PRIMITIVES["nil?"]
empty_p = PRIMITIVES["empty?"]
contains_p = PRIMITIVES["contains?"]
starts_with_p = PRIMITIVES["starts-with?"]
ends_with_p = PRIMITIVES["ends-with?"]
slice = PRIMITIVES["slice"]
get = PRIMITIVES["get"]
append = PRIMITIVES["append"]
cons = PRIMITIVES["cons"]
keys = PRIMITIVES["keys"]
join = PRIMITIVES["join"]
range = PRIMITIVES["range"]
apply = lambda f, args: f(*args)
assoc = PRIMITIVES["assoc"]
concat = PRIMITIVES["concat"]
split = PRIMITIVES["split"]
length = PRIMITIVES["len"]
merge = PRIMITIVES["merge"]
trim = PRIMITIVES["trim"]
replace = PRIMITIVES["replace"]
parse_int = PRIMITIVES["parse-int"]
upper = PRIMITIVES["upper"]
has_key_p = PRIMITIVES["has-key?"]
dict_p = PRIMITIVES["dict?"]
boolean_p = PRIMITIVES["boolean?"]
symbol_p = PRIMITIVES["symbol?"]
keyword_p = PRIMITIVES["keyword?"]
number_p = PRIMITIVES["number?"]
string_p = PRIMITIVES["string?"]
list_p = PRIMITIVES["list?"]
dissoc = PRIMITIVES["dissoc"]
index_of = PRIMITIVES["index-of"]
lower = PRIMITIVES["lower"]
char_from_code = PRIMITIVES["char-from-code"]
# =========================================================================
# Platform interface — Parser
# =========================================================================
import re as _re_parser
_IDENT_START_RE = _re_parser.compile(r"[a-zA-Z_~*+\-><=/!?&]")
_IDENT_CHAR_RE = _re_parser.compile(r"[a-zA-Z0-9_~*+\-><=/!?.:&/#,]")
def ident_start_p(ch):
return bool(_IDENT_START_RE.match(ch))
def ident_char_p(ch):
return bool(_IDENT_CHAR_RE.match(ch))
def parse_number(s):
"""Parse a numeric string to int or float."""
try:
if "." in s or "e" in s or "E" in s:
return float(s)
return int(s)
except (ValueError, TypeError):
return float(s)
# Reader macro registry
_reader_macros = {}
def reader_macro_get(name):
return _reader_macros.get(name, NIL)
def reader_macro_set_b(name, handler):
_reader_macros[name] = handler
return NIL
# =========================================================================
# Platform: deps module — component dependency analysis
# =========================================================================
import re as _re
def component_deps(c):
"""Return cached deps list for a component (may be empty)."""
return list(c.deps) if hasattr(c, "deps") and c.deps else []
def component_set_deps(c, deps):
"""Cache deps on a component."""
c.deps = set(deps) if not isinstance(deps, set) else deps
def component_css_classes(c):
"""Return pre-scanned CSS class list for a component."""
return list(c.css_classes) if hasattr(c, "css_classes") and c.css_classes else []
def env_components(env):
"""Placeholder — overridden by transpiled version from deps.sx."""
return [k for k, v in env.items()
if isinstance(v, (Component, Macro))]
def regex_find_all(pattern, source):
"""Return list of capture group 1 matches."""
return [m.group(1) for m in _re.finditer(pattern, source)]
def scan_css_classes(source):
"""Extract CSS class strings from SX source."""
classes = set()
for m in _re.finditer(r':class\s+"([^"]*)"', source):
classes.update(m.group(1).split())
for m in _re.finditer(r':class\s+\(str\s+((?:"[^"]*"\s*)+)\)', source):
for s in _re.findall(r'"([^"]*)"', m.group(1)):
classes.update(s.split())
for m in _re.finditer(r';;\s*@css\s+(.+)', source):
classes.update(m.group(1).split())
return list(classes)
def component_io_refs(c):
"""Return cached IO refs list, or NIL if not yet computed."""
if not hasattr(c, "io_refs") or c.io_refs is None:
return NIL
return list(c.io_refs)
def component_set_io_refs(c, refs):
"""Cache IO refs on a component."""
c.io_refs = set(refs) if not isinstance(refs, set) else refs
# =========================================================================
# Platform: CEK module — explicit CEK machine
# =========================================================================
# Standalone aliases for primitives used by cek.sx / frames.sx
inc = PRIMITIVES["inc"]
dec = PRIMITIVES["dec"]
zip_pairs = PRIMITIVES["zip-pairs"]
continuation_p = PRIMITIVES["continuation?"]
def make_cek_continuation(captured, rest_kont):
"""Create a Continuation storing captured CEK frames as data."""
c = Continuation(lambda v=NIL: v)
c._cek_data = {"captured": captured, "rest-kont": rest_kont}
return c
def continuation_data(c):
"""Return the _cek_data dict from a CEK continuation."""
return getattr(c, '_cek_data', {}) or {}
# =========================================================================
# Platform interface -- Async adapter
# =========================================================================
import contextvars
import inspect as _inspect
from shared.sx.primitives_io import (
IO_PRIMITIVES, RequestContext, execute_io,
)
# Lazy imports to avoid circular dependency (html.py imports sx_ref.py)
_css_class_collector_cv = None
_svg_context_cv = None
def _ensure_html_imports():
global _css_class_collector_cv, _svg_context_cv
if _css_class_collector_cv is None:
from shared.sx.html import css_class_collector, _svg_context
_css_class_collector_cv = css_class_collector
_svg_context_cv = _svg_context
# When True, async_aser expands known components server-side
_expand_components_cv: contextvars.ContextVar[bool] = contextvars.ContextVar(
"_expand_components_ref", default=False
)
class _AsyncThunk:
__slots__ = ("expr", "env", "ctx")
def __init__(self, expr, env, ctx):
self.expr = expr
self.env = env
self.ctx = ctx
def io_primitive_p(name):
return name in IO_PRIMITIVES
def expand_components_p():
return _expand_components_cv.get()
def svg_context_p():
_ensure_html_imports()
return _svg_context_cv.get(False)
def svg_context_set(val):
_ensure_html_imports()
return _svg_context_cv.set(val)
def svg_context_reset(token):
_ensure_html_imports()
_svg_context_cv.reset(token)
def css_class_collect(val):
_ensure_html_imports()
collector = _css_class_collector_cv.get(None)
if collector is not None:
collector.update(str(val).split())
def is_raw_html(x):
return isinstance(x, _RawHTML)
def make_sx_expr(s):
return SxExpr(s)
def is_sx_expr(x):
return isinstance(x, SxExpr)
# Predicate helpers used by adapter-async (these are in PRIMITIVES but
# the bootstrapped code calls them as plain functions)
def string_p(x):
return isinstance(x, str)
def list_p(x):
return isinstance(x, _b_list)
def number_p(x):
return isinstance(x, (int, float)) and not isinstance(x, bool)
def is_async_coroutine(x):
return _inspect.iscoroutine(x)
async def async_await(x):
return await x
async def _async_trampoline(val):
while isinstance(val, _AsyncThunk):
val = await async_eval(val.expr, val.env, val.ctx)
return val
async def async_eval(expr, env, ctx=None):
"""Evaluate with I/O primitives. Entry point for async evaluation."""
if ctx is None:
ctx = RequestContext()
result = await _async_eval_inner(expr, env, ctx)
while isinstance(result, _AsyncThunk):
result = await _async_eval_inner(result.expr, result.env, result.ctx)
return result
async def _async_eval_inner(expr, env, ctx):
"""Intercept I/O primitives, delegate everything else to sync eval."""
if isinstance(expr, list) and expr:
head = expr[0]
if isinstance(head, Symbol) and head.name in IO_PRIMITIVES:
args_list, kwargs = await _parse_io_args(expr[1:], env, ctx)
return await execute_io(head.name, args_list, kwargs, ctx)
is_render = isinstance(expr, list) and is_render_expr(expr)
result = eval_expr(expr, env)
result = trampoline(result)
if is_render and isinstance(result, str):
return _RawHTML(result)
return result
async def _parse_io_args(exprs, env, ctx):
"""Parse and evaluate I/O node args (keyword + positional)."""
from shared.sx.types import Keyword as _Kw
args_list = []
kwargs = {}
i = 0
while i < len(exprs):
item = exprs[i]
if isinstance(item, _Kw) and i + 1 < len(exprs):
kwargs[item.name] = await async_eval(exprs[i + 1], env, ctx)
i += 2
else:
args_list.append(await async_eval(item, env, ctx))
i += 1
return args_list, kwargs
async def async_eval_to_sx(expr, env, ctx=None):
"""Evaluate and produce SX source string (wire format)."""
if ctx is None:
ctx = RequestContext()
result = await async_aser(expr, env, ctx)
if isinstance(result, SxExpr):
return result
if result is None or result is NIL:
return SxExpr("")
if isinstance(result, str):
return SxExpr(result)
return SxExpr(sx_serialize(result))
async def async_eval_slot_to_sx(expr, env, ctx=None):
"""Like async_eval_to_sx but expands component calls server-side."""
if ctx is None:
ctx = RequestContext()
token = _expand_components_cv.set(True)
try:
result = await async_eval_slot_inner(expr, env, ctx)
if isinstance(result, SxExpr):
return result
if result is None or result is NIL:
return SxExpr("")
if isinstance(result, str):
return SxExpr(result)
return SxExpr(sx_serialize(result))
finally:
_expand_components_cv.reset(token)
# === Transpiled from eval ===
# trampoline
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
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:
debug_log('Undefined symbol:', name, 'primitive?:', is_primitive(name))
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
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 == 'deftype')):
return sf_deftype(args, env)
elif sx_truthy((name == 'defeffect')):
return sf_defeffect(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 == 'scope')):
return sf_scope(args, env)
elif sx_truthy((name == 'provide')):
return sf_provide(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
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
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
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
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
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
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
# cond-scheme?
def cond_scheme_p(clauses):
return every_p(lambda c: ((type_of(c) == 'list') if not sx_truthy((type_of(c) == 'list')) else (len(c) == 2)), clauses)
# sf-cond
def sf_cond(args, env):
if sx_truthy(cond_scheme_p(args)):
return sf_cond_scheme(args, env)
else:
return sf_cond_clojure(args, env)
# sf-cond-scheme
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
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
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
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
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
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
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)
if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))):
for binding in bindings:
vname = (symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))
local[vname] = trampoline(eval_expr(nth(binding, 1), local))
else:
i = 0
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)))
for e in slice(body, 0, (len(body) - 1)):
trampoline(eval_expr(e, local))
return make_thunk(last(body), local)
# sf-named-let
def sf_named_let(args, env):
loop_name = symbol_name(first(args))
bindings = nth(args, 1)
body = slice(args, 2)
params = []
inits = []
if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))):
for binding in bindings:
params.append((symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding)))
inits.append(nth(binding, 1))
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
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 (symbol_name(first(p)) if sx_truthy(((type_of(p) == 'list') if not sx_truthy((type_of(p) == 'list')) else ((len(p) == 3) if not sx_truthy((len(p) == 3)) else ((type_of(nth(p, 1)) == 'keyword') if not sx_truthy((type_of(nth(p, 1)) == 'keyword')) else (keyword_name(nth(p, 1)) == 'as'))))) else p)), params_expr)
return make_lambda(param_names, body, env)
# sf-define
def sf_define(args, env):
name_sym = first(args)
has_effects = ((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))
val_idx = (3 if sx_truthy(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))) else 1)
value = trampoline(eval_expr(nth(args, val_idx), 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
if sx_truthy(has_effects):
effects_raw = nth(args, 2)
effect_list = (map(lambda e: (symbol_name(e) if sx_truthy((type_of(e) == 'symbol')) else sx_str(e)), effects_raw) if sx_truthy((type_of(effects_raw) == 'list')) else [sx_str(effects_raw)])
effect_anns = (env_get(env, '*effect-annotations*') if sx_truthy(env_has(env, '*effect-annotations*')) else {})
effect_anns[symbol_name(name_sym)] = effect_list
env['*effect-annotations*'] = effect_anns
return value
# sf-defcomp
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)
param_types = nth(parsed, 2)
affinity = defcomp_kwarg(args, 'affinity', 'auto')
comp = make_component(comp_name, params, has_children, body, env, affinity)
effects = defcomp_kwarg(args, 'effects', NIL)
if sx_truthy(((not sx_truthy(is_nil(param_types))) if not sx_truthy((not sx_truthy(is_nil(param_types)))) else (not sx_truthy(empty_p(keys(param_types)))))):
component_set_param_types(comp, param_types)
if sx_truthy((not sx_truthy(is_nil(effects)))):
effect_list = (map(lambda e: (symbol_name(e) if sx_truthy((type_of(e) == 'symbol')) else sx_str(e)), effects) if sx_truthy((type_of(effects) == 'list')) else [sx_str(effects)])
effect_anns = (env_get(env, '*effect-annotations*') if sx_truthy(env_has(env, '*effect-annotations*')) else {})
effect_anns[symbol_name(name_sym)] = effect_list
env['*effect-annotations*'] = effect_anns
env[symbol_name(name_sym)] = comp
return comp
# defcomp-kwarg
def defcomp_kwarg(args, key, default_):
_cells = {}
end = (len(args) - 1)
_cells['result'] = default_
for i in range(2, end, 1):
if sx_truthy(((type_of(nth(args, i)) == 'keyword') if not sx_truthy((type_of(nth(args, i)) == 'keyword')) else ((keyword_name(nth(args, i)) == key) if not sx_truthy((keyword_name(nth(args, i)) == key)) else ((i + 1) < end)))):
val = nth(args, (i + 1))
_cells['result'] = (keyword_name(val) if sx_truthy((type_of(val) == 'keyword')) else val)
return _cells['result']
# parse-comp-params
def parse_comp_params(params_expr):
_cells = {}
params = []
param_types = {}
_cells['has_children'] = False
_cells['in_key'] = False
for p in params_expr:
if sx_truthy(((type_of(p) == 'list') if not sx_truthy((type_of(p) == 'list')) else ((len(p) == 3) if not sx_truthy((len(p) == 3)) else ((type_of(first(p)) == 'symbol') if not sx_truthy((type_of(first(p)) == 'symbol')) else ((type_of(nth(p, 1)) == 'keyword') if not sx_truthy((type_of(nth(p, 1)) == 'keyword')) else (keyword_name(nth(p, 1)) == 'as')))))):
name = symbol_name(first(p))
ptype = nth(p, 2)
type_val = (symbol_name(ptype) if sx_truthy((type_of(ptype) == 'symbol')) else ptype)
if sx_truthy((not sx_truthy(_cells['has_children']))):
params.append(name)
param_types[name] = type_val
else:
if sx_truthy((type_of(p) == 'symbol')):
name = symbol_name(p)
if sx_truthy((name == '&key')):
_cells['in_key'] = True
elif sx_truthy((name == '&rest')):
_cells['has_children'] = True
elif sx_truthy((name == '&children')):
_cells['has_children'] = True
elif sx_truthy(_cells['has_children']):
NIL
elif sx_truthy(_cells['in_key']):
params.append(name)
else:
params.append(name)
return [params, _cells['has_children'], param_types]
# sf-defisland
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
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):
_cells = {}
params = []
_cells['rest_param'] = NIL
reduce(lambda state, p: (assoc(state, 'in-rest', True) if sx_truthy(((type_of(p) == 'symbol') if not sx_truthy((type_of(p) == 'symbol')) else (symbol_name(p) == '&rest'))) else (_sx_begin(_sx_cell_set(_cells, 'rest_param', (symbol_name(p) if sx_truthy((type_of(p) == 'symbol')) else p)), state) if sx_truthy(get(state, 'in-rest')) else _sx_begin(_sx_append(params, (symbol_name(p) if sx_truthy((type_of(p) == 'symbol')) else p)), state))), {'in-rest': False}, params_expr)
return [params, _cells['rest_param']]
# sf-defstyle
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
# make-type-def
def make_type_def(name, params, body):
return {'name': name, 'params': params, 'body': body}
# normalize-type-body
def normalize_type_body(body):
if sx_truthy(is_nil(body)):
return 'nil'
elif sx_truthy((type_of(body) == 'symbol')):
return symbol_name(body)
elif sx_truthy((type_of(body) == 'string')):
return body
elif sx_truthy((type_of(body) == 'keyword')):
return keyword_name(body)
elif sx_truthy((type_of(body) == 'dict')):
return map_dict(lambda k, v: normalize_type_body(v), body)
elif sx_truthy((type_of(body) == 'list')):
if sx_truthy(empty_p(body)):
return 'any'
else:
head = first(body)
head_name = (symbol_name(head) if sx_truthy((type_of(head) == 'symbol')) else sx_str(head))
if sx_truthy((head_name == 'union')):
return cons('or', map(normalize_type_body, rest(body)))
else:
return cons(head_name, map(normalize_type_body, rest(body)))
else:
return sx_str(body)
# sf-deftype
def sf_deftype(args, env):
name_or_form = first(args)
body_expr = nth(args, 1)
type_name = NIL
type_params = []
if sx_truthy((type_of(name_or_form) == 'symbol')):
type_name = symbol_name(name_or_form)
else:
if sx_truthy((type_of(name_or_form) == 'list')):
type_name = symbol_name(first(name_or_form))
type_params = map(lambda p: (symbol_name(p) if sx_truthy((type_of(p) == 'symbol')) else sx_str(p)), rest(name_or_form))
body = normalize_type_body(body_expr)
registry = (env_get(env, '*type-registry*') if sx_truthy(env_has(env, '*type-registry*')) else {})
registry[type_name] = make_type_def(type_name, type_params, body)
env['*type-registry*'] = registry
return NIL
# sf-defeffect
def sf_defeffect(args, env):
effect_name = (symbol_name(first(args)) if sx_truthy((type_of(first(args)) == 'symbol')) else sx_str(first(args)))
registry = (env_get(env, '*effect-registry*') if sx_truthy(env_has(env, '*effect-registry*')) else [])
if sx_truthy((not sx_truthy(contains_p(registry, effect_name)))):
registry.append(effect_name)
env['*effect-registry*'] = registry
return NIL
# sf-begin
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
def sf_quote(args, env):
if sx_truthy(empty_p(args)):
return NIL
else:
return first(args)
# sf-quasiquote
def sf_quasiquote(args, env):
return qq_expand(first(args), env)
# qq-expand
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
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!
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
def sf_letrec(args, env):
bindings = first(args)
body = rest(args)
local = env_extend(env)
names = []
val_exprs = []
if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))):
for binding in bindings:
vname = (symbol_name(first(binding)) if sx_truthy((type_of(first(binding)) == 'symbol')) else first(binding))
names.append(vname)
val_exprs.append(nth(binding, 1))
local[vname] = NIL
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
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))
return dynamic_wind_call(before, body, after, env)
# sf-scope
def sf_scope(args, env):
_cells = {}
name = trampoline(eval_expr(first(args), env))
rest = slice(args, 1)
val = NIL
body_exprs = NIL
if sx_truthy(((len(rest) >= 2) if not sx_truthy((len(rest) >= 2)) else ((type_of(first(rest)) == 'keyword') if not sx_truthy((type_of(first(rest)) == 'keyword')) else (keyword_name(first(rest)) == 'value')))):
val = trampoline(eval_expr(nth(rest, 1), env))
body_exprs = slice(rest, 2)
else:
body_exprs = rest
scope_push(name, val)
_cells['result'] = NIL
for e in body_exprs:
_cells['result'] = trampoline(eval_expr(e, env))
scope_pop(name)
return _cells['result']
# sf-provide
def sf_provide(args, env):
_cells = {}
name = trampoline(eval_expr(first(args), env))
val = trampoline(eval_expr(nth(args, 1), env))
body_exprs = slice(args, 2)
_cells['result'] = NIL
scope_push(name, val)
for e in body_exprs:
_cells['result'] = trampoline(eval_expr(e, env))
scope_pop(name)
return _cells['result']
# expand-macro
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
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
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
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
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
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
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
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
def ho_for_each(args, env):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
for item in coll:
call_fn(f, [item], env)
return NIL
# === Transpiled from forms (server definition forms) ===
# parse-key-params
def parse_key_params(params_expr):
_cells = {}
params = []
_cells['in_key'] = False
for p in params_expr:
if sx_truthy((type_of(p) == 'symbol')):
name = symbol_name(p)
if sx_truthy((name == '&key')):
_cells['in_key'] = True
elif sx_truthy(_cells['in_key']):
params.append(name)
else:
params.append(name)
return params
# parse-handler-args
def parse_handler_args(args):
_cells = {}
'Parse defhandler args after the name symbol.\n Scans for :keyword value option pairs, then a list (params), then body.\n Returns dict with keys: opts, params, body.'
opts = {}
_cells['params'] = []
_cells['body'] = NIL
_cells['i'] = 0
n = len(args)
_cells['done'] = False
for idx in range(0, n):
if sx_truthy(((not sx_truthy(_cells['done'])) if not sx_truthy((not sx_truthy(_cells['done']))) else (idx == _cells['i']))):
arg = nth(args, idx)
if sx_truthy((type_of(arg) == 'keyword')):
if sx_truthy(((idx + 1) < n)):
val = nth(args, (idx + 1))
opts[keyword_name(arg)] = (keyword_name(val) if sx_truthy((type_of(val) == 'keyword')) else val)
_cells['i'] = (idx + 2)
elif sx_truthy((type_of(arg) == 'list')):
_cells['params'] = parse_key_params(arg)
if sx_truthy(((idx + 1) < n)):
_cells['body'] = nth(args, (idx + 1))
_cells['done'] = True
else:
_cells['body'] = arg
_cells['done'] = True
return {'opts': opts, 'params': _cells['params'], 'body': _cells['body']}
# sf-defhandler
def sf_defhandler(args, env):
name_sym = first(args)
name = symbol_name(name_sym)
parsed = parse_handler_args(rest(args))
opts = get(parsed, 'opts')
params = get(parsed, 'params')
body = get(parsed, 'body')
hdef = make_handler_def(name, params, body, env, opts)
env[sx_str('handler:', name)] = hdef
return hdef
# sf-defquery
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
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
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
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
def stream_chunk_bindings(chunk):
return dissoc(chunk, 'stream-id')
# normalize-binding-key
def normalize_binding_key(key):
return replace(key, '_', '-')
# bind-stream-chunk
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
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) ===
# HTML_TAGS
HTML_TAGS = ['html', 'head', 'body', 'title', 'meta', 'link', 'script', 'style', 'noscript', 'header', 'nav', 'main', 'section', 'article', 'aside', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hgroup', 'div', 'p', 'blockquote', 'pre', 'figure', 'figcaption', 'address', 'details', 'summary', 'a', 'span', 'em', 'strong', 'small', 'b', 'i', 'u', 's', 'mark', 'sub', 'sup', 'abbr', 'cite', 'code', 'time', 'br', 'wbr', 'hr', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption', 'colgroup', 'col', 'form', 'input', 'textarea', 'select', 'option', 'optgroup', 'button', 'label', 'fieldset', 'legend', 'output', 'datalist', 'img', 'video', 'audio', 'source', 'picture', 'canvas', 'iframe', 'svg', 'math', 'path', 'circle', 'ellipse', 'rect', 'line', 'polyline', 'polygon', 'text', 'tspan', 'g', 'defs', 'use', 'clipPath', 'mask', 'pattern', 'linearGradient', 'radialGradient', 'stop', 'filter', 'feGaussianBlur', 'feOffset', 'feBlend', 'feColorMatrix', 'feComposite', 'feMerge', 'feMergeNode', 'feTurbulence', 'feComponentTransfer', 'feFuncR', 'feFuncG', 'feFuncB', 'feFuncA', 'feDisplacementMap', 'feFlood', 'feImage', 'feMorphology', 'feSpecularLighting', 'feDiffuseLighting', 'fePointLight', 'feSpotLight', 'feDistantLight', 'animate', 'animateTransform', 'foreignObject', 'template', 'slot', 'dialog', 'menu']
# VOID_ELEMENTS
VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']
# BOOLEAN_ATTRS
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?
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') if sx_truthy((name == 'defhandler')) else ((name == 'deftype') if sx_truthy((name == 'deftype')) else (name == 'defeffect'))))))))
# parse-element-args
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
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
def eval_cond(clauses, env):
if sx_truthy(cond_scheme_p(clauses)):
return eval_cond_scheme(clauses, env)
else:
return eval_cond_clojure(clauses, env)
# eval-cond-scheme
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
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
def process_bindings(bindings, env):
local = env_extend(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?
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'))))))))
# merge-spread-attrs
def merge_spread_attrs(target, spread_dict):
for key in keys(spread_dict):
val = dict_get(spread_dict, key)
if sx_truthy((key == 'class')):
existing = dict_get(target, 'class')
target['class'] = (sx_str(existing, ' ', val) if sx_truthy((existing if not sx_truthy(existing) else (not sx_truthy((existing == ''))))) else val)
else:
if sx_truthy((key == 'style')):
existing = dict_get(target, 'style')
target['style'] = (sx_str(existing, ';', val) if sx_truthy((existing if not sx_truthy(existing) else (not sx_truthy((existing == ''))))) else val)
else:
target[key] = val
return NIL
# === Transpiled from parser ===
# sx-parse
def sx_parse(source):
_cells = {}
_cells['pos'] = 0
len_src = len(source)
def skip_comment():
while True:
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (not sx_truthy((nth(source, _cells['pos']) == '\n'))))):
_cells['pos'] = (_cells['pos'] + 1)
continue
break
def skip_ws():
while True:
if sx_truthy((_cells['pos'] < len_src)):
ch = nth(source, _cells['pos'])
if sx_truthy(((ch == ' ') if sx_truthy((ch == ' ')) else ((ch == '\t') if sx_truthy((ch == '\t')) else ((ch == '\n') if sx_truthy((ch == '\n')) else (ch == '\r'))))):
_cells['pos'] = (_cells['pos'] + 1)
continue
elif sx_truthy((ch == ';')):
_cells['pos'] = (_cells['pos'] + 1)
skip_comment()
continue
else:
break
break
def hex_digit_value(ch):
return index_of('0123456789abcdef', lower(ch))
def read_string():
_cells['pos'] = (_cells['pos'] + 1)
_cells['buf'] = ''
while True:
if sx_truthy((_cells['pos'] >= len_src)):
error('Unterminated string')
break
else:
ch = nth(source, _cells['pos'])
if sx_truthy((ch == '"')):
_cells['pos'] = (_cells['pos'] + 1)
break
elif sx_truthy((ch == '\\')):
_cells['pos'] = (_cells['pos'] + 1)
esc = nth(source, _cells['pos'])
if sx_truthy((esc == 'u')):
_cells['pos'] = (_cells['pos'] + 1)
d0 = hex_digit_value(nth(source, _cells['pos']))
_ = _sx_cell_set(_cells, 'pos', (_cells['pos'] + 1))
d1 = hex_digit_value(nth(source, _cells['pos']))
_ = _sx_cell_set(_cells, 'pos', (_cells['pos'] + 1))
d2 = hex_digit_value(nth(source, _cells['pos']))
_ = _sx_cell_set(_cells, 'pos', (_cells['pos'] + 1))
d3 = hex_digit_value(nth(source, _cells['pos']))
_ = _sx_cell_set(_cells, 'pos', (_cells['pos'] + 1))
_cells['buf'] = sx_str(_cells['buf'], char_from_code(((d0 * 4096) + (d1 * 256))))
continue
else:
_cells['buf'] = sx_str(_cells['buf'], ('\n' if sx_truthy((esc == 'n')) else ('\t' if sx_truthy((esc == 't')) else ('\r' if sx_truthy((esc == 'r')) else esc))))
_cells['pos'] = (_cells['pos'] + 1)
continue
else:
_cells['buf'] = sx_str(_cells['buf'], ch)
_cells['pos'] = (_cells['pos'] + 1)
continue
return _cells['buf']
def read_ident():
start = _cells['pos']
while True:
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else ident_char_p(nth(source, _cells['pos'])))):
_cells['pos'] = (_cells['pos'] + 1)
continue
break
return slice(source, start, _cells['pos'])
def read_keyword():
_cells['pos'] = (_cells['pos'] + 1)
return make_keyword(read_ident())
def read_number():
start = _cells['pos']
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (nth(source, _cells['pos']) == '-'))):
_cells['pos'] = (_cells['pos'] + 1)
def read_digits():
while True:
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (lambda c: ((c >= '0') if not sx_truthy((c >= '0')) else (c <= '9')))(nth(source, _cells['pos'])))):
_cells['pos'] = (_cells['pos'] + 1)
continue
break
read_digits()
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (nth(source, _cells['pos']) == '.'))):
_cells['pos'] = (_cells['pos'] + 1)
read_digits()
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else ((nth(source, _cells['pos']) == 'e') if sx_truthy((nth(source, _cells['pos']) == 'e')) else (nth(source, _cells['pos']) == 'E')))):
_cells['pos'] = (_cells['pos'] + 1)
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else ((nth(source, _cells['pos']) == '+') if sx_truthy((nth(source, _cells['pos']) == '+')) else (nth(source, _cells['pos']) == '-')))):
_cells['pos'] = (_cells['pos'] + 1)
read_digits()
return parse_number(slice(source, start, _cells['pos']))
def read_symbol():
name = read_ident()
if sx_truthy((name == 'true')):
return True
elif sx_truthy((name == 'false')):
return False
elif sx_truthy((name == 'nil')):
return NIL
else:
return make_symbol(name)
def read_list(close_ch):
items = []
while True:
skip_ws()
if sx_truthy((_cells['pos'] >= len_src)):
error('Unterminated list')
break
else:
if sx_truthy((nth(source, _cells['pos']) == close_ch)):
_cells['pos'] = (_cells['pos'] + 1)
break
else:
items.append(read_expr())
continue
return items
def read_map():
result = {}
while True:
skip_ws()
if sx_truthy((_cells['pos'] >= len_src)):
error('Unterminated map')
break
else:
if sx_truthy((nth(source, _cells['pos']) == '}')):
_cells['pos'] = (_cells['pos'] + 1)
break
else:
key_expr = read_expr()
key_str = (keyword_name(key_expr) if sx_truthy((type_of(key_expr) == 'keyword')) else sx_str(key_expr))
val_expr = read_expr()
result[key_str] = val_expr
continue
return result
def read_raw_string():
_cells['buf'] = ''
while True:
if sx_truthy((_cells['pos'] >= len_src)):
error('Unterminated raw string')
break
else:
ch = nth(source, _cells['pos'])
if sx_truthy((ch == '|')):
_cells['pos'] = (_cells['pos'] + 1)
break
else:
_cells['buf'] = sx_str(_cells['buf'], ch)
_cells['pos'] = (_cells['pos'] + 1)
continue
return _cells['buf']
def read_expr():
skip_ws()
if sx_truthy((_cells['pos'] >= len_src)):
return error('Unexpected end of input')
else:
ch = nth(source, _cells['pos'])
if sx_truthy((ch == '(')):
_cells['pos'] = (_cells['pos'] + 1)
return read_list(')')
elif sx_truthy((ch == '[')):
_cells['pos'] = (_cells['pos'] + 1)
return read_list(']')
elif sx_truthy((ch == '{')):
_cells['pos'] = (_cells['pos'] + 1)
return read_map()
elif sx_truthy((ch == '"')):
return read_string()
elif sx_truthy((ch == ':')):
return read_keyword()
elif sx_truthy((ch == "'")):
_cells['pos'] = (_cells['pos'] + 1)
return [make_symbol('quote'), read_expr()]
elif sx_truthy((ch == '`')):
_cells['pos'] = (_cells['pos'] + 1)
return [make_symbol('quasiquote'), read_expr()]
elif sx_truthy((ch == ',')):
_cells['pos'] = (_cells['pos'] + 1)
if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (nth(source, _cells['pos']) == '@'))):
_cells['pos'] = (_cells['pos'] + 1)
return [make_symbol('splice-unquote'), read_expr()]
else:
return [make_symbol('unquote'), read_expr()]
elif sx_truthy((ch == '#')):
_cells['pos'] = (_cells['pos'] + 1)
if sx_truthy((_cells['pos'] >= len_src)):
return error('Unexpected end of input after #')
else:
dispatch_ch = nth(source, _cells['pos'])
if sx_truthy((dispatch_ch == ';')):
_cells['pos'] = (_cells['pos'] + 1)
read_expr()
return read_expr()
elif sx_truthy((dispatch_ch == '|')):
_cells['pos'] = (_cells['pos'] + 1)
return read_raw_string()
elif sx_truthy((dispatch_ch == "'")):
_cells['pos'] = (_cells['pos'] + 1)
return [make_symbol('quote'), read_expr()]
elif sx_truthy(ident_start_p(dispatch_ch)):
macro_name = read_ident()
handler = reader_macro_get(macro_name)
if sx_truthy(handler):
return handler(read_expr())
else:
return error(sx_str('Unknown reader macro: #', macro_name))
else:
return error(sx_str('Unknown reader macro: #', dispatch_ch))
elif sx_truthy((((ch >= '0') if not sx_truthy((ch >= '0')) else (ch <= '9')) if sx_truthy(((ch >= '0') if not sx_truthy((ch >= '0')) else (ch <= '9'))) else ((ch == '-') if not sx_truthy((ch == '-')) else (((_cells['pos'] + 1) < len_src) if not sx_truthy(((_cells['pos'] + 1) < len_src)) else (lambda next_ch: ((next_ch >= '0') if not sx_truthy((next_ch >= '0')) else (next_ch <= '9')))(nth(source, (_cells['pos'] + 1))))))):
return read_number()
elif sx_truthy(((ch == '.') if not sx_truthy((ch == '.')) else (((_cells['pos'] + 2) < len_src) if not sx_truthy(((_cells['pos'] + 2) < len_src)) else ((nth(source, (_cells['pos'] + 1)) == '.') if not sx_truthy((nth(source, (_cells['pos'] + 1)) == '.')) else (nth(source, (_cells['pos'] + 2)) == '.'))))):
_cells['pos'] = (_cells['pos'] + 3)
return make_symbol('...')
elif sx_truthy(ident_start_p(ch)):
return read_symbol()
else:
return error(sx_str('Unexpected character: ', ch))
exprs = []
while True:
skip_ws()
if sx_truthy((_cells['pos'] < len_src)):
exprs.append(read_expr())
continue
break
return exprs
# sx-serialize
def sx_serialize(val):
_match = type_of(val)
if _match == 'nil':
return 'nil'
elif _match == 'boolean':
if sx_truthy(val):
return 'true'
else:
return 'false'
elif _match == 'number':
return sx_str(val)
elif _match == 'string':
return sx_str('"', escape_string(val), '"')
elif _match == 'symbol':
return symbol_name(val)
elif _match == 'keyword':
return sx_str(':', keyword_name(val))
elif _match == 'list':
return sx_str('(', join(' ', map(sx_serialize, val)), ')')
elif _match == 'dict':
return sx_serialize_dict(val)
elif _match == 'sx-expr':
return sx_expr_source(val)
elif _match == 'spread':
return sx_str('(make-spread ', sx_serialize_dict(spread_attrs(val)), ')')
else:
return sx_str(val)
# sx-serialize-dict
def sx_serialize_dict(d):
return sx_str('{', join(' ', reduce(lambda acc, key: concat(acc, [sx_str(':', key), sx_serialize(dict_get(d, key))]), [], keys(d))), '}')
# serialize
serialize = sx_serialize
# === Transpiled from adapter-html ===
# render-to-html
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)
elif _match == 'spread':
sx_emit('element-attrs', spread_attrs(expr))
return ''
else:
return render_value_to_html(trampoline(eval_expr(expr, env)), env)
# render-value-to-html
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)
elif _match == 'spread':
sx_emit('element-attrs', spread_attrs(val))
return ''
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', 'deftype', 'defeffect', 'map', 'map-indexed', 'filter', 'for-each', 'scope', 'provide']
# render-html-form?
def is_render_html_form(name):
return contains_p(RENDER_HTML_FORMS, name)
# render-list-to-html
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
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:
if sx_truthy((len(expr) == 3)):
return render_to_html(nth(expr, 2), env)
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)
if sx_truthy((len(expr) == 3)):
return render_to_html(nth(expr, 2), local)
else:
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'))):
if sx_truthy((len(expr) == 2)):
return render_to_html(nth(expr, 1), env)
else:
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))
elif sx_truthy((name == 'scope')):
scope_name = trampoline(eval_expr(nth(expr, 1), env))
rest_args = slice(expr, 2)
scope_val = NIL
body_exprs = NIL
if sx_truthy(((len(rest_args) >= 2) if not sx_truthy((len(rest_args) >= 2)) else ((type_of(first(rest_args)) == 'keyword') if not sx_truthy((type_of(first(rest_args)) == 'keyword')) else (keyword_name(first(rest_args)) == 'value')))):
scope_val = trampoline(eval_expr(nth(rest_args, 1), env))
body_exprs = slice(rest_args, 2)
else:
body_exprs = rest_args
scope_push(scope_name, scope_val)
result = (render_to_html(first(body_exprs), env) if sx_truthy((len(body_exprs) == 1)) else join('', map(lambda e: render_to_html(e, env), body_exprs)))
scope_pop(scope_name)
return result
elif sx_truthy((name == 'provide')):
prov_name = trampoline(eval_expr(nth(expr, 1), env))
prov_val = trampoline(eval_expr(nth(expr, 2), env))
body_start = 3
body_count = (len(expr) - 3)
scope_push(prov_name, prov_val)
result = (render_to_html(nth(expr, body_start), env) if sx_truthy((body_count == 1)) else join('', map(lambda i: render_to_html(nth(expr, i), env), range(body_start, (body_start + body_count)))))
scope_pop(prov_name)
return result
else:
return render_value_to_html(trampoline(eval_expr(expr, env)), env)
# render-lambda-html
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
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
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)
if sx_truthy(is_void):
return sx_str('<', tag, render_attrs(attrs), ' />')
else:
scope_push('element-attrs', NIL)
content = join('', map(lambda c: render_to_html(c, env), children))
for spread_dict in sx_emitted('element-attrs'):
merge_spread_attrs(attrs, spread_dict)
scope_pop('element-attrs')
return sx_str('<', tag, render_attrs(attrs), '>', content, '</', tag, '>')
# render-html-lake
def render_html_lake(args, env):
_cells = {}
_cells['lake_id'] = NIL
_cells['lake_tag'] = 'div'
children = []
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'lake_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'lake_tag', kval) if sx_truthy((kname == 'tag')) else NIL)), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) 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)
lake_attrs = {'data-sx-lake': (_cells['lake_id'] if sx_truthy(_cells['lake_id']) else '')}
scope_push('element-attrs', NIL)
content = join('', map(lambda c: render_to_html(c, env), children))
for spread_dict in sx_emitted('element-attrs'):
merge_spread_attrs(lake_attrs, spread_dict)
scope_pop('element-attrs')
return sx_str('<', _cells['lake_tag'], render_attrs(lake_attrs), '>', content, '</', _cells['lake_tag'], '>')
# render-html-marsh
def render_html_marsh(args, env):
_cells = {}
_cells['marsh_id'] = NIL
_cells['marsh_tag'] = 'div'
children = []
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'marsh_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'marsh_tag', kval) if sx_truthy((kname == 'tag')) else (NIL if sx_truthy((kname == 'transform')) else NIL))), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) 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)
marsh_attrs = {'data-sx-marsh': (_cells['marsh_id'] if sx_truthy(_cells['marsh_id']) else '')}
scope_push('element-attrs', NIL)
content = join('', map(lambda c: render_to_html(c, env), children))
for spread_dict in sx_emitted('element-attrs'):
merge_spread_attrs(marsh_attrs, spread_dict)
scope_pop('element-attrs')
return sx_str('<', _cells['marsh_tag'], render_attrs(marsh_attrs), '>', content, '</', _cells['marsh_tag'], '>')
# render-html-island
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_sx = serialize_island_state(kwargs)
return sx_str('<span data-sx-island="', escape_attr(island_name), '"', (sx_str(' data-sx-state="', escape_attr(state_sx), '"') if sx_truthy(state_sx) else ''), '>', body_html, '</span>')
# serialize-island-state
def serialize_island_state(kwargs):
if sx_truthy(is_empty_dict(kwargs)):
return NIL
else:
return sx_serialize(kwargs)
# === Transpiled from adapter-sx ===
# render-to-sx
def render_to_sx(expr, env):
result = aser(expr, env)
if sx_truthy((type_of(result) == 'string')):
return result
else:
return serialize(result)
# aser
def aser(expr, env):
set_render_active_b(True)
result = _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))), ('spread', lambda: _sx_begin(sx_emit('element-attrs', spread_attrs(expr)), NIL)), (None, lambda: expr)])
if sx_truthy(is_spread(result)):
sx_emit('element-attrs', spread_attrs(result))
return NIL
else:
return result
# aser-list
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
def aser_fragment(children, env):
parts = []
for c in children:
result = aser(c, env)
if sx_truthy((type_of(result) == 'list')):
for item in result:
if sx_truthy((not sx_truthy(is_nil(item)))):
parts.append(serialize(item))
else:
if sx_truthy((not sx_truthy(is_nil(result)))):
parts.append(serialize(result))
if sx_truthy(empty_p(parts)):
return ''
else:
return sx_str('(<> ', join(' ', parts), ')')
# aser-call
def aser_call(name, args, env):
_cells = {}
attr_parts = []
child_parts = []
_cells['skip'] = False
_cells['i'] = 0
scope_push('element-attrs', NIL)
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = aser(nth(args, (_cells['i'] + 1)), env)
if sx_truthy((not sx_truthy(is_nil(val)))):
attr_parts.append(sx_str(':', keyword_name(arg)))
attr_parts.append(serialize(val))
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
val = aser(arg, env)
if sx_truthy((not sx_truthy(is_nil(val)))):
if sx_truthy((type_of(val) == 'list')):
for item in val:
if sx_truthy((not sx_truthy(is_nil(item)))):
child_parts.append(serialize(item))
else:
child_parts.append(serialize(val))
_cells['i'] = (_cells['i'] + 1)
for spread_dict in sx_emitted('element-attrs'):
for k in keys(spread_dict):
v = dict_get(spread_dict, k)
attr_parts.append(sx_str(':', k))
attr_parts.append(serialize(v))
scope_pop('element-attrs')
parts = concat([name], attr_parts, child_parts)
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', 'deftype', 'defeffect', 'scope', 'provide']
# HO_FORM_NAMES
HO_FORM_NAMES = ['map', 'map-indexed', 'filter', 'reduce', 'some', 'every?', 'for-each']
# special-form?
def is_special_form(name):
return contains_p(SPECIAL_FORM_NAMES, name)
# ho-form?
def is_ho_form(name):
return contains_p(HO_FORM_NAMES, name)
# aser-special
def aser_special(name, expr, env):
_cells = {}
args = rest(expr)
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)
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)
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 cek_call(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 cek_call(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:
cek_call(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') if sx_truthy((name == 'defrelation')) else ((name == 'deftype') if sx_truthy((name == 'deftype')) else (name == 'defeffect')))))))))))):
trampoline(eval_expr(expr, env))
return NIL
elif sx_truthy((name == 'scope')):
scope_name = trampoline(eval_expr(first(args), env))
rest_args = rest(args)
scope_val = NIL
body_args = NIL
if sx_truthy(((len(rest_args) >= 2) if not sx_truthy((len(rest_args) >= 2)) else ((type_of(first(rest_args)) == 'keyword') if not sx_truthy((type_of(first(rest_args)) == 'keyword')) else (keyword_name(first(rest_args)) == 'value')))):
scope_val = trampoline(eval_expr(nth(rest_args, 1), env))
body_args = slice(rest_args, 2)
else:
body_args = rest_args
scope_push(scope_name, scope_val)
_cells['result'] = NIL
for body in body_args:
_cells['result'] = aser(body, env)
scope_pop(scope_name)
return _cells['result']
elif sx_truthy((name == 'provide')):
prov_name = trampoline(eval_expr(first(args), env))
prov_val = trampoline(eval_expr(nth(args, 1), env))
_cells['result'] = NIL
scope_push(prov_name, prov_val)
for body in slice(args, 2):
_cells['result'] = aser(body, env)
scope_pop(prov_name)
return _cells['result']
else:
return trampoline(eval_expr(expr, env))
# eval-case-aser
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
def scan_refs(node):
refs = []
scan_refs_walk(node, refs)
return refs
# scan-refs-walk
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')):
for item in node:
scan_refs_walk(item, refs)
return NIL
elif sx_truthy((type_of(node) == 'dict')):
for key in keys(node):
scan_refs_walk(dict_get(node, key), refs)
return NIL
else:
return NIL
# transitive-deps-walk
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') if sx_truthy((type_of(val) == 'component')) else (type_of(val) == 'island'))):
for ref in scan_refs(component_body(val)):
transitive_deps_walk(ref, seen, env)
return NIL
elif sx_truthy((type_of(val) == 'macro')):
for ref in scan_refs(macro_body(val)):
transitive_deps_walk(ref, seen, env)
return NIL
else:
return NIL
return NIL
# transitive-deps
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
def compute_all_deps(env):
for name in env_components(env):
val = env_get(env, name)
if sx_truthy(((type_of(val) == 'component') if sx_truthy((type_of(val) == 'component')) else (type_of(val) == 'island'))):
component_set_deps(val, transitive_deps(name, env))
return NIL
# scan-components-from-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
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
def page_component_bundle(page_source, env):
return components_needed(page_source, env)
# page-css-classes
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
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')):
for item in node:
scan_io_refs_walk(item, io_names, refs)
return NIL
elif sx_truthy((type_of(node) == 'dict')):
for key in keys(node):
scan_io_refs_walk(dict_get(node, key), io_names, refs)
return NIL
else:
return NIL
# scan-io-refs
def scan_io_refs(node, io_names):
refs = []
scan_io_refs_walk(node, io_names, refs)
return refs
# transitive-io-refs-walk
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)
for dep in scan_refs(component_body(val)):
transitive_io_refs_walk(dep, seen, all_refs, env, io_names)
return NIL
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)
for dep in scan_refs(macro_body(val)):
transitive_io_refs_walk(dep, seen, all_refs, env, io_names)
return NIL
else:
return NIL
return NIL
# transitive-io-refs
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
def compute_all_io_refs(env, io_names):
for name in env_components(env):
val = env_get(env, name)
if sx_truthy((type_of(val) == 'component')):
component_set_io_refs(val, transitive_io_refs(name, env, io_names))
return NIL
# component-io-refs-cached
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?
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
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
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
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 frames (CEK continuation frames) ===
# make-cek-state
def make_cek_state(control, env, kont):
return {'control': control, 'env': env, 'kont': kont, 'phase': 'eval', 'value': NIL}
# make-cek-value
def make_cek_value(value, env, kont):
return {'control': NIL, 'env': env, 'kont': kont, 'phase': 'continue', 'value': value}
# cek-terminal?
def cek_terminal_p(state):
return ((get(state, 'phase') == 'continue') if not sx_truthy((get(state, 'phase') == 'continue')) else empty_p(get(state, 'kont')))
# cek-control
def cek_control(s):
return get(s, 'control')
# cek-env
def cek_env(s):
return get(s, 'env')
# cek-kont
def cek_kont(s):
return get(s, 'kont')
# cek-phase
def cek_phase(s):
return get(s, 'phase')
# cek-value
def cek_value(s):
return get(s, 'value')
# make-if-frame
def make_if_frame(then_expr, else_expr, env):
return {'type': 'if', 'then': then_expr, 'else': else_expr, 'env': env}
# make-when-frame
def make_when_frame(body_exprs, env):
return {'type': 'when', 'body': body_exprs, 'env': env}
# make-begin-frame
def make_begin_frame(remaining, env):
return {'type': 'begin', 'remaining': remaining, 'env': env}
# make-let-frame
def make_let_frame(name, remaining, body, local):
return {'type': 'let', 'name': name, 'remaining': remaining, 'body': body, 'env': local}
# make-define-frame
def make_define_frame(name, env, has_effects, effect_list):
return {'type': 'define', 'name': name, 'env': env, 'has-effects': has_effects, 'effect-list': effect_list}
# make-set-frame
def make_set_frame(name, env):
return {'type': 'set', 'name': name, 'env': env}
# make-arg-frame
def make_arg_frame(f, evaled, remaining, env, raw_args):
return {'type': 'arg', 'f': f, 'evaled': evaled, 'remaining': remaining, 'env': env, 'raw-args': raw_args}
# make-call-frame
def make_call_frame(f, args, env):
return {'type': 'call', 'f': f, 'args': args, 'env': env}
# make-cond-frame
def make_cond_frame(remaining, env, scheme_p):
return {'type': 'cond', 'remaining': remaining, 'env': env, 'scheme': scheme_p}
# make-case-frame
def make_case_frame(match_val, remaining, env):
return {'type': 'case', 'match-val': match_val, 'remaining': remaining, 'env': env}
# make-thread-frame
def make_thread_frame(remaining, env):
return {'type': 'thread', 'remaining': remaining, 'env': env}
# make-map-frame
def make_map_frame(f, remaining, results, env):
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': False}
# make-map-indexed-frame
def make_map_indexed_frame(f, remaining, results, env):
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': True}
# make-filter-frame
def make_filter_frame(f, remaining, results, current_item, env):
return {'type': 'filter', 'f': f, 'remaining': remaining, 'results': results, 'current-item': current_item, 'env': env}
# make-reduce-frame
def make_reduce_frame(f, remaining, env):
return {'type': 'reduce', 'f': f, 'remaining': remaining, 'env': env}
# make-for-each-frame
def make_for_each_frame(f, remaining, env):
return {'type': 'for-each', 'f': f, 'remaining': remaining, 'env': env}
# make-some-frame
def make_some_frame(f, remaining, env):
return {'type': 'some', 'f': f, 'remaining': remaining, 'env': env}
# make-every-frame
def make_every_frame(f, remaining, env):
return {'type': 'every', 'f': f, 'remaining': remaining, 'env': env}
# make-scope-frame
def make_scope_frame(name, remaining, env):
return {'type': 'scope', 'name': name, 'remaining': remaining, 'env': env}
# make-reset-frame
def make_reset_frame(env):
return {'type': 'reset', 'env': env}
# make-dict-frame
def make_dict_frame(remaining, results, env):
return {'type': 'dict', 'remaining': remaining, 'results': results, 'env': env}
# make-and-frame
def make_and_frame(remaining, env):
return {'type': 'and', 'remaining': remaining, 'env': env}
# make-or-frame
def make_or_frame(remaining, env):
return {'type': 'or', 'remaining': remaining, 'env': env}
# make-dynamic-wind-frame
def make_dynamic_wind_frame(phase, body_thunk, after_thunk, env):
return {'type': 'dynamic-wind', 'phase': phase, 'body-thunk': body_thunk, 'after-thunk': after_thunk, 'env': env}
# make-reactive-reset-frame
def make_reactive_reset_frame(env, update_fn, first_render_p):
return {'type': 'reactive-reset', 'env': env, 'update-fn': update_fn, 'first-render': first_render_p}
# make-deref-frame
def make_deref_frame(env):
return {'type': 'deref', 'env': env}
# frame-type
def frame_type(f):
return get(f, 'type')
# kont-push
def kont_push(frame, kont):
return cons(frame, kont)
# kont-top
def kont_top(kont):
return first(kont)
# kont-pop
def kont_pop(kont):
return rest(kont)
# kont-empty?
def kont_empty_p(kont):
return empty_p(kont)
# kont-capture-to-reset
def kont_capture_to_reset(kont):
def scan(k, captured):
if sx_truthy(empty_p(k)):
return error('shift without enclosing reset')
else:
frame = first(k)
if sx_truthy(((frame_type(frame) == 'reset') if sx_truthy((frame_type(frame) == 'reset')) else (frame_type(frame) == 'reactive-reset'))):
return [captured, rest(k)]
else:
return scan(rest(k), append(captured, [frame]))
return scan(kont, [])
# has-reactive-reset-frame?
def has_reactive_reset_frame_p(kont):
if sx_truthy(empty_p(kont)):
return False
else:
if sx_truthy((frame_type(first(kont)) == 'reactive-reset')):
return True
else:
return has_reactive_reset_frame_p(rest(kont))
# kont-capture-to-reactive-reset
def kont_capture_to_reactive_reset(kont):
def scan(k, captured):
if sx_truthy(empty_p(k)):
return error('reactive deref without enclosing reactive-reset')
else:
frame = first(k)
if sx_truthy((frame_type(frame) == 'reactive-reset')):
return [captured, frame, rest(k)]
else:
return scan(rest(k), append(captured, [frame]))
return scan(kont, [])
# === Transpiled from page-helpers (pure data transformation helpers) ===
# special-form-category-map
special_form_category_map = {'if': 'Control Flow', 'when': 'Control Flow', 'cond': 'Control Flow', 'case': 'Control Flow', 'and': 'Control Flow', 'or': 'Control Flow', 'let': 'Binding', 'let*': 'Binding', 'letrec': 'Binding', 'define': 'Binding', 'set!': 'Binding', 'lambda': 'Functions & Components', 'fn': 'Functions & Components', 'defcomp': 'Functions & Components', 'defmacro': 'Functions & Components', 'begin': 'Sequencing & Threading', 'do': 'Sequencing & Threading', '->': 'Sequencing & Threading', 'quote': 'Quoting', 'quasiquote': 'Quoting', 'reset': 'Continuations', 'shift': 'Continuations', 'dynamic-wind': 'Guards', 'map': 'Higher-Order Forms', 'map-indexed': 'Higher-Order Forms', 'filter': 'Higher-Order Forms', 'reduce': 'Higher-Order Forms', 'some': 'Higher-Order Forms', 'every?': 'Higher-Order Forms', 'for-each': 'Higher-Order Forms', 'defstyle': 'Domain Definitions', 'defhandler': 'Domain Definitions', 'defpage': 'Domain Definitions', 'defquery': 'Domain Definitions', 'defaction': 'Domain Definitions'}
# extract-define-kwargs
def extract_define_kwargs(expr):
result = {}
items = slice(expr, 2)
n = len(items)
for idx in range(0, n):
if sx_truthy((((idx + 1) < n) if not sx_truthy(((idx + 1) < n)) else (type_of(nth(items, idx)) == 'keyword'))):
key = keyword_name(nth(items, idx))
val = nth(items, (idx + 1))
result[key] = (sx_str('(', join(' ', map(serialize, val)), ')') if sx_truthy((type_of(val) == 'list')) else sx_str(val))
return result
# categorize-special-forms
def categorize_special_forms(parsed_exprs):
categories = {}
for expr in parsed_exprs:
if sx_truthy(((type_of(expr) == 'list') if not sx_truthy((type_of(expr) == 'list')) else ((len(expr) >= 2) if not sx_truthy((len(expr) >= 2)) else ((type_of(first(expr)) == 'symbol') if not sx_truthy((type_of(first(expr)) == 'symbol')) else (symbol_name(first(expr)) == 'define-special-form'))))):
name = nth(expr, 1)
kwargs = extract_define_kwargs(expr)
category = (get(special_form_category_map, name) if sx_truthy(get(special_form_category_map, name)) else 'Other')
if sx_truthy((not sx_truthy(has_key_p(categories, category)))):
categories[category] = []
get(categories, category).append({'name': name, 'syntax': (get(kwargs, 'syntax') if sx_truthy(get(kwargs, 'syntax')) else ''), 'doc': (get(kwargs, 'doc') if sx_truthy(get(kwargs, 'doc')) else ''), 'tail-position': (get(kwargs, 'tail-position') if sx_truthy(get(kwargs, 'tail-position')) else ''), 'example': (get(kwargs, 'example') if sx_truthy(get(kwargs, 'example')) else '')})
return categories
# build-ref-items-with-href
def build_ref_items_with_href(items, base_path, detail_keys, n_fields):
return map(lambda item: ((lambda name: (lambda field2: (lambda field3: {'name': name, 'desc': field2, 'exists': field3, 'href': (sx_str(base_path, name) if sx_truthy((field3 if not sx_truthy(field3) else some(lambda k: (k == name), detail_keys))) else NIL)})(nth(item, 2)))(nth(item, 1)))(nth(item, 0)) if sx_truthy((n_fields == 3)) else (lambda name: (lambda desc: {'name': name, 'desc': desc, 'href': (sx_str(base_path, name) if sx_truthy(some(lambda k: (k == name), detail_keys)) else NIL)})(nth(item, 1)))(nth(item, 0))), items)
# build-reference-data
def build_reference_data(slug, raw_data, detail_keys):
_match = slug
if _match == 'attributes':
return {'req-attrs': build_ref_items_with_href(get(raw_data, 'req-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3), 'beh-attrs': build_ref_items_with_href(get(raw_data, 'beh-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3), 'uniq-attrs': build_ref_items_with_href(get(raw_data, 'uniq-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3)}
elif _match == 'headers':
return {'req-headers': build_ref_items_with_href(get(raw_data, 'req-headers'), '/geography/hypermedia/reference/headers/', detail_keys, 3), 'resp-headers': build_ref_items_with_href(get(raw_data, 'resp-headers'), '/geography/hypermedia/reference/headers/', detail_keys, 3)}
elif _match == 'events':
return {'events-list': build_ref_items_with_href(get(raw_data, 'events-list'), '/geography/hypermedia/reference/events/', detail_keys, 2)}
elif _match == 'js-api':
return {'js-api-list': map(lambda item: {'name': nth(item, 0), 'desc': nth(item, 1)}, get(raw_data, 'js-api-list'))}
else:
return {'req-attrs': build_ref_items_with_href(get(raw_data, 'req-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3), 'beh-attrs': build_ref_items_with_href(get(raw_data, 'beh-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3), 'uniq-attrs': build_ref_items_with_href(get(raw_data, 'uniq-attrs'), '/geography/hypermedia/reference/attributes/', detail_keys, 3)}
# build-attr-detail
def build_attr_detail(slug, detail):
if sx_truthy(is_nil(detail)):
return {'attr-not-found': True}
else:
return {'attr-not-found': NIL, 'attr-title': slug, 'attr-description': get(detail, 'description'), 'attr-example': get(detail, 'example'), 'attr-handler': get(detail, 'handler'), 'attr-demo': get(detail, 'demo'), 'attr-wire-id': (sx_str('ref-wire-', replace(replace(slug, ':', '-'), '*', 'star')) if sx_truthy(has_key_p(detail, 'handler')) else NIL)}
# build-header-detail
def build_header_detail(slug, detail):
if sx_truthy(is_nil(detail)):
return {'header-not-found': True}
else:
return {'header-not-found': NIL, 'header-title': slug, 'header-direction': get(detail, 'direction'), 'header-description': get(detail, 'description'), 'header-example': get(detail, 'example'), 'header-demo': get(detail, 'demo')}
# build-event-detail
def build_event_detail(slug, detail):
if sx_truthy(is_nil(detail)):
return {'event-not-found': True}
else:
return {'event-not-found': NIL, 'event-title': slug, 'event-description': get(detail, 'description'), 'event-example': get(detail, 'example'), 'event-demo': get(detail, 'demo')}
# build-component-source
def build_component_source(comp_data):
comp_type = get(comp_data, 'type')
name = get(comp_data, 'name')
params = get(comp_data, 'params')
has_children = get(comp_data, 'has-children')
body_sx = get(comp_data, 'body-sx')
affinity = get(comp_data, 'affinity')
if sx_truthy((comp_type == 'not-found')):
return sx_str(';; component ', name, ' not found')
else:
param_strs = ((['&rest', 'children'] if sx_truthy(has_children) else []) if sx_truthy(empty_p(params)) else (append(cons('&key', params), ['&rest', 'children']) if sx_truthy(has_children) else cons('&key', params)))
params_sx = sx_str('(', join(' ', param_strs), ')')
form_name = ('defisland' if sx_truthy((comp_type == 'island')) else 'defcomp')
affinity_str = (sx_str(' :affinity ', affinity) if sx_truthy(((comp_type == 'component') if not sx_truthy((comp_type == 'component')) else ((not sx_truthy(is_nil(affinity))) if not sx_truthy((not sx_truthy(is_nil(affinity)))) else (not sx_truthy((affinity == 'auto')))))) else '')
return sx_str('(', form_name, ' ', name, ' ', params_sx, affinity_str, '\n ', body_sx, ')')
# build-bundle-analysis
def build_bundle_analysis(pages_raw, components_raw, total_components, total_macros, pure_count, io_count):
_cells = {}
pages_data = []
for page in pages_raw:
needed_names = get(page, 'needed-names')
n = len(needed_names)
pct = (round(((n / total_components) * 100)) if sx_truthy((total_components > 0)) else 0)
savings = (100 - pct)
_cells['pure_in_page'] = 0
_cells['io_in_page'] = 0
page_io_refs = []
comp_details = []
for comp_name in needed_names:
info = get(components_raw, comp_name)
if sx_truthy((not sx_truthy(is_nil(info)))):
if sx_truthy(get(info, 'is-pure')):
_cells['pure_in_page'] = (_cells['pure_in_page'] + 1)
else:
_cells['io_in_page'] = (_cells['io_in_page'] + 1)
for ref in (get(info, 'io-refs') if sx_truthy(get(info, 'io-refs')) else []):
if sx_truthy((not sx_truthy(some(lambda r: (r == ref), page_io_refs)))):
page_io_refs.append(ref)
comp_details.append({'name': comp_name, 'is-pure': get(info, 'is-pure'), 'affinity': get(info, 'affinity'), 'render-target': get(info, 'render-target'), 'io-refs': (get(info, 'io-refs') if sx_truthy(get(info, 'io-refs')) else []), 'deps': (get(info, 'deps') if sx_truthy(get(info, 'deps')) else []), 'source': get(info, 'source')})
pages_data.append({'name': get(page, 'name'), 'path': get(page, 'path'), 'direct': get(page, 'direct'), 'needed': n, 'pct': pct, 'savings': savings, 'io-refs': len(page_io_refs), 'pure-in-page': _cells['pure_in_page'], 'io-in-page': _cells['io_in_page'], 'components': comp_details})
return {'pages': pages_data, 'total-components': total_components, 'total-macros': total_macros, 'pure-count': pure_count, 'io-count': io_count}
# build-routing-analysis
def build_routing_analysis(pages_raw):
_cells = {}
pages_data = []
_cells['client_count'] = 0
_cells['server_count'] = 0
for page in pages_raw:
has_data = get(page, 'has-data')
content_src = (get(page, 'content-src') if sx_truthy(get(page, 'content-src')) else '')
_cells['mode'] = NIL
_cells['reason'] = ''
if sx_truthy(has_data):
_cells['mode'] = 'server'
_cells['reason'] = 'Has :data expression — needs server IO'
_cells['server_count'] = (_cells['server_count'] + 1)
elif sx_truthy(empty_p(content_src)):
_cells['mode'] = 'server'
_cells['reason'] = 'No content expression'
_cells['server_count'] = (_cells['server_count'] + 1)
else:
_cells['mode'] = 'client'
_cells['client_count'] = (_cells['client_count'] + 1)
pages_data.append({'name': get(page, 'name'), 'path': get(page, 'path'), 'mode': _cells['mode'], 'has-data': has_data, 'content-expr': (sx_str(slice(content_src, 0, 80), '...') if sx_truthy((len(content_src) > 80)) else content_src), 'reason': _cells['reason']})
return {'pages': pages_data, 'total-pages': (_cells['client_count'] + _cells['server_count']), 'client-count': _cells['client_count'], 'server-count': _cells['server_count']}
# build-affinity-analysis
def build_affinity_analysis(demo_components, page_plans):
return {'components': demo_components, 'page-plans': page_plans}
# === Transpiled from router (client-side route matching) ===
# split-path-segments
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
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
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 = {}
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
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):
_cells = {}
match_path = ((sx_url_to_path(path) if sx_truthy(sx_url_to_path(path)) else path) if sx_truthy(starts_with_p(path, '/(')) else path)
path_segs = split_path_segments(match_path)
_cells['result'] = NIL
for route in routes:
if sx_truthy(is_nil(_cells['result'])):
params = match_route_segments(path_segs, get(route, 'parsed'))
if sx_truthy((not sx_truthy(is_nil(params)))):
matched = merge(route, {})
matched['params'] = params
_cells['result'] = matched
return _cells['result']
# _fn-to-segment
def _fn_to_segment(name):
_match = name
if _match == 'doc':
return 'docs'
elif _match == 'spec':
return 'specs'
elif _match == 'bootstrapper':
return 'bootstrappers'
elif _match == 'test':
return 'testing'
elif _match == 'example':
return 'examples'
elif _match == 'protocol':
return 'protocols'
elif _match == 'essay':
return 'essays'
elif _match == 'plan':
return 'plans'
elif _match == 'reference-detail':
return 'reference'
else:
return name
# sx-url-to-path
def sx_url_to_path(url):
if sx_truthy((not sx_truthy((starts_with_p(url, '/(') if not sx_truthy(starts_with_p(url, '/(')) else ends_with_p(url, ')'))))):
return NIL
else:
inner = slice(url, 2, (len(url) - 1))
s = replace(replace(replace(inner, '.', '/'), '(', ''), ')', '')
segs = filter(lambda s: (not sx_truthy(empty_p(s))), split(s, '/'))
return sx_str('/', join('/', map(_fn_to_segment, segs)))
# _count-leading-dots
def _count_leading_dots(s):
if sx_truthy(empty_p(s)):
return 0
else:
if sx_truthy(starts_with_p(s, '.')):
return (1 + _count_leading_dots(slice(s, 1)))
else:
return 0
# _strip-trailing-close
def _strip_trailing_close(s):
if sx_truthy(ends_with_p(s, ')')):
return _strip_trailing_close(slice(s, 0, (len(s) - 1)))
else:
return s
# _index-of-safe
def _index_of_safe(s, needle):
idx = index_of(s, needle)
if sx_truthy((is_nil(idx) if sx_truthy(is_nil(idx)) else (idx < 0))):
return NIL
else:
return idx
# _last-index-of
def _last_index_of(s, needle):
idx = _index_of_safe(s, needle)
if sx_truthy(is_nil(idx)):
return NIL
else:
rest_idx = _last_index_of(slice(s, (idx + 1)), needle)
if sx_truthy(is_nil(rest_idx)):
return idx
else:
return ((idx + 1) + rest_idx)
# _pop-sx-url-level
def _pop_sx_url_level(url):
stripped = _strip_trailing_close(url)
close_count = (len(url) - len(_strip_trailing_close(url)))
if sx_truthy((close_count <= 1)):
return '/'
else:
last_dp = _last_index_of(stripped, '.(')
if sx_truthy(is_nil(last_dp)):
return '/'
else:
return sx_str(slice(stripped, 0, last_dp), slice(url, (len(url) - (close_count - 1))))
# _pop-sx-url-levels
def _pop_sx_url_levels(url, n):
if sx_truthy((n <= 0)):
return url
else:
return _pop_sx_url_levels(_pop_sx_url_level(url), (n - 1))
# _split-pos-kw
def _split_pos_kw(tokens, i, pos, kw):
if sx_truthy((i >= len(tokens))):
return {'positional': join('.', pos), 'keywords': kw}
else:
tok = nth(tokens, i)
if sx_truthy(starts_with_p(tok, ':')):
val = (nth(tokens, (i + 1)) if sx_truthy(((i + 1) < len(tokens))) else '')
return _split_pos_kw(tokens, (i + 2), pos, append(kw, [[tok, val]]))
else:
return _split_pos_kw(tokens, (i + 1), append(pos, [tok]), kw)
# _parse-relative-body
def _parse_relative_body(body):
if sx_truthy(empty_p(body)):
return {'positional': '', 'keywords': []}
else:
return _split_pos_kw(split(body, '.'), 0, [], [])
# _extract-innermost
def _extract_innermost(url):
stripped = _strip_trailing_close(url)
suffix = slice(url, len(_strip_trailing_close(url)))
last_dp = _last_index_of(stripped, '.(')
if sx_truthy(is_nil(last_dp)):
return {'before': '/(', 'content': slice(stripped, 2), 'suffix': suffix}
else:
return {'before': slice(stripped, 0, (last_dp + 2)), 'content': slice(stripped, (last_dp + 2)), 'suffix': suffix}
# _find-kw-in-tokens
def _find_kw_in_tokens(tokens, i, kw):
if sx_truthy((i >= len(tokens))):
return NIL
else:
if sx_truthy(((nth(tokens, i) == kw) if not sx_truthy((nth(tokens, i) == kw)) else ((i + 1) < len(tokens)))):
return nth(tokens, (i + 1))
else:
return _find_kw_in_tokens(tokens, (i + 1), kw)
# _find-keyword-value
def _find_keyword_value(content, kw):
return _find_kw_in_tokens(split(content, '.'), 0, kw)
# _replace-kw-in-tokens
def _replace_kw_in_tokens(tokens, i, kw, value):
if sx_truthy((i >= len(tokens))):
return []
else:
if sx_truthy(((nth(tokens, i) == kw) if not sx_truthy((nth(tokens, i) == kw)) else ((i + 1) < len(tokens)))):
return append([kw, value], _replace_kw_in_tokens(tokens, (i + 2), kw, value))
else:
return cons(nth(tokens, i), _replace_kw_in_tokens(tokens, (i + 1), kw, value))
# _set-keyword-in-content
def _set_keyword_in_content(content, kw, value):
current = _find_keyword_value(content, kw)
if sx_truthy(is_nil(current)):
return sx_str(content, '.', kw, '.', value)
else:
return join('.', _replace_kw_in_tokens(split(content, '.'), 0, kw, value))
# _is-delta-value?
def _is_delta_value_p(s):
return ((not sx_truthy(empty_p(s))) if not sx_truthy((not sx_truthy(empty_p(s)))) else ((len(s) > 1) if not sx_truthy((len(s) > 1)) else (starts_with_p(s, '+') if sx_truthy(starts_with_p(s, '+')) else starts_with_p(s, '-'))))
# _apply-delta
def _apply_delta(current_str, delta_str):
cur = parse_int(current_str, NIL)
delta = parse_int(delta_str, NIL)
if sx_truthy((is_nil(cur) if sx_truthy(is_nil(cur)) else is_nil(delta))):
return delta_str
else:
return sx_str((cur + delta))
# _apply-kw-pairs
def _apply_kw_pairs(content, kw_pairs):
if sx_truthy(empty_p(kw_pairs)):
return content
else:
pair = first(kw_pairs)
kw = first(pair)
raw_val = nth(pair, 1)
actual_val = ((lambda current: (raw_val if sx_truthy(is_nil(current)) else _apply_delta(current, raw_val)))(_find_keyword_value(content, kw)) if sx_truthy(_is_delta_value_p(raw_val)) else raw_val)
return _apply_kw_pairs(_set_keyword_in_content(content, kw, actual_val), rest(kw_pairs))
# _apply-keywords-to-url
def _apply_keywords_to_url(url, kw_pairs):
if sx_truthy(empty_p(kw_pairs)):
return url
else:
parts = _extract_innermost(url)
new_content = _apply_kw_pairs(get(parts, 'content'), kw_pairs)
return sx_str(get(parts, 'before'), new_content, get(parts, 'suffix'))
# _normalize-relative
def _normalize_relative(url):
if sx_truthy(starts_with_p(url, '(')):
return url
else:
return sx_str('(', url, ')')
# resolve-relative-url
def resolve_relative_url(current, relative):
canonical = _normalize_relative(relative)
rel_inner = slice(canonical, 1, (len(canonical) - 1))
dots = _count_leading_dots(rel_inner)
body = slice(rel_inner, _count_leading_dots(rel_inner))
if sx_truthy((dots == 0)):
return current
else:
parsed = _parse_relative_body(body)
pos_body = get(parsed, 'positional')
kw_pairs = get(parsed, 'keywords')
after_nav = ((current if sx_truthy(empty_p(pos_body)) else (lambda stripped: (lambda suffix: sx_str(stripped, '.', pos_body, suffix))(slice(current, len(_strip_trailing_close(current)))))(_strip_trailing_close(current))) if sx_truthy((dots == 1)) else (lambda base: (base if sx_truthy(empty_p(pos_body)) else (sx_str('/(', pos_body, ')') if sx_truthy((base == '/')) else (lambda stripped: (lambda suffix: sx_str(stripped, '.(', pos_body, ')', suffix))(slice(base, len(_strip_trailing_close(base)))))(_strip_trailing_close(base)))))(_pop_sx_url_levels(current, (dots - 1))))
return _apply_keywords_to_url(after_nav, kw_pairs)
# relative-sx-url?
def relative_sx_url_p(url):
return ((starts_with_p(url, '(') if not sx_truthy(starts_with_p(url, '(')) else (not sx_truthy(starts_with_p(url, '/(')))) if sx_truthy((starts_with_p(url, '(') if not sx_truthy(starts_with_p(url, '(')) else (not sx_truthy(starts_with_p(url, '/('))))) else starts_with_p(url, '.'))
# _url-special-forms
def _url_special_forms():
return ['!source', '!inspect', '!diff', '!search', '!raw', '!json']
# url-special-form?
def url_special_form_p(name):
return (starts_with_p(name, '!') if not sx_truthy(starts_with_p(name, '!')) else contains_p(_url_special_forms(), name))
# parse-sx-url
def parse_sx_url(url):
if sx_truthy((url == '/')):
return {'type': 'home', 'raw': url}
elif sx_truthy(relative_sx_url_p(url)):
return {'type': 'relative', 'raw': url}
elif sx_truthy((starts_with_p(url, '/(!') if not sx_truthy(starts_with_p(url, '/(!')) else ends_with_p(url, ')'))):
inner = slice(url, 2, (len(url) - 1))
dot_pos = _index_of_safe(inner, '.')
paren_pos = _index_of_safe(inner, '(')
end_pos = (len(inner) if sx_truthy((is_nil(dot_pos) if not sx_truthy(is_nil(dot_pos)) else is_nil(paren_pos))) else (paren_pos if sx_truthy(is_nil(dot_pos)) else (dot_pos if sx_truthy(is_nil(paren_pos)) else min(dot_pos, paren_pos))))
form_name = slice(inner, 0, end_pos)
rest_part = slice(inner, end_pos)
inner_expr = (slice(rest_part, 1) if sx_truthy(starts_with_p(rest_part, '.')) else rest_part)
return {'type': 'special-form', 'form': form_name, 'inner': inner_expr, 'raw': url}
elif sx_truthy((starts_with_p(url, '/(~') if not sx_truthy(starts_with_p(url, '/(~')) else ends_with_p(url, ')'))):
name = slice(url, 2, (len(url) - 1))
return {'type': 'direct-component', 'name': name, 'raw': url}
elif sx_truthy((starts_with_p(url, '/(') if not sx_truthy(starts_with_p(url, '/(')) else ends_with_p(url, ')'))):
return {'type': 'absolute', 'raw': url}
else:
return {'type': 'path', 'raw': url}
# url-special-form-name
def url_special_form_name(url):
parsed = parse_sx_url(url)
if sx_truthy((get(parsed, 'type') == 'special-form')):
return get(parsed, 'form')
else:
return NIL
# url-special-form-inner
def url_special_form_inner(url):
parsed = parse_sx_url(url)
if sx_truthy((get(parsed, 'type') == 'special-form')):
return get(parsed, 'inner')
else:
return NIL
# url-to-expr
def url_to_expr(url_path):
if sx_truthy(((url_path == '/') if sx_truthy((url_path == '/')) else empty_p(url_path))):
return []
else:
trimmed = (slice(url_path, 1) if sx_truthy(starts_with_p(url_path, '/')) else url_path)
sx_source = replace(trimmed, '.', ' ')
exprs = sx_parse(sx_source)
if sx_truthy(empty_p(exprs)):
return []
else:
return first(exprs)
# auto-quote-unknowns
def auto_quote_unknowns(expr, env):
if sx_truthy((not sx_truthy(list_p(expr)))):
return expr
else:
if sx_truthy(empty_p(expr)):
return expr
else:
return cons(first(expr), map(lambda child: (auto_quote_unknowns(child, env) if sx_truthy(list_p(child)) else ((lambda name: (child if sx_truthy((env_has(env, name) if sx_truthy(env_has(env, name)) else (starts_with_p(name, ':') if sx_truthy(starts_with_p(name, ':')) else (starts_with_p(name, '~') if sx_truthy(starts_with_p(name, '~')) else starts_with_p(name, '!'))))) else name))(symbol_name(child)) if sx_truthy((type_of(child) == 'symbol')) else child)), rest(expr)))
# prepare-url-expr
def prepare_url_expr(url_path, env):
expr = url_to_expr(url_path)
if sx_truthy(empty_p(expr)):
return expr
else:
return auto_quote_unknowns(expr, env)
# === Transpiled from cek (explicit CEK machine evaluator) ===
# cek-run
def cek_run(state):
if sx_truthy(cek_terminal_p(state)):
return cek_value(state)
else:
return cek_run(cek_step(state))
# cek-step
def cek_step(state):
if sx_truthy((cek_phase(state) == 'eval')):
return step_eval(state)
else:
return step_continue(state)
# step-eval
def step_eval(state):
expr = cek_control(state)
env = cek_env(state)
kont = cek_kont(state)
_match = type_of(expr)
if _match == 'number':
return make_cek_value(expr, env, kont)
elif _match == 'string':
return make_cek_value(expr, env, kont)
elif _match == 'boolean':
return make_cek_value(expr, env, kont)
elif _match == 'nil':
return make_cek_value(NIL, env, kont)
elif _match == 'symbol':
name = symbol_name(expr)
val = (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)))))))
return make_cek_value(val, env, kont)
elif _match == 'keyword':
return make_cek_value(keyword_name(expr), env, kont)
elif _match == 'dict':
ks = keys(expr)
if sx_truthy(empty_p(ks)):
return make_cek_value({}, env, kont)
else:
first_key = first(ks)
remaining_entries = []
for k in rest(ks):
remaining_entries.append([k, get(expr, k)])
return make_cek_state(get(expr, first_key), env, kont_push(make_dict_frame(remaining_entries, [[first_key]], env), kont))
elif _match == 'list':
if sx_truthy(empty_p(expr)):
return make_cek_value([], env, kont)
else:
return step_eval_list(expr, env, kont)
else:
return make_cek_value(expr, env, kont)
# step-eval-list
def step_eval_list(expr, env, kont):
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')))))):
if sx_truthy(empty_p(expr)):
return make_cek_value([], env, kont)
else:
return make_cek_state(first(expr), env, kont_push(make_map_frame(NIL, rest(expr), [], env), kont))
else:
if sx_truthy((type_of(head) == 'symbol')):
name = symbol_name(head)
if sx_truthy((name == 'if')):
return step_sf_if(args, env, kont)
elif sx_truthy((name == 'when')):
return step_sf_when(args, env, kont)
elif sx_truthy((name == 'cond')):
return step_sf_cond(args, env, kont)
elif sx_truthy((name == 'case')):
return step_sf_case(args, env, kont)
elif sx_truthy((name == 'and')):
return step_sf_and(args, env, kont)
elif sx_truthy((name == 'or')):
return step_sf_or(args, env, kont)
elif sx_truthy((name == 'let')):
return step_sf_let(args, env, kont)
elif sx_truthy((name == 'let*')):
return step_sf_let(args, env, kont)
elif sx_truthy((name == 'lambda')):
return step_sf_lambda(args, env, kont)
elif sx_truthy((name == 'fn')):
return step_sf_lambda(args, env, kont)
elif sx_truthy((name == 'define')):
return step_sf_define(args, env, kont)
elif sx_truthy((name == 'defcomp')):
return make_cek_value(sf_defcomp(args, env), env, kont)
elif sx_truthy((name == 'defisland')):
return make_cek_value(sf_defisland(args, env), env, kont)
elif sx_truthy((name == 'defmacro')):
return make_cek_value(sf_defmacro(args, env), env, kont)
elif sx_truthy((name == 'defstyle')):
return make_cek_value(sf_defstyle(args, env), env, kont)
elif sx_truthy((name == 'defhandler')):
return make_cek_value(sf_defhandler(args, env), env, kont)
elif sx_truthy((name == 'defpage')):
return make_cek_value(sf_defpage(args, env), env, kont)
elif sx_truthy((name == 'defquery')):
return make_cek_value(sf_defquery(args, env), env, kont)
elif sx_truthy((name == 'defaction')):
return make_cek_value(sf_defaction(args, env), env, kont)
elif sx_truthy((name == 'deftype')):
return make_cek_value(sf_deftype(args, env), env, kont)
elif sx_truthy((name == 'defeffect')):
return make_cek_value(sf_defeffect(args, env), env, kont)
elif sx_truthy((name == 'begin')):
return step_sf_begin(args, env, kont)
elif sx_truthy((name == 'do')):
return step_sf_begin(args, env, kont)
elif sx_truthy((name == 'quote')):
return make_cek_value((NIL if sx_truthy(empty_p(args)) else first(args)), env, kont)
elif sx_truthy((name == 'quasiquote')):
return make_cek_value(qq_expand(first(args), env), env, kont)
elif sx_truthy((name == '->')):
return step_sf_thread_first(args, env, kont)
elif sx_truthy((name == 'set!')):
return step_sf_set_b(args, env, kont)
elif sx_truthy((name == 'letrec')):
return make_cek_value(sf_letrec(args, env), env, kont)
elif sx_truthy((name == 'reset')):
return step_sf_reset(args, env, kont)
elif sx_truthy((name == 'shift')):
return step_sf_shift(args, env, kont)
elif sx_truthy((name == 'deref')):
return step_sf_deref(args, env, kont)
elif sx_truthy((name == 'scope')):
return step_sf_scope(args, env, kont)
elif sx_truthy((name == 'provide')):
return step_sf_provide(args, env, kont)
elif sx_truthy((name == 'dynamic-wind')):
return make_cek_value(sf_dynamic_wind(args, env), env, kont)
elif sx_truthy((name == 'map')):
return step_ho_map(args, env, kont)
elif sx_truthy((name == 'map-indexed')):
return step_ho_map_indexed(args, env, kont)
elif sx_truthy((name == 'filter')):
return step_ho_filter(args, env, kont)
elif sx_truthy((name == 'reduce')):
return step_ho_reduce(args, env, kont)
elif sx_truthy((name == 'some')):
return step_ho_some(args, env, kont)
elif sx_truthy((name == 'every?')):
return step_ho_every(args, env, kont)
elif sx_truthy((name == 'for-each')):
return step_ho_for_each(args, env, kont)
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_cek_state(expand_macro(mac, args, env), env, kont)
elif sx_truthy((render_active_p() if not sx_truthy(render_active_p()) else is_render_expr(expr))):
return make_cek_value(render_expr(expr, env), env, kont)
else:
return step_eval_call(head, args, env, kont)
else:
return step_eval_call(head, args, env, kont)
# step-sf-if
def step_sf_if(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_if_frame(nth(args, 1), (nth(args, 2) if sx_truthy((len(args) > 2)) else NIL), env), kont))
# step-sf-when
def step_sf_when(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_when_frame(rest(args), env), kont))
# step-sf-begin
def step_sf_begin(args, env, kont):
if sx_truthy(empty_p(args)):
return make_cek_value(NIL, env, kont)
else:
if sx_truthy((len(args) == 1)):
return make_cek_state(first(args), env, kont)
else:
return make_cek_state(first(args), env, kont_push(make_begin_frame(rest(args), env), kont))
# step-sf-let
def step_sf_let(args, env, kont):
if sx_truthy((type_of(first(args)) == 'symbol')):
return make_cek_value(sf_named_let(args, env), env, kont)
else:
bindings = first(args)
body = rest(args)
local = env_extend(env)
if sx_truthy(empty_p(bindings)):
return step_sf_begin(body, local, kont)
else:
first_binding = (first(bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else [first(bindings), nth(bindings, 1)])
rest_bindings = (rest(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 pairs: _sx_begin(reduce(lambda acc, i: _sx_append(pairs, [nth(bindings, (i * 2)), nth(bindings, ((i * 2) + 1))]), NIL, range(1, (len(bindings) / 2))), pairs))([]))
vname = (symbol_name(first(first_binding)) if sx_truthy((type_of(first(first_binding)) == 'symbol')) else first(first_binding))
return make_cek_state(nth(first_binding, 1), local, kont_push(make_let_frame(vname, rest_bindings, body, local), kont))
# step-sf-define
def step_sf_define(args, env, kont):
name_sym = first(args)
has_effects = ((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))
val_idx = (3 if sx_truthy(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))) else 1)
effect_list = (nth(args, 2) if sx_truthy(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))) else NIL)
return make_cek_state(nth(args, val_idx), env, kont_push(make_define_frame(symbol_name(name_sym), env, has_effects, effect_list), kont))
# step-sf-set!
def step_sf_set_b(args, env, kont):
return make_cek_state(nth(args, 1), env, kont_push(make_set_frame(symbol_name(first(args)), env), kont))
# step-sf-and
def step_sf_and(args, env, kont):
if sx_truthy(empty_p(args)):
return make_cek_value(True, env, kont)
else:
return make_cek_state(first(args), env, kont_push(make_and_frame(rest(args), env), kont))
# step-sf-or
def step_sf_or(args, env, kont):
if sx_truthy(empty_p(args)):
return make_cek_value(False, env, kont)
else:
return make_cek_state(first(args), env, kont_push(make_or_frame(rest(args), env), kont))
# step-sf-cond
def step_sf_cond(args, env, kont):
scheme_p = cond_scheme_p(args)
if sx_truthy(scheme_p):
if sx_truthy(empty_p(args)):
return make_cek_value(NIL, env, kont)
else:
clause = first(args)
test = first(clause)
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_cek_state(nth(clause, 1), env, kont)
else:
return make_cek_state(test, env, kont_push(make_cond_frame(args, env, True), kont))
else:
if sx_truthy((len(args) < 2)):
return make_cek_value(NIL, env, kont)
else:
test = first(args)
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_cek_state(nth(args, 1), env, kont)
else:
return make_cek_state(test, env, kont_push(make_cond_frame(args, env, False), kont))
# step-sf-case
def step_sf_case(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_case_frame(NIL, rest(args), env), kont))
# step-sf-thread-first
def step_sf_thread_first(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_thread_frame(rest(args), env), kont))
# step-sf-lambda
def step_sf_lambda(args, env, kont):
return make_cek_value(sf_lambda(args, env), env, kont)
# step-sf-scope
def step_sf_scope(args, env, kont):
return make_cek_value(sf_scope(args, env), env, kont)
# step-sf-provide
def step_sf_provide(args, env, kont):
return make_cek_value(sf_provide(args, env), env, kont)
# step-sf-reset
def step_sf_reset(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_reset_frame(env), kont))
# step-sf-shift
def step_sf_shift(args, env, kont):
k_name = symbol_name(first(args))
body = nth(args, 1)
captured_result = kont_capture_to_reset(kont)
captured = first(captured_result)
rest_kont = nth(captured_result, 1)
k = make_cek_continuation(captured, rest_kont)
shift_env = env_extend(env)
shift_env[k_name] = k
return make_cek_state(body, shift_env, rest_kont)
# step-sf-deref
def step_sf_deref(args, env, kont):
return make_cek_state(first(args), env, kont_push(make_deref_frame(env), kont))
# cek-call
def cek_call(f, args):
a = ([] if sx_truthy(is_nil(args)) else args)
if sx_truthy(is_nil(f)):
return NIL
elif sx_truthy(is_lambda(f)):
return cek_run(continue_with_call(f, a, {}, a, []))
elif sx_truthy(is_callable(f)):
return apply(f, a)
else:
return NIL
# reactive-shift-deref
def reactive_shift_deref(sig, env, kont):
_cells = {}
scan_result = kont_capture_to_reactive_reset(kont)
captured_frames = first(scan_result)
reset_frame = nth(scan_result, 1)
remaining_kont = nth(scan_result, 2)
update_fn = get(reset_frame, 'update-fn')
_cells['sub_disposers'] = []
subscriber = _sx_fn(lambda : (
for_each(lambda d: cek_call(d, NIL), _cells['sub_disposers']),
_sx_cell_set(_cells, 'sub_disposers', []),
(lambda new_reset: (lambda new_kont: with_island_scope(lambda d: _sx_append(_cells['sub_disposers'], d), lambda : cek_run(make_cek_value(signal_value(sig), env, new_kont))))(concat(captured_frames, [new_reset], remaining_kont)))(make_reactive_reset_frame(env, update_fn, False))
)[-1])
signal_add_sub(sig, subscriber)
register_in_scope(_sx_fn(lambda : (
signal_remove_sub(sig, subscriber),
for_each(lambda d: cek_call(d, NIL), _cells['sub_disposers'])
)[-1]))
initial_kont = concat(captured_frames, [reset_frame], remaining_kont)
return make_cek_value(signal_value(sig), env, initial_kont)
# step-eval-call
def step_eval_call(head, args, env, kont):
return make_cek_state(head, env, kont_push(make_arg_frame(NIL, [], args, env, args), kont))
# step-ho-map
def step_ho_map(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_map_frame(f, rest(coll), [], env), kont))
# step-ho-map-indexed
def step_ho_map_indexed(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [0, first(coll)], env, [], kont_push(make_map_indexed_frame(f, rest(coll), [], env), kont))
# step-ho-filter
def step_ho_filter(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_filter_frame(f, rest(coll), [], first(coll), env), kont))
# step-ho-reduce
def step_ho_reduce(args, env, kont):
f = trampoline(eval_expr(first(args), env))
init = trampoline(eval_expr(nth(args, 1), env))
coll = trampoline(eval_expr(nth(args, 2), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(init, env, kont)
else:
return continue_with_call(f, [init, first(coll)], env, [], kont_push(make_reduce_frame(f, rest(coll), env), kont))
# step-ho-some
def step_ho_some(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(False, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_some_frame(f, rest(coll), env), kont))
# step-ho-every
def step_ho_every(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(True, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_every_frame(f, rest(coll), env), kont))
# step-ho-for-each
def step_ho_for_each(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(NIL, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_for_each_frame(f, rest(coll), env), kont))
# step-continue
def step_continue(state):
value = cek_value(state)
env = cek_env(state)
kont = cek_kont(state)
if sx_truthy(kont_empty_p(kont)):
return state
else:
frame = kont_top(kont)
rest_k = kont_pop(kont)
ft = frame_type(frame)
if sx_truthy((ft == 'if')):
if sx_truthy((value if not sx_truthy(value) else (not sx_truthy(is_nil(value))))):
return make_cek_state(get(frame, 'then'), get(frame, 'env'), rest_k)
else:
if sx_truthy(is_nil(get(frame, 'else'))):
return make_cek_value(NIL, env, rest_k)
else:
return make_cek_state(get(frame, 'else'), get(frame, 'env'), rest_k)
elif sx_truthy((ft == 'when')):
if sx_truthy((value if not sx_truthy(value) else (not sx_truthy(is_nil(value))))):
body = get(frame, 'body')
fenv = get(frame, 'env')
if sx_truthy(empty_p(body)):
return make_cek_value(NIL, fenv, rest_k)
else:
if sx_truthy((len(body) == 1)):
return make_cek_state(first(body), fenv, rest_k)
else:
return make_cek_state(first(body), fenv, kont_push(make_begin_frame(rest(body), fenv), rest_k))
else:
return make_cek_value(NIL, env, rest_k)
elif sx_truthy((ft == 'begin')):
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(value, fenv, rest_k)
else:
if sx_truthy((len(remaining) == 1)):
return make_cek_state(first(remaining), fenv, rest_k)
else:
return make_cek_state(first(remaining), fenv, kont_push(make_begin_frame(rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'let')):
name = get(frame, 'name')
remaining = get(frame, 'remaining')
body = get(frame, 'body')
local = get(frame, 'env')
local[name] = value
if sx_truthy(empty_p(remaining)):
return step_sf_begin(body, local, rest_k)
else:
next_binding = first(remaining)
vname = (symbol_name(first(next_binding)) if sx_truthy((type_of(first(next_binding)) == 'symbol')) else first(next_binding))
return make_cek_state(nth(next_binding, 1), local, kont_push(make_let_frame(vname, rest(remaining), body, local), rest_k))
elif sx_truthy((ft == 'define')):
name = get(frame, 'name')
fenv = get(frame, 'env')
has_effects = get(frame, 'has-effects')
effect_list = get(frame, 'effect-list')
if sx_truthy((is_lambda(value) if not sx_truthy(is_lambda(value)) else is_nil(lambda_name(value)))):
value.name = name
fenv[name] = value
if sx_truthy(has_effects):
effect_names = (map(lambda e: (symbol_name(e) if sx_truthy((type_of(e) == 'symbol')) else sx_str(e)), effect_list) if sx_truthy((type_of(effect_list) == 'list')) else [sx_str(effect_list)])
effect_anns = (env_get(fenv, '*effect-annotations*') if sx_truthy(env_has(fenv, '*effect-annotations*')) else {})
effect_anns[name] = effect_names
fenv['*effect-annotations*'] = effect_anns
return make_cek_value(value, fenv, rest_k)
elif sx_truthy((ft == 'set')):
name = get(frame, 'name')
fenv = get(frame, 'env')
fenv[name] = value
return make_cek_value(value, env, rest_k)
elif sx_truthy((ft == 'and')):
if sx_truthy((not sx_truthy(value))):
return make_cek_value(value, env, rest_k)
else:
remaining = get(frame, 'remaining')
if sx_truthy(empty_p(remaining)):
return make_cek_value(value, env, rest_k)
else:
return make_cek_state(first(remaining), get(frame, 'env'), (rest_k if sx_truthy((len(remaining) == 1)) else kont_push(make_and_frame(rest(remaining), get(frame, 'env')), rest_k)))
elif sx_truthy((ft == 'or')):
if sx_truthy(value):
return make_cek_value(value, env, rest_k)
else:
remaining = get(frame, 'remaining')
if sx_truthy(empty_p(remaining)):
return make_cek_value(False, env, rest_k)
else:
return make_cek_state(first(remaining), get(frame, 'env'), (rest_k if sx_truthy((len(remaining) == 1)) else kont_push(make_or_frame(rest(remaining), get(frame, 'env')), rest_k)))
elif sx_truthy((ft == 'cond')):
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
scheme_p = get(frame, 'scheme')
if sx_truthy(scheme_p):
if sx_truthy(value):
return make_cek_state(nth(first(remaining), 1), fenv, rest_k)
else:
next_clauses = rest(remaining)
if sx_truthy(empty_p(next_clauses)):
return make_cek_value(NIL, fenv, rest_k)
else:
next_clause = first(next_clauses)
next_test = first(next_clause)
if sx_truthy((((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else'))) if sx_truthy(((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else')))) else ((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else')))):
return make_cek_state(nth(next_clause, 1), fenv, rest_k)
else:
return make_cek_state(next_test, fenv, kont_push(make_cond_frame(next_clauses, fenv, True), rest_k))
else:
if sx_truthy(value):
return make_cek_state(nth(remaining, 1), fenv, rest_k)
else:
next = slice(remaining, 2)
if sx_truthy((len(next) < 2)):
return make_cek_value(NIL, fenv, rest_k)
else:
next_test = first(next)
if sx_truthy((((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else')) if sx_truthy(((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else'))) else ((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else'))))):
return make_cek_state(nth(next, 1), fenv, rest_k)
else:
return make_cek_state(next_test, fenv, kont_push(make_cond_frame(next, fenv, False), rest_k))
elif sx_truthy((ft == 'case')):
match_val = get(frame, 'match-val')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(is_nil(match_val)):
return sf_case_step_loop(value, remaining, fenv, rest_k)
else:
return sf_case_step_loop(match_val, remaining, fenv, rest_k)
elif sx_truthy((ft == 'thread')):
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(value, fenv, rest_k)
else:
form = first(remaining)
rest_forms = rest(remaining)
result = ((lambda f: (lambda rargs: (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, fenv)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(cons(value, rargs)))(map(lambda a: trampoline(eval_expr(a, fenv)), rest(form))))(trampoline(eval_expr(first(form), fenv))) if sx_truthy((type_of(form) == 'list')) else (lambda f: (f(value) 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, [value], fenv)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(trampoline(eval_expr(form, fenv))))
if sx_truthy(empty_p(rest_forms)):
return make_cek_value(result, fenv, rest_k)
else:
return make_cek_value(result, fenv, kont_push(make_thread_frame(rest_forms, fenv), rest_k))
elif sx_truthy((ft == 'arg')):
f = get(frame, 'f')
evaled = get(frame, 'evaled')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
raw_args = get(frame, 'raw-args')
if sx_truthy(is_nil(f)):
if sx_truthy(empty_p(remaining)):
return continue_with_call(value, [], fenv, raw_args, rest_k)
else:
return make_cek_state(first(remaining), fenv, kont_push(make_arg_frame(value, [], rest(remaining), fenv, raw_args), rest_k))
else:
new_evaled = append(evaled, [value])
if sx_truthy(empty_p(remaining)):
return continue_with_call(f, new_evaled, fenv, raw_args, rest_k)
else:
return make_cek_state(first(remaining), fenv, kont_push(make_arg_frame(f, new_evaled, rest(remaining), fenv, raw_args), rest_k))
elif sx_truthy((ft == 'dict')):
remaining = get(frame, 'remaining')
results = get(frame, 'results')
fenv = get(frame, 'env')
last_result = last(results)
completed = append(slice(results, 0, (len(results) - 1)), [[first(last_result), value]])
if sx_truthy(empty_p(remaining)):
d = {}
for pair in completed:
d[first(pair)] = nth(pair, 1)
return make_cek_value(d, fenv, rest_k)
else:
next_entry = first(remaining)
return make_cek_state(nth(next_entry, 1), fenv, kont_push(make_dict_frame(rest(remaining), append(completed, [[first(next_entry)]]), fenv), rest_k))
elif sx_truthy((ft == 'reset')):
return make_cek_value(value, env, rest_k)
elif sx_truthy((ft == 'deref')):
val = value
fenv = get(frame, 'env')
if sx_truthy((not sx_truthy(is_signal(val)))):
return make_cek_value(val, fenv, rest_k)
else:
if sx_truthy(has_reactive_reset_frame_p(rest_k)):
return reactive_shift_deref(val, fenv, rest_k)
else:
ctx = sx_context('sx-reactive', NIL)
if sx_truthy(ctx):
dep_list = get(ctx, 'deps')
notify_fn = get(ctx, 'notify')
if sx_truthy((not sx_truthy(contains_p(dep_list, val)))):
dep_list.append(val)
signal_add_sub(val, notify_fn)
return make_cek_value(signal_value(val), fenv, rest_k)
elif sx_truthy((ft == 'reactive-reset')):
update_fn = get(frame, 'update-fn')
first_p = get(frame, 'first-render')
if sx_truthy((update_fn if not sx_truthy(update_fn) else (not sx_truthy(first_p)))):
cek_call(update_fn, [value])
return make_cek_value(value, env, rest_k)
elif sx_truthy((ft == 'scope')):
name = get(frame, 'name')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
scope_pop(name)
return make_cek_value(value, fenv, rest_k)
else:
return make_cek_state(first(remaining), fenv, kont_push(make_scope_frame(name, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'map')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
results = get(frame, 'results')
indexed = get(frame, 'indexed')
fenv = get(frame, 'env')
new_results = append(results, [value])
if sx_truthy(empty_p(remaining)):
return make_cek_value(new_results, fenv, rest_k)
else:
call_args = ([len(new_results), first(remaining)] if sx_truthy(indexed) else [first(remaining)])
next_frame = (make_map_indexed_frame(f, rest(remaining), new_results, fenv) if sx_truthy(indexed) else make_map_frame(f, rest(remaining), new_results, fenv))
return continue_with_call(f, call_args, fenv, [], kont_push(next_frame, rest_k))
elif sx_truthy((ft == 'filter')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
results = get(frame, 'results')
current_item = get(frame, 'current-item')
fenv = get(frame, 'env')
new_results = (append(results, [current_item]) if sx_truthy(value) else results)
if sx_truthy(empty_p(remaining)):
return make_cek_value(new_results, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_filter_frame(f, rest(remaining), new_results, first(remaining), fenv), rest_k))
elif sx_truthy((ft == 'reduce')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(value, fenv, rest_k)
else:
return continue_with_call(f, [value, first(remaining)], fenv, [], kont_push(make_reduce_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'for-each')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(NIL, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_for_each_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'some')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(value):
return make_cek_value(value, fenv, rest_k)
else:
if sx_truthy(empty_p(remaining)):
return make_cek_value(False, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_some_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'every')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy((not sx_truthy(value))):
return make_cek_value(False, fenv, rest_k)
else:
if sx_truthy(empty_p(remaining)):
return make_cek_value(True, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_every_frame(f, rest(remaining), fenv), rest_k))
else:
return error(sx_str('Unknown frame type: ', ft))
# continue-with-call
def continue_with_call(f, args, env, raw_args, kont):
if sx_truthy(continuation_p(f)):
arg = (NIL if sx_truthy(empty_p(args)) else first(args))
cont_data = continuation_data(f)
captured = get(cont_data, 'captured')
rest_k = get(cont_data, 'rest-kont')
return make_cek_value(arg, env, concat(captured, rest_k))
elif 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 make_cek_value(apply(f, args), env, kont)
elif sx_truthy(is_lambda(f)):
params = lambda_params(f)
local = env_merge(lambda_closure(f), 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_cek_state(lambda_body(f), local, kont)
elif sx_truthy((is_component(f) if sx_truthy(is_component(f)) else is_island(f))):
parsed = parse_keyword_args(raw_args, env)
kwargs = first(parsed)
children = nth(parsed, 1)
local = env_merge(component_closure(f), env)
for p in component_params(f):
local[p] = (dict_get(kwargs, p) if sx_truthy(dict_get(kwargs, p)) else NIL)
if sx_truthy(component_has_children(f)):
local['children'] = children
return make_cek_state(component_body(f), local, kont)
else:
return error(sx_str('Not callable: ', inspect(f)))
# sf-case-step-loop
def sf_case_step_loop(match_val, clauses, env, kont):
if sx_truthy((len(clauses) < 2)):
return make_cek_value(NIL, env, kont)
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_cek_state(body, env, kont)
else:
test_val = trampoline(eval_expr(test, env))
if sx_truthy((match_val == test_val)):
return make_cek_state(body, env, kont)
else:
return sf_case_step_loop(match_val, slice(clauses, 2), env, kont)
# eval-expr-cek
def eval_expr_cek(expr, env):
return cek_run(make_cek_state(expr, env, []))
# trampoline-cek
def trampoline_cek(val):
if sx_truthy(is_thunk(val)):
return eval_expr_cek(thunk_expr(val), thunk_env(val))
else:
return val
# primitive-name
def primitive_name(f):
_cells = {}
if sx_truthy(is_lambda(f)):
return lambda_name(f)
else:
_cells['result'] = NIL
names = ['+', '-', '*', '/', '=', '<', '>', '<=', '>=', 'not', 'and', 'or', 'str', 'len', 'first', 'rest', 'nth', 'list', 'cons', 'append', 'map', 'filter', 'reduce', 'for-each', 'some', 'every?', 'get', 'keys', 'dict', 'dict?', 'has-key?', 'assoc', 'empty?', 'nil?', 'number?', 'string?', 'list?', 'type-of', 'identity', 'inc', 'dec', 'mod', 'join', 'split', 'slice', 'contains?', 'starts-with?', 'upper', 'lower', 'trim', 'replace', 'format']
for name in names:
if sx_truthy((is_nil(_cells['result']) if not sx_truthy(is_nil(_cells['result'])) else (is_primitive(name) if not sx_truthy(is_primitive(name)) else is_identical(f, get_primitive(name))))):
_cells['result'] = name
return _cells['result']
# cek-serialize-value
def cek_serialize_value(val):
if sx_truthy(is_nil(val)):
return NIL
elif sx_truthy(number_p(val)):
return val
elif sx_truthy(string_p(val)):
return val
elif sx_truthy(boolean_p(val)):
return val
elif sx_truthy(symbol_p(val)):
return val
elif sx_truthy(keyword_p(val)):
return val
elif sx_truthy(list_p(val)):
return map(cek_serialize_value, val)
elif sx_truthy(is_lambda(val)):
return [make_symbol('lambda'), lambda_params(val), lambda_body(val)]
elif sx_truthy(is_callable(val)):
return [make_symbol('primitive'), (primitive_name(val) if sx_truthy(primitive_name(val)) else '?')]
elif sx_truthy(dict_p(val)):
return cek_serialize_env(val)
else:
return sx_str(val)
# cek-serialize-env
def cek_serialize_env(env):
result = {}
ks = keys(env)
for k in ks:
result[k] = cek_serialize_value(get(env, k))
return result
# cek-serialize-frame
def cek_serialize_frame(frame):
result = {}
ks = keys(frame)
for k in ks:
v = get(frame, k)
result[k] = (v if sx_truthy((k == 'type')) else (v if sx_truthy((k == 'tag')) else (cek_serialize_value(v) if sx_truthy((k == 'f')) else (cek_serialize_env(v) if sx_truthy((k == 'env')) else (map(cek_serialize_value, v) if sx_truthy((k == 'evaled')) else (v if sx_truthy((k == 'remaining')) else (map(cek_serialize_value, v) if sx_truthy((k == 'results')) else (v if sx_truthy((k == 'raw-args')) else (cek_serialize_value(v) if sx_truthy((k == 'current-item')) else (v if sx_truthy((k == 'name')) else (cek_serialize_value(v) if sx_truthy((k == 'update-fn')) else (v if sx_truthy((k == 'first-render')) else cek_serialize_value(v)))))))))))))
return result
# cek-freeze
def cek_freeze(state):
return {'phase': get(state, 'phase'), 'control': get(state, 'control'), 'value': cek_serialize_value(get(state, 'value')), 'env': cek_serialize_env(get(state, 'env')), 'kont': map(cek_serialize_frame, get(state, 'kont'))}
# cek-thaw-value
def cek_thaw_value(val):
if sx_truthy(is_nil(val)):
return NIL
elif sx_truthy(number_p(val)):
return val
elif sx_truthy(string_p(val)):
return val
elif sx_truthy(boolean_p(val)):
return val
elif sx_truthy(symbol_p(val)):
return val
elif sx_truthy(keyword_p(val)):
return val
elif sx_truthy((list_p(val) if not sx_truthy(list_p(val)) else ((not sx_truthy(empty_p(val))) if not sx_truthy((not sx_truthy(empty_p(val)))) else (symbol_p(first(val)) if not sx_truthy(symbol_p(first(val))) else (symbol_name(first(val)) == 'primitive'))))):
return get_primitive(nth(val, 1))
elif sx_truthy((list_p(val) if not sx_truthy(list_p(val)) else ((not sx_truthy(empty_p(val))) if not sx_truthy((not sx_truthy(empty_p(val)))) else (symbol_p(first(val)) if not sx_truthy(symbol_p(first(val))) else (symbol_name(first(val)) == 'lambda'))))):
return make_lambda(nth(val, 1), nth(val, 2), {})
elif sx_truthy(list_p(val)):
return map(cek_thaw_value, val)
elif sx_truthy(dict_p(val)):
return cek_thaw_env(val)
else:
return val
# cek-thaw-env
def cek_thaw_env(frozen_env):
result = make_env()
for k in keys(frozen_env):
result[k] = cek_thaw_value(get(frozen_env, k))
return result
# cek-thaw-frame
def cek_thaw_frame(frozen_frame):
result = {}
ks = keys(frozen_frame)
for k in ks:
v = get(frozen_frame, k)
result[k] = (v if sx_truthy((k == 'type')) else (v if sx_truthy((k == 'tag')) else (cek_thaw_value(v) if sx_truthy((k == 'f')) else (cek_thaw_env(v) if sx_truthy((k == 'env')) else (map(cek_thaw_value, v) if sx_truthy((k == 'evaled')) else (v if sx_truthy((k == 'remaining')) else (map(cek_thaw_value, v) if sx_truthy((k == 'results')) else (v if sx_truthy((k == 'raw-args')) else (cek_thaw_value(v) if sx_truthy((k == 'current-item')) else (v if sx_truthy((k == 'name')) else (cek_thaw_value(v) if sx_truthy((k == 'update-fn')) else (v if sx_truthy((k == 'first-render')) else cek_thaw_value(v)))))))))))))
return result
# cek-thaw
def cek_thaw(frozen):
return {'phase': get(frozen, 'phase'), 'control': get(frozen, 'control'), 'value': cek_thaw_value(get(frozen, 'value')), 'env': cek_thaw_env(get(frozen, 'env')), 'kont': map(cek_thaw_frame, get(frozen, 'kont'))}
# === Transpiled from signals (reactive signal runtime) ===
# make-signal
def make_signal(value):
return {'__signal': True, 'value': value, 'subscribers': [], 'deps': []}
# signal?
def is_signal(x):
return (dict_p(x) if not sx_truthy(dict_p(x)) else has_key_p(x, '__signal'))
# signal-value
def signal_value(s):
return get(s, 'value')
# signal-set-value!
def signal_set_value(s, v):
return _sx_dict_set(s, 'value', v)
# signal-subscribers
def signal_subscribers(s):
return get(s, 'subscribers')
# signal-add-sub!
def signal_add_sub(s, f):
if sx_truthy((not sx_truthy(contains_p(get(s, 'subscribers'), f)))):
return _sx_append(get(s, 'subscribers'), f)
return NIL
# signal-remove-sub!
def signal_remove_sub(s, f):
return _sx_dict_set(s, 'subscribers', filter(lambda sub: (not sx_truthy(is_identical(sub, f))), get(s, 'subscribers')))
# signal-deps
def signal_deps(s):
return get(s, 'deps')
# signal-set-deps!
def signal_set_deps(s, deps):
return _sx_dict_set(s, 'deps', deps)
# signal
def signal(initial_value):
return make_signal(initial_value)
# deref
def deref(s):
if sx_truthy((not sx_truthy(is_signal(s)))):
return s
else:
ctx = sx_context('sx-reactive', NIL)
if sx_truthy(ctx):
dep_list = get(ctx, 'deps')
notify_fn = get(ctx, 'notify')
if sx_truthy((not sx_truthy(contains_p(dep_list, s)))):
dep_list.append(s)
signal_add_sub(s, notify_fn)
return signal_value(s)
# reset!
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!
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
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: _sx_begin(scope_push('sx-reactive', ctx), (lambda new_val: _sx_begin(scope_pop('sx-reactive'), signal_set_deps(s, get(ctx, 'deps')), (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))))(cek_call(compute_fn, NIL))))({'deps': [], 'notify': recompute})
)[-1])
recompute()
register_in_scope(lambda : dispose_computed(s))
return s
# effect
def effect(effect_fn):
_cells = {}
_cells['deps'] = []
_cells['disposed'] = False
_cells['cleanup_fn'] = NIL
run_effect = lambda : (_sx_begin((cek_call(_cells['cleanup_fn'], NIL) if sx_truthy(_cells['cleanup_fn']) else NIL), for_each(lambda dep: signal_remove_sub(dep, run_effect), _cells['deps']), _sx_cell_set(_cells, 'deps', []), (lambda ctx: _sx_begin(scope_push('sx-reactive', ctx), (lambda result: _sx_begin(scope_pop('sx-reactive'), _sx_cell_set(_cells, 'deps', get(ctx, 'deps')), (_sx_cell_set(_cells, 'cleanup_fn', result) if sx_truthy(is_callable(result)) else NIL)))(cek_call(effect_fn, NIL))))({'deps': [], 'notify': run_effect})) if sx_truthy((not sx_truthy(_cells['disposed']))) else NIL)
run_effect()
dispose_fn = _sx_fn(lambda : (
_sx_cell_set(_cells, 'disposed', True),
(cek_call(_cells['cleanup_fn'], NIL) if sx_truthy(_cells['cleanup_fn']) else NIL),
for_each(lambda dep: signal_remove_sub(dep, run_effect), _cells['deps']),
_sx_cell_set(_cells, 'deps', [])
)[-1])
register_in_scope(dispose_fn)
return dispose_fn
# *batch-depth*
_batch_depth = 0
# *batch-queue*
_batch_queue = []
# batch
def batch(thunk):
_batch_depth = (_batch_depth + 1)
cek_call(thunk, NIL)
_batch_depth = (_batch_depth - 1)
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)
for sub in pending:
sub()
return NIL
return NIL
# notify-subscribers
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
def flush_subscribers(s):
for sub in signal_subscribers(s):
sub()
return NIL
# dispose-computed
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
# with-island-scope
def with_island_scope(scope_fn, body_fn):
scope_push('sx-island-scope', scope_fn)
result = body_fn()
scope_pop('sx-island-scope')
return result
# register-in-scope
def register_in_scope(disposable):
collector = sx_context('sx-island-scope', NIL)
if sx_truthy(collector):
return cek_call(collector, [disposable])
return NIL
# with-marsh-scope
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
def dispose_marsh_scope(marsh_el):
disposers = dom_get_data(marsh_el, 'sx-marsh-disposers')
if sx_truthy(disposers):
for d in disposers:
cek_call(d, NIL)
return dom_set_data(marsh_el, 'sx-marsh-disposers', NIL)
return NIL
# *store-registry*
_store_registry = {}
# def-store
def def_store(name, init_fn):
registry = _store_registry
if sx_truthy((not sx_truthy(has_key_p(registry, name)))):
_store_registry = assoc(registry, name, cek_call(init_fn, NIL))
return get(_store_registry, name)
# 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
def emit_event(el, event_name, detail):
return dom_dispatch(el, event_name, detail)
# on-event
def on_event(el, event_name, handler):
return dom_listen(el, event_name, handler)
# bridge-event
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))((cek_call(transform_fn, [detail]) if sx_truthy(transform_fn) else detail)))(event_detail(e)))))
# resource
def resource(fetch_fn):
state = signal({'loading': True, 'data': NIL, 'error': NIL})
promise_then(cek_call(fetch_fn, NIL), lambda data: reset_b(state, {'loading': False, 'data': data, 'error': NIL}), lambda err: reset_b(state, {'loading': False, 'data': NIL, 'error': err}))
return state
# === Transpiled from adapter-async ===
# async-render
async def async_render(expr, env, ctx):
_match = type_of(expr)
if _match == 'nil':
return ''
elif _match == 'boolean':
return ''
elif _match == 'string':
return escape_html(expr)
elif _match == 'number':
return escape_html(sx_str(expr))
elif _match == 'raw-html':
return raw_html_content(expr)
elif _match == 'spread':
sx_emit('element-attrs', spread_attrs(expr))
return ''
elif _match == 'symbol':
val = (await async_eval(expr, env, ctx))
return (await async_render(val, env, ctx))
elif _match == 'keyword':
return escape_html(keyword_name(expr))
elif _match == 'list':
if sx_truthy(empty_p(expr)):
return ''
else:
return (await async_render_list(expr, env, ctx))
elif _match == 'dict':
return ''
else:
return escape_html(sx_str(expr))
# async-render-list
async def async_render_list(expr, env, ctx):
head = first(expr)
if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
if sx_truthy((is_lambda(head) if sx_truthy(is_lambda(head)) else (type_of(head) == 'list'))):
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
else:
return join('', (await async_map_render(expr, env, ctx)))
else:
name = symbol_name(head)
args = rest(expr)
if sx_truthy(io_primitive_p(name)):
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
elif sx_truthy((name == 'raw!')):
return (await async_render_raw(args, env, ctx))
elif sx_truthy((name == '<>')):
return join('', (await async_map_render(args, env, ctx)))
elif sx_truthy(starts_with_p(name, 'html:')):
return (await async_render_element(slice(name, 5), args, env, ctx))
elif sx_truthy(async_render_form_p(name)):
if sx_truthy((contains_p(HTML_TAGS, name) if not sx_truthy(contains_p(HTML_TAGS, name)) else (((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword')) if sx_truthy(((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword'))) else svg_context_p()))):
return (await async_render_element(name, args, env, ctx))
else:
return (await dispatch_async_render_form(name, expr, env, ctx))
elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
return (await async_render(trampoline(expand_macro(env_get(env, name), args, env)), env, ctx))
elif sx_truthy(contains_p(HTML_TAGS, name)):
return (await async_render_element(name, args, env, ctx))
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 (await async_render_island(env_get(env, name), args, env, ctx))
elif sx_truthy(starts_with_p(name, '~')):
val = (env_get(env, name) if sx_truthy(env_has(env, name)) else NIL)
if sx_truthy(is_component(val)):
return (await async_render_component(val, args, env, ctx))
elif sx_truthy(is_macro(val)):
return (await async_render(trampoline(expand_macro(val, args, env)), env, ctx))
else:
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
elif sx_truthy(((index_of(name, '-') > 0) if not sx_truthy((index_of(name, '-') > 0)) else ((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword')))):
return (await async_render_element(name, args, env, ctx))
elif sx_truthy(svg_context_p()):
return (await async_render_element(name, args, env, ctx))
else:
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
# async-render-raw
async def async_render_raw(args, env, ctx):
parts = []
for arg in args:
val = (await async_eval(arg, env, ctx))
if sx_truthy(is_raw_html(val)):
parts.append(raw_html_content(val))
elif sx_truthy((type_of(val) == 'string')):
parts.append(val)
elif sx_truthy(((not sx_truthy(is_nil(val))) if not sx_truthy((not sx_truthy(is_nil(val)))) else (not sx_truthy((val == False))))):
parts.append(sx_str(val))
return join('', parts)
# async-render-element
async def async_render_element(tag, args, env, ctx):
attrs = {}
children = []
(await async_parse_element_args(args, attrs, children, env, ctx))
class_val = dict_get(attrs, 'class')
if sx_truthy(((not sx_truthy(is_nil(class_val))) if not sx_truthy((not sx_truthy(is_nil(class_val)))) else (not sx_truthy((class_val == False))))):
css_class_collect(sx_str(class_val))
if sx_truthy(contains_p(VOID_ELEMENTS, tag)):
return sx_str('<', tag, render_attrs(attrs), '>')
else:
token = (svg_context_set(True) if sx_truthy(((tag == 'svg') if sx_truthy((tag == 'svg')) else (tag == 'math'))) else NIL)
content_parts = []
scope_push('element-attrs', NIL)
for c in children:
content_parts.append((await async_render(c, env, ctx)))
for spread_dict in sx_emitted('element-attrs'):
merge_spread_attrs(attrs, spread_dict)
scope_pop('element-attrs')
if sx_truthy(token):
svg_context_reset(token)
return sx_str('<', tag, render_attrs(attrs), '>', join('', content_parts), '</', tag, '>')
# async-parse-element-args
async def async_parse_element_args(args, attrs, children, env, ctx):
_cells = {}
_cells['skip'] = False
_cells['i'] = 0
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = (await async_eval(nth(args, (_cells['i'] + 1)), env, ctx))
attrs[keyword_name(arg)] = val
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
children.append(arg)
_cells['i'] = (_cells['i'] + 1)
return NIL
# async-render-component
async def async_render_component(comp, args, env, ctx):
kwargs = {}
children = []
(await async_parse_kw_args(args, kwargs, children, env, ctx))
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)):
parts = []
for c in children:
parts.append((await async_render(c, env, ctx)))
local['children'] = make_raw_html(join('', parts))
return (await async_render(component_body(comp), local, ctx))
# async-render-island
async def async_render_island(island, args, env, ctx):
kwargs = {}
children = []
(await async_parse_kw_args(args, kwargs, children, env, ctx))
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)):
parts = []
for c in children:
parts.append((await async_render(c, env, ctx)))
local['children'] = make_raw_html(join('', parts))
body_html = (await async_render(component_body(island), local, ctx))
state_json = serialize_island_state(kwargs)
return sx_str('<span data-sx-island="', escape_attr(island_name), '"', (sx_str(' data-sx-state="', escape_attr(state_json), '"') if sx_truthy(state_json) else ''), '>', body_html, '</span>')
# async-render-lambda
async def async_render_lambda(f, args, env, ctx):
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 (await async_render(lambda_body(f), local, ctx))
# async-parse-kw-args
async def async_parse_kw_args(args, kwargs, children, env, ctx):
_cells = {}
_cells['skip'] = False
_cells['i'] = 0
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = (await async_eval(nth(args, (_cells['i'] + 1)), env, ctx))
kwargs[keyword_name(arg)] = val
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
children.append(arg)
_cells['i'] = (_cells['i'] + 1)
return NIL
# async-map-render
async def async_map_render(exprs, env, ctx):
results = []
for x in exprs:
results.append((await async_render(x, env, ctx)))
return results
# ASYNC_RENDER_FORMS
ASYNC_RENDER_FORMS = ['if', 'when', 'cond', 'case', 'let', 'let*', 'begin', 'do', 'define', 'defcomp', 'defisland', 'defmacro', 'defstyle', 'defhandler', 'deftype', 'defeffect', 'map', 'map-indexed', 'filter', 'for-each', 'scope', 'provide']
# async-render-form?
def async_render_form_p(name):
return contains_p(ASYNC_RENDER_FORMS, name)
# dispatch-async-render-form
async def dispatch_async_render_form(name, expr, env, ctx):
if sx_truthy((name == 'if')):
cond_val = (await async_eval(nth(expr, 1), env, ctx))
if sx_truthy(cond_val):
return (await async_render(nth(expr, 2), env, ctx))
else:
if sx_truthy((len(expr) > 3)):
return (await async_render(nth(expr, 3), env, ctx))
else:
return ''
elif sx_truthy((name == 'when')):
if sx_truthy((not sx_truthy((await async_eval(nth(expr, 1), env, ctx))))):
return ''
else:
if sx_truthy((len(expr) == 3)):
return (await async_render(nth(expr, 2), env, ctx))
else:
return join('', (await async_map_render(slice(expr, 2), env, ctx)))
elif sx_truthy((name == 'cond')):
clauses = rest(expr)
if sx_truthy(cond_scheme_p(clauses)):
return (await async_render_cond_scheme(clauses, env, ctx))
else:
return (await async_render_cond_clojure(clauses, env, ctx))
elif sx_truthy((name == 'case')):
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
elif sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))):
local = (await async_process_bindings(nth(expr, 1), env, ctx))
if sx_truthy((len(expr) == 3)):
return (await async_render(nth(expr, 2), local, ctx))
else:
return join('', (await async_map_render(slice(expr, 2), local, ctx)))
elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
if sx_truthy((len(expr) == 2)):
return (await async_render(nth(expr, 1), env, ctx))
else:
return join('', (await async_map_render(rest(expr), env, ctx)))
elif sx_truthy(is_definition_form(name)):
(await async_eval(expr, env, ctx))
return ''
elif sx_truthy((name == 'map')):
f = (await async_eval(nth(expr, 1), env, ctx))
coll = (await async_eval(nth(expr, 2), env, ctx))
return join('', (await async_map_fn_render(f, coll, env, ctx)))
elif sx_truthy((name == 'map-indexed')):
f = (await async_eval(nth(expr, 1), env, ctx))
coll = (await async_eval(nth(expr, 2), env, ctx))
return join('', (await async_map_indexed_fn_render(f, coll, env, ctx)))
elif sx_truthy((name == 'filter')):
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
elif sx_truthy((name == 'for-each')):
f = (await async_eval(nth(expr, 1), env, ctx))
coll = (await async_eval(nth(expr, 2), env, ctx))
return join('', (await async_map_fn_render(f, coll, env, ctx)))
elif sx_truthy((name == 'scope')):
scope_name = (await async_eval(nth(expr, 1), env, ctx))
rest_args = slice(expr, 2)
scope_val = NIL
body_exprs = NIL
if sx_truthy(((len(rest_args) >= 2) if not sx_truthy((len(rest_args) >= 2)) else ((type_of(first(rest_args)) == 'keyword') if not sx_truthy((type_of(first(rest_args)) == 'keyword')) else (keyword_name(first(rest_args)) == 'value')))):
scope_val = (await async_eval(nth(rest_args, 1), env, ctx))
body_exprs = slice(rest_args, 2)
else:
body_exprs = rest_args
scope_push(scope_name, scope_val)
result = ((await async_render(first(body_exprs), env, ctx)) if sx_truthy((len(body_exprs) == 1)) else join('', (await async_map_render(body_exprs, env, ctx))))
scope_pop(scope_name)
return result
elif sx_truthy((name == 'provide')):
prov_name = (await async_eval(nth(expr, 1), env, ctx))
prov_val = (await async_eval(nth(expr, 2), env, ctx))
body_start = 3
body_count = (len(expr) - 3)
scope_push(prov_name, prov_val)
result = ((await async_render(nth(expr, body_start), env, ctx)) if sx_truthy((body_count == 1)) else join('', (await async_map_render(slice(expr, body_start), env, ctx))))
scope_pop(prov_name)
return result
else:
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
# async-render-cond-scheme
async def async_render_cond_scheme(clauses, env, ctx):
if sx_truthy(empty_p(clauses)):
return ''
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 (await async_render(body, env, ctx))
else:
if sx_truthy((await async_eval(test, env, ctx))):
return (await async_render(body, env, ctx))
else:
return (await async_render_cond_scheme(rest(clauses), env, ctx))
# async-render-cond-clojure
async def async_render_cond_clojure(clauses, env, ctx):
if sx_truthy((len(clauses) < 2)):
return ''
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 (await async_render(body, env, ctx))
else:
if sx_truthy((await async_eval(test, env, ctx))):
return (await async_render(body, env, ctx))
else:
return (await async_render_cond_clojure(slice(clauses, 2), env, ctx))
# async-process-bindings
async def async_process_bindings(bindings, env, ctx):
local = env_extend(env)
if sx_truthy(((type_of(bindings) == 'list') if not sx_truthy((type_of(bindings) == 'list')) else (not sx_truthy(empty_p(bindings))))):
if sx_truthy((type_of(first(bindings)) == 'list')):
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] = (await async_eval(nth(pair, 1), local, ctx))
else:
(await async_process_bindings_flat(bindings, local, ctx))
return local
# async-process-bindings-flat
async def async_process_bindings_flat(bindings, local, ctx):
_cells = {}
_cells['skip'] = False
_cells['i'] = 0
for item in bindings:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
name = (symbol_name(item) if sx_truthy((type_of(item) == 'symbol')) else sx_str(item))
if sx_truthy(((_cells['i'] + 1) < len(bindings))):
local[name] = (await async_eval(nth(bindings, (_cells['i'] + 1)), local, ctx))
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
return NIL
# async-map-fn-render
async def async_map_fn_render(f, coll, env, ctx):
results = []
for item in coll:
if sx_truthy(is_lambda(f)):
results.append((await async_render_lambda(f, [item], env, ctx)))
else:
r = (await async_invoke(f, item))
results.append((await async_render(r, env, ctx)))
return results
# async-map-indexed-fn-render
async def async_map_indexed_fn_render(f, coll, env, ctx):
_cells = {}
results = []
_cells['i'] = 0
for item in coll:
if sx_truthy(is_lambda(f)):
results.append((await async_render_lambda(f, [_cells['i'], item], env, ctx)))
else:
r = (await async_invoke(f, _cells['i'], item))
results.append((await async_render(r, env, ctx)))
_cells['i'] = (_cells['i'] + 1)
return results
# async-invoke
async def async_invoke(f, *args):
r = apply(f, args)
if sx_truthy(is_async_coroutine(r)):
return (await async_await(r))
else:
return r
# async-aser
async def async_aser(expr, env, ctx):
t = type_of(expr)
result = NIL
if sx_truthy((t == 'number')):
result = expr
elif sx_truthy((t == 'string')):
result = expr
elif sx_truthy((t == 'boolean')):
result = expr
elif sx_truthy((t == 'nil')):
result = NIL
elif sx_truthy((t == 'symbol')):
name = symbol_name(expr)
result = (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)))))))
elif sx_truthy((t == 'keyword')):
result = keyword_name(expr)
elif sx_truthy((t == 'dict')):
result = (await async_aser_dict(expr, env, ctx))
elif sx_truthy((t == 'spread')):
sx_emit('element-attrs', spread_attrs(expr))
result = NIL
elif sx_truthy((t == 'list')):
result = ([] if sx_truthy(empty_p(expr)) else (await async_aser_list(expr, env, ctx)))
else:
result = expr
if sx_truthy(is_spread(result)):
sx_emit('element-attrs', spread_attrs(result))
return NIL
else:
return result
# async-aser-dict
async def async_aser_dict(expr, env, ctx):
result = {}
for key in keys(expr):
result[key] = (await async_aser(dict_get(expr, key), env, ctx))
return result
# async-aser-list
async def async_aser_list(expr, env, ctx):
head = first(expr)
args = rest(expr)
if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
if sx_truthy((is_lambda(head) if sx_truthy(is_lambda(head)) else (type_of(head) == 'list'))):
return (await async_aser_eval_call(head, args, env, ctx))
else:
return (await async_aser_map_list(expr, env, ctx))
else:
name = symbol_name(head)
if sx_truthy(io_primitive_p(name)):
return (await async_eval(expr, env, ctx))
elif sx_truthy((name == '<>')):
return (await async_aser_fragment(args, env, ctx))
elif sx_truthy((name == 'raw!')):
return (await async_aser_call('raw!', args, env, ctx))
elif sx_truthy(starts_with_p(name, 'html:')):
return (await async_aser_call(slice(name, 5), args, env, ctx))
elif sx_truthy(starts_with_p(name, '~')):
val = (env_get(env, name) if sx_truthy(env_has(env, name)) else NIL)
if sx_truthy(is_macro(val)):
return (await async_aser(trampoline(expand_macro(val, args, env)), env, ctx))
elif sx_truthy((is_component(val) if not sx_truthy(is_component(val)) else (expand_components_p() if sx_truthy(expand_components_p()) else (component_affinity(val) == 'server')))):
return (await async_aser_component(val, args, env, ctx))
else:
return (await async_aser_call(name, args, env, ctx))
elif sx_truthy(async_aser_form_p(name)):
if sx_truthy((contains_p(HTML_TAGS, name) if not sx_truthy(contains_p(HTML_TAGS, name)) else (((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword')) if sx_truthy(((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword'))) else svg_context_p()))):
return (await async_aser_call(name, args, env, ctx))
else:
return (await dispatch_async_aser_form(name, expr, env, ctx))
elif sx_truthy(contains_p(HTML_TAGS, name)):
return (await async_aser_call(name, args, env, ctx))
elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
return (await async_aser(trampoline(expand_macro(env_get(env, name), args, env)), env, ctx))
elif sx_truthy(((index_of(name, '-') > 0) if not sx_truthy((index_of(name, '-') > 0)) else ((len(expr) > 1) if not sx_truthy((len(expr) > 1)) else (type_of(nth(expr, 1)) == 'keyword')))):
return (await async_aser_call(name, args, env, ctx))
elif sx_truthy(svg_context_p()):
return (await async_aser_call(name, args, env, ctx))
else:
return (await async_aser_eval_call(head, args, env, ctx))
# async-aser-eval-call
async def async_aser_eval_call(head, args, env, ctx):
f = (await async_eval(head, env, ctx))
evaled_args = (await async_eval_args(args, env, ctx))
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)))))):
r = apply(f, evaled_args)
if sx_truthy(is_async_coroutine(r)):
return (await async_await(r))
else:
return r
elif sx_truthy(is_lambda(f)):
local = env_merge(lambda_closure(f), env)
for_each_indexed(lambda i, p: _sx_dict_set(local, p, nth(evaled_args, i)), lambda_params(f))
return (await async_aser(lambda_body(f), local, ctx))
elif sx_truthy(is_component(f)):
return (await async_aser_call(sx_str('~', component_name(f)), args, env, ctx))
elif sx_truthy(is_island(f)):
return (await async_aser_call(sx_str('~', component_name(f)), args, env, ctx))
else:
return error(sx_str('Not callable: ', inspect(f)))
# async-eval-args
async def async_eval_args(args, env, ctx):
results = []
for a in args:
results.append((await async_eval(a, env, ctx)))
return results
# async-aser-map-list
async def async_aser_map_list(exprs, env, ctx):
results = []
for x in exprs:
results.append((await async_aser(x, env, ctx)))
return results
# async-aser-fragment
async def async_aser_fragment(children, env, ctx):
parts = []
for c in children:
result = (await async_aser(c, env, ctx))
if sx_truthy((type_of(result) == 'list')):
for item in result:
if sx_truthy((not sx_truthy(is_nil(item)))):
parts.append(serialize(item))
else:
if sx_truthy((not sx_truthy(is_nil(result)))):
parts.append(serialize(result))
if sx_truthy(empty_p(parts)):
return make_sx_expr('')
else:
return make_sx_expr(sx_str('(<> ', join(' ', parts), ')'))
# async-aser-component
async def async_aser_component(comp, args, env, ctx):
kwargs = {}
children = []
(await async_parse_aser_kw_args(args, kwargs, children, env, ctx))
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)):
child_parts = []
for c in children:
result = (await async_aser(c, env, ctx))
if sx_truthy(list_p(result)):
for item in result:
if sx_truthy((not sx_truthy(is_nil(item)))):
child_parts.append(serialize(item))
else:
if sx_truthy((not sx_truthy(is_nil(result)))):
child_parts.append(serialize(result))
local['children'] = make_sx_expr(sx_str('(<> ', join(' ', child_parts), ')'))
return (await async_aser(component_body(comp), local, ctx))
# async-parse-aser-kw-args
async def async_parse_aser_kw_args(args, kwargs, children, env, ctx):
_cells = {}
_cells['skip'] = False
_cells['i'] = 0
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = (await async_aser(nth(args, (_cells['i'] + 1)), env, ctx))
kwargs[keyword_name(arg)] = val
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
children.append(arg)
_cells['i'] = (_cells['i'] + 1)
return NIL
# async-aser-call
async def async_aser_call(name, args, env, ctx):
_cells = {}
token = (svg_context_set(True) if sx_truthy(((name == 'svg') if sx_truthy((name == 'svg')) else (name == 'math'))) else NIL)
attr_parts = []
child_parts = []
_cells['skip'] = False
_cells['i'] = 0
scope_push('element-attrs', NIL)
for arg in args:
if sx_truthy(_cells['skip']):
_cells['skip'] = False
_cells['i'] = (_cells['i'] + 1)
else:
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
val = (await async_aser(nth(args, (_cells['i'] + 1)), env, ctx))
if sx_truthy((not sx_truthy(is_nil(val)))):
attr_parts.append(sx_str(':', keyword_name(arg)))
if sx_truthy((type_of(val) == 'list')):
live = filter(lambda v: (not sx_truthy(is_nil(v))), val)
if sx_truthy(empty_p(live)):
attr_parts.append('nil')
else:
items = map(serialize, live)
if sx_truthy(some(lambda v: is_sx_expr(v), live)):
attr_parts.append(sx_str('(<> ', join(' ', items), ')'))
else:
attr_parts.append(sx_str('(list ', join(' ', items), ')'))
else:
attr_parts.append(serialize(val))
_cells['skip'] = True
_cells['i'] = (_cells['i'] + 1)
else:
result = (await async_aser(arg, env, ctx))
if sx_truthy((not sx_truthy(is_nil(result)))):
if sx_truthy((type_of(result) == 'list')):
for item in result:
if sx_truthy((not sx_truthy(is_nil(item)))):
child_parts.append(serialize(item))
else:
child_parts.append(serialize(result))
_cells['i'] = (_cells['i'] + 1)
for spread_dict in sx_emitted('element-attrs'):
for k in keys(spread_dict):
v = dict_get(spread_dict, k)
attr_parts.append(sx_str(':', k))
attr_parts.append(serialize(v))
scope_pop('element-attrs')
if sx_truthy(token):
svg_context_reset(token)
parts = concat([name], attr_parts, child_parts)
return make_sx_expr(sx_str('(', join(' ', parts), ')'))
# ASYNC_ASER_FORM_NAMES
ASYNC_ASER_FORM_NAMES = ['if', 'when', 'cond', 'case', 'and', 'or', 'let', 'let*', 'lambda', 'fn', 'define', 'defcomp', 'defmacro', 'defstyle', 'defhandler', 'defpage', 'defquery', 'defaction', 'begin', 'do', 'quote', '->', 'set!', 'defisland', 'deftype', 'defeffect', 'scope', 'provide']
# ASYNC_ASER_HO_NAMES
ASYNC_ASER_HO_NAMES = ['map', 'map-indexed', 'filter', 'for-each']
# async-aser-form?
def async_aser_form_p(name):
return (contains_p(ASYNC_ASER_FORM_NAMES, name) if sx_truthy(contains_p(ASYNC_ASER_FORM_NAMES, name)) else contains_p(ASYNC_ASER_HO_NAMES, name))
# dispatch-async-aser-form
async def dispatch_async_aser_form(name, expr, env, ctx):
_cells = {}
args = rest(expr)
if sx_truthy((name == 'if')):
cond_val = (await async_eval(first(args), env, ctx))
if sx_truthy(cond_val):
return (await async_aser(nth(args, 1), env, ctx))
else:
if sx_truthy((len(args) > 2)):
return (await async_aser(nth(args, 2), env, ctx))
else:
return NIL
elif sx_truthy((name == 'when')):
if sx_truthy((not sx_truthy((await async_eval(first(args), env, ctx))))):
return NIL
else:
_cells['result'] = NIL
for body in rest(args):
_cells['result'] = (await async_aser(body, env, ctx))
return _cells['result']
elif sx_truthy((name == 'cond')):
if sx_truthy(cond_scheme_p(args)):
return (await async_aser_cond_scheme(args, env, ctx))
else:
return (await async_aser_cond_clojure(args, env, ctx))
elif sx_truthy((name == 'case')):
match_val = (await async_eval(first(args), env, ctx))
return (await async_aser_case_loop(match_val, rest(args), env, ctx))
elif sx_truthy(((name == 'let') if sx_truthy((name == 'let')) else (name == 'let*'))):
local = (await async_process_bindings(first(args), env, ctx))
_cells['result'] = NIL
for body in rest(args):
_cells['result'] = (await async_aser(body, local, ctx))
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'] = (await async_aser(body, env, ctx))
return _cells['result']
elif sx_truthy((name == 'and')):
_cells['result'] = True
_cells['stop'] = False
for arg in args:
if sx_truthy((not sx_truthy(_cells['stop']))):
_cells['result'] = (await async_eval(arg, env, ctx))
if sx_truthy((not sx_truthy(_cells['result']))):
_cells['stop'] = True
return _cells['result']
elif sx_truthy((name == 'or')):
_cells['result'] = False
_cells['stop'] = False
for arg in args:
if sx_truthy((not sx_truthy(_cells['stop']))):
_cells['result'] = (await async_eval(arg, env, ctx))
if sx_truthy(_cells['result']):
_cells['stop'] = True
return _cells['result']
elif sx_truthy(((name == 'lambda') if sx_truthy((name == 'lambda')) else (name == 'fn'))):
return sf_lambda(args, env)
elif sx_truthy((name == 'quote')):
if sx_truthy(empty_p(args)):
return NIL
else:
return first(args)
elif sx_truthy((name == '->')):
return (await async_aser_thread_first(args, env, ctx))
elif sx_truthy((name == 'set!')):
value = (await async_eval(nth(args, 1), env, ctx))
env[symbol_name(first(args))] = value
return value
elif sx_truthy((name == 'map')):
return (await async_aser_ho_map(args, env, ctx))
elif sx_truthy((name == 'map-indexed')):
return (await async_aser_ho_map_indexed(args, env, ctx))
elif sx_truthy((name == 'filter')):
return (await async_eval(expr, env, ctx))
elif sx_truthy((name == 'for-each')):
return (await async_aser_ho_for_each(args, env, ctx))
elif sx_truthy((name == 'defisland')):
(await async_eval(expr, env, ctx))
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 == 'deftype') if sx_truthy((name == 'deftype')) else (name == 'defeffect'))))))))))):
(await async_eval(expr, env, ctx))
return NIL
elif sx_truthy((name == 'scope')):
scope_name = (await async_eval(first(args), env, ctx))
rest_args = rest(args)
scope_val = NIL
body_args = NIL
if sx_truthy(((len(rest_args) >= 2) if not sx_truthy((len(rest_args) >= 2)) else ((type_of(first(rest_args)) == 'keyword') if not sx_truthy((type_of(first(rest_args)) == 'keyword')) else (keyword_name(first(rest_args)) == 'value')))):
scope_val = (await async_eval(nth(rest_args, 1), env, ctx))
body_args = slice(rest_args, 2)
else:
body_args = rest_args
scope_push(scope_name, scope_val)
_cells['result'] = NIL
for body in body_args:
_cells['result'] = (await async_aser(body, env, ctx))
scope_pop(scope_name)
return _cells['result']
elif sx_truthy((name == 'provide')):
prov_name = (await async_eval(first(args), env, ctx))
prov_val = (await async_eval(nth(args, 1), env, ctx))
_cells['result'] = NIL
scope_push(prov_name, prov_val)
for body in slice(args, 2):
_cells['result'] = (await async_aser(body, env, ctx))
scope_pop(prov_name)
return _cells['result']
else:
return (await async_eval(expr, env, ctx))
# async-aser-cond-scheme
async def async_aser_cond_scheme(clauses, env, ctx):
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 (await async_aser(body, env, ctx))
else:
if sx_truthy((await async_eval(test, env, ctx))):
return (await async_aser(body, env, ctx))
else:
return (await async_aser_cond_scheme(rest(clauses), env, ctx))
# async-aser-cond-clojure
async def async_aser_cond_clojure(clauses, env, ctx):
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 (await async_aser(body, env, ctx))
else:
if sx_truthy((await async_eval(test, env, ctx))):
return (await async_aser(body, env, ctx))
else:
return (await async_aser_cond_clojure(slice(clauses, 2), env, ctx))
# async-aser-case-loop
async def async_aser_case_loop(match_val, clauses, env, ctx):
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 (await async_aser(body, env, ctx))
else:
if sx_truthy((match_val == (await async_eval(test, env, ctx)))):
return (await async_aser(body, env, ctx))
else:
return (await async_aser_case_loop(match_val, slice(clauses, 2), env, ctx))
# async-aser-thread-first
async def async_aser_thread_first(args, env, ctx):
_cells = {}
_cells['result'] = (await async_eval(first(args), env, ctx))
for form in rest(args):
if sx_truthy((type_of(form) == 'list')):
f = (await async_eval(first(form), env, ctx))
fn_args = cons(_cells['result'], (await async_eval_args(rest(form), env, ctx)))
_cells['result'] = (await async_invoke_or_lambda(f, fn_args, env, ctx))
else:
f = (await async_eval(form, env, ctx))
_cells['result'] = (await async_invoke_or_lambda(f, [_cells['result']], env, ctx))
return _cells['result']
# async-invoke-or-lambda
async def async_invoke_or_lambda(f, args, env, ctx):
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)))))):
r = apply(f, args)
if sx_truthy(is_async_coroutine(r)):
return (await async_await(r))
else:
return r
elif sx_truthy(is_lambda(f)):
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 (await async_eval(lambda_body(f), local, ctx))
else:
return error(sx_str('-> form not callable: ', inspect(f)))
# async-aser-ho-map
async def async_aser_ho_map(args, env, ctx):
f = (await async_eval(first(args), env, ctx))
coll = (await async_eval(nth(args, 1), env, ctx))
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((await async_aser(lambda_body(f), local, ctx)))
else:
results.append((await async_invoke(f, item)))
return results
# async-aser-ho-map-indexed
async def async_aser_ho_map_indexed(args, env, ctx):
_cells = {}
f = (await async_eval(first(args), env, ctx))
coll = (await async_eval(nth(args, 1), env, ctx))
results = []
_cells['i'] = 0
for item in coll:
if sx_truthy(is_lambda(f)):
local = env_merge(lambda_closure(f), env)
local[first(lambda_params(f))] = _cells['i']
local[nth(lambda_params(f), 1)] = item
results.append((await async_aser(lambda_body(f), local, ctx)))
else:
results.append((await async_invoke(f, _cells['i'], item)))
_cells['i'] = (_cells['i'] + 1)
return results
# async-aser-ho-for-each
async def async_aser_ho_for_each(args, env, ctx):
f = (await async_eval(first(args), env, ctx))
coll = (await async_eval(nth(args, 1), env, ctx))
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((await async_aser(lambda_body(f), local, ctx)))
else:
results.append((await async_invoke(f, item)))
return results
# async-eval-slot-inner
async def async_eval_slot_inner(expr, env, ctx):
result = NIL
if sx_truthy((list_p(expr) if not sx_truthy(list_p(expr)) else (not sx_truthy(empty_p(expr))))):
head = first(expr)
if sx_truthy(((type_of(head) == 'symbol') if not sx_truthy((type_of(head) == 'symbol')) else starts_with_p(symbol_name(head), '~'))):
name = symbol_name(head)
val = (env_get(env, name) if sx_truthy(env_has(env, name)) else NIL)
if sx_truthy(is_component(val)):
result = (await async_aser_component(val, rest(expr), env, ctx))
else:
result = (await async_maybe_expand_result((await async_aser(expr, env, ctx)), env, ctx))
else:
result = (await async_maybe_expand_result((await async_aser(expr, env, ctx)), env, ctx))
else:
result = (await async_maybe_expand_result((await async_aser(expr, env, ctx)), env, ctx))
if sx_truthy(is_sx_expr(result)):
return result
else:
if sx_truthy(is_nil(result)):
return make_sx_expr('')
else:
if sx_truthy(string_p(result)):
return make_sx_expr(result)
else:
return make_sx_expr(serialize(result))
# async-maybe-expand-result
async def async_maybe_expand_result(result, env, ctx):
raw = (trim(sx_str(result)) if sx_truthy(is_sx_expr(result)) else (trim(result) if sx_truthy(string_p(result)) else NIL))
if sx_truthy((raw if not sx_truthy(raw) else starts_with_p(raw, '(~'))):
parsed = sx_parse(raw)
if sx_truthy((parsed if not sx_truthy(parsed) else (not sx_truthy(empty_p(parsed))))):
return (await async_eval_slot_inner(first(parsed), env, ctx))
else:
return result
else:
return result
# =========================================================================
# Fixups -- wire up render adapter dispatch
# =========================================================================
def _setup_html_adapter():
global _render_expr_fn
_render_expr_fn = lambda expr, env: render_list_to_html(expr, env)
def _setup_sx_adapter():
global _render_expr_fn
_render_expr_fn = lambda expr, env: aser_list(expr, env)
# Wrap aser_call and aser_fragment to return SxExpr
# so serialize() won't double-quote them
_orig_aser_call = None
_orig_aser_fragment = None
def _wrap_aser_outputs():
global aser_call, aser_fragment, _orig_aser_call, _orig_aser_fragment
_orig_aser_call = aser_call
_orig_aser_fragment = aser_fragment
def _aser_call_wrapped(name, args, env):
result = _orig_aser_call(name, args, env)
return SxExpr(result) if isinstance(result, str) else result
def _aser_fragment_wrapped(children, env):
result = _orig_aser_fragment(children, env)
return SxExpr(result) if isinstance(result, str) else result
aser_call = _aser_call_wrapped
aser_fragment = _aser_fragment_wrapped
# Override sf_set_bang to walk the Env scope chain so that (set! var val)
# updates the variable in its defining scope, not just the local copy.
def sf_set_bang(args, env):
name = symbol_name(first(args))
value = trampoline(eval_expr(nth(args, 1), env))
env = _ensure_env(env)
try:
env.set(name, value)
except KeyError:
# Not found in chain — define locally (matches prior behavior)
env[name] = value
return value
# Override recursive cek_run with iterative loop (avoids Python stack overflow)
def cek_run(state):
"""Drive CEK machine to completion (iterative)."""
while not cek_terminal_p(state):
state = cek_step(state)
return cek_value(state)
# CEK is the canonical evaluator — override eval_expr to use it.
# The tree-walk evaluator (eval_expr from eval.sx) is superseded.
_tree_walk_eval_expr = eval_expr
def eval_expr(expr, env):
"""Evaluate expr using the CEK machine."""
return cek_run(make_cek_state(expr, env, []))
# CEK never produces thunks — trampoline becomes identity
_tree_walk_trampoline = trampoline
def trampoline(val):
"""In CEK mode, values are immediate — resolve any legacy thunks."""
if is_thunk(val):
return eval_expr(thunk_expr(val), thunk_env(val))
return val
# =========================================================================
# Public API
# =========================================================================
# Wrap aser outputs to return SxExpr
_wrap_aser_outputs()
# Set HTML as default adapter
_setup_html_adapter()
def evaluate(expr, env=None):
"""Evaluate expr in env and return the result."""
if env is None:
env = _Env()
elif isinstance(env, dict):
env = _Env(env)
result = eval_expr(expr, env)
while is_thunk(result):
result = eval_expr(thunk_expr(result), thunk_env(result))
return result
def render(expr, env=None):
"""Render expr to HTML string."""
global _render_mode
if env is None:
env = {}
try:
_render_mode = True
return render_to_html(expr, env)
finally:
_render_mode = False
def make_env(**kwargs):
"""Create an environment with initial bindings."""
return _Env(dict(kwargs))
def populate_effect_annotations(env, effect_map=None):
"""Populate *effect-annotations* in env from boundary declarations.
If effect_map is provided, use it directly (dict of name -> effects list).
Otherwise, parse boundary.sx via boundary_parser.
"""
if effect_map is None:
from shared.sx.ref.boundary_parser import parse_boundary_effects
effect_map = parse_boundary_effects()
anns = env.get("*effect-annotations*", {})
if not isinstance(anns, dict):
anns = {}
anns.update(effect_map)
env["*effect-annotations*"] = anns
return anns
def check_component_effects(env, comp_name=None):
"""Check effect violations for components in env.
If comp_name is given, check only that component.
Returns list of diagnostic dicts (warnings, not errors).
"""
anns = env.get("*effect-annotations*")
if not anns:
return []
diagnostics = []
names = [comp_name] if comp_name else [k for k in env if isinstance(k, str) and k.startswith("~")]
for name in names:
val = env.get(name)
if val is not None and type_of(val) == "component":
comp_effects = anns.get(name)
if comp_effects is None:
continue # unannotated — skip
body = val.body if hasattr(val, "body") else None
if body is None:
continue
_walk_effects(body, name, comp_effects, anns, diagnostics)
return diagnostics
def _walk_effects(node, comp_name, caller_effects, anns, diagnostics):
"""Walk AST node and check effect calls."""
if not isinstance(node, list) or not node:
return
head = node[0]
if isinstance(head, Symbol):
callee = head.name
callee_effects = anns.get(callee)
if callee_effects is not None and caller_effects is not None:
for e in callee_effects:
if e not in caller_effects:
diagnostics.append({
"level": "warning",
"message": f"`{callee}` has effects {callee_effects} but `{comp_name}` only allows {caller_effects or '[pure]'}",
"component": comp_name,
})
break
for child in node[1:]:
_walk_effects(child, comp_name, caller_effects, anns, diagnostics)