Files
rose-ash/shared/sx/ref/sx_ref.py
giles ea2b71cfa3 Add provide/context/emit!/emitted — render-time dynamic scope
Four new primitives for scoped downward value passing and upward
accumulation through the render tree. Specced in .sx, bootstrapped
to Python and JS across all adapters (eval, html, sx, dom, async).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:58:21 +00:00

4589 lines
184 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 {}
# Render-time accumulator buckets (per render pass)
_collect_buckets: dict[str, list] = {}
def _collect_reset():
"""Reset all collect buckets (call at start of each render pass)."""
global _collect_buckets
_collect_buckets = {}
# Render-time dynamic scope stacks (provide/context/emit!)
_provide_stacks: dict[str, list[dict]] = {}
def provide_push(name, value=None):
"""Push a provider scope with name, value, and empty emitted list."""
_provide_stacks.setdefault(name, []).append({"value": value, "emitted": []})
def provide_pop(name):
"""Pop the most recent provider scope for name."""
if name in _provide_stacks and _provide_stacks[name]:
_provide_stacks[name].pop()
def sx_context(name, *default):
"""Read value from nearest enclosing provider. Error if no provider and no default."""
if name in _provide_stacks and _provide_stacks[name]:
return _provide_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 provider's accumulator. Error if no provider."""
if name in _provide_stacks and _provide_stacks[name]:
_provide_stacks[name][-1]["emitted"].append(value)
else:
raise RuntimeError(f"No provider for emit!: {name}")
return NIL
def sx_emitted(name):
"""Return list of values emitted into nearest matching provider."""
if name in _provide_stacks and _provide_stacks[name]:
return list(_provide_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, _Signal):
return "signal"
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 render-time accumulator (deduplicated)."""
if bucket not in _collect_buckets:
_collect_buckets[bucket] = []
items = _collect_buckets[bucket]
if value not in items:
items.append(value)
def sx_collected(bucket):
"""Return all values in named render-time accumulator."""
return list(_collect_buckets.get(bucket, []))
def sx_clear_collected(bucket):
"""Clear a named render-time accumulator bucket."""
if bucket in _collect_buckets:
_collect_buckets[bucket] = []
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
# -------------------------------------------------------------------------
# Signal platform -- reactive state primitives
# -------------------------------------------------------------------------
class _Signal:
"""Reactive signal container."""
__slots__ = ("value", "subscribers", "deps")
def __init__(self, value):
self.value = value
self.subscribers = []
self.deps = []
class _TrackingContext:
"""Context for discovering signal dependencies."""
__slots__ = ("notify_fn", "deps")
def __init__(self, notify_fn):
self.notify_fn = notify_fn
self.deps = []
_tracking_context = None
def make_signal(value):
return _Signal(value)
def is_signal(x):
return isinstance(x, _Signal)
def signal_value(s):
return s.value if isinstance(s, _Signal) else s
def signal_set_value(s, v):
if isinstance(s, _Signal):
s.value = v
def signal_subscribers(s):
return list(s.subscribers) if isinstance(s, _Signal) else []
def signal_add_sub(s, fn):
if isinstance(s, _Signal) and fn not in s.subscribers:
s.subscribers.append(fn)
def signal_remove_sub(s, fn):
if isinstance(s, _Signal) and fn in s.subscribers:
s.subscribers.remove(fn)
def signal_deps(s):
return list(s.deps) if isinstance(s, _Signal) else []
def signal_set_deps(s, deps):
if isinstance(s, _Signal):
s.deps = list(deps) if isinstance(deps, list) else []
def set_tracking_context(ctx):
global _tracking_context
_tracking_context = ctx
def get_tracking_context():
global _tracking_context
return _tracking_context if _tracking_context is not None else NIL
def make_tracking_context(notify_fn):
return _TrackingContext(notify_fn)
def tracking_context_deps(ctx):
return ctx.deps if isinstance(ctx, _TrackingContext) else []
def tracking_context_add_dep(ctx, s):
if isinstance(ctx, _TrackingContext) and s not in ctx.deps:
ctx.deps.append(s)
def tracking_context_notify_fn(ctx):
return ctx.notify_fn if isinstance(ctx, _TrackingContext) else NIL
def invoke(f, *args):
"""Call f with args — handles both native callables and SX lambdas.
In Python, all transpiled lambdas are natively callable, so this is
just a direct call. The JS host needs dispatch logic here because
SX lambdas from runtime-evaluated code are objects, not functions.
"""
return f(*args)
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"))
def serialize(val):
"""Serialize an SX value to SX source text.
Note: parser.sx defines sx-serialize with a serialize alias, but parser.sx
is only included in JS builds (for client-side parsing). Python builds
provide this as a platform function.
"""
t = type_of(val)
if t == "sx-expr":
return val.source
if t == "nil":
return "nil"
if t == "boolean":
return "true" if val else "false"
if t == "number":
return str(val)
if t == "string":
return '"' + escape_string(val) + '"'
if t == "symbol":
return symbol_name(val)
if t == "keyword":
return ":" + keyword_name(val)
if t == "raw-html":
escaped = escape_string(raw_html_content(val))
return '(raw! "' + escaped + '")'
if t == "list":
if not val:
return "()"
items = [serialize(x) for x in val]
return "(" + " ".join(items) + ")"
if t == "dict":
items = []
for k, v in val.items():
items.append(":" + str(k))
items.append(serialize(v))
return "{" + " ".join(items) + "}"
if callable(val):
return "nil"
return str(val)
# Aliases for transpiled code — parser.sx defines sx-serialize/sx-serialize-dict
# but parser.sx is JS-only. Provide aliases so transpiled render.sx works.
sx_serialize = serialize
sx_serialize_dict = lambda d: serialize(d)
_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["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["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 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
# provide/context/emit! — render-time dynamic scope
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()}
# 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?"]
dissoc = PRIMITIVES["dissoc"]
index_of = PRIMITIVES["index-of"]
# =========================================================================
# 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 interface -- Async adapter
# =========================================================================
import contextvars
import 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 sx_parse(src):
from shared.sx.parser import parse_all
return parse_all(src)
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 == '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))
call_thunk(before, env)
push_wind_b(before, after)
result = call_thunk(body, env)
pop_wind_b()
call_thunk(after, env)
return 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
provide_push(name, val)
for e in body_exprs:
_cells['result'] = trampoline(eval_expr(e, env))
provide_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 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':
return expr
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':
return val
else:
return escape_html(sx_str(val))
# RENDER_HTML_FORMS
RENDER_HTML_FORMS = ['if', 'when', 'cond', 'case', 'let', 'let*', 'begin', 'do', 'define', 'defcomp', 'defisland', 'defmacro', 'defstyle', 'defhandler', 'deftype', 'defeffect', 'map', 'map-indexed', 'filter', 'for-each', '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('', filter(lambda x: (not sx_truthy(is_spread(x))), map(lambda x: render_value_to_html(x, env), expr)))
else:
name = symbol_name(head)
args = rest(expr)
if sx_truthy((name == '<>')):
return join('', filter(lambda x: (not sx_truthy(is_spread(x))), 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:
results = map(lambda i: render_to_html(nth(expr, i), env), range(2, len(expr)))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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:
results = map(lambda i: render_to_html(nth(expr, i), local), range(2, len(expr)))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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:
results = map(lambda i: render_to_html(nth(expr, i), env), range(1, len(expr)))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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('', filter(lambda r: (not sx_truthy(is_spread(r))), 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('', filter(lambda r: (not sx_truthy(is_spread(r))), 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('', filter(lambda r: (not sx_truthy(is_spread(r))), 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 == '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)
provide_push(prov_name, prov_val)
result = (render_to_html(nth(expr, body_start), env) if sx_truthy((body_count == 1)) else join('', filter(lambda r: (not sx_truthy(is_spread(r))), map(lambda i: render_to_html(nth(expr, i), env), range(body_start, (body_start + body_count))))))
provide_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)):
parts = []
for c in children:
r = render_to_html(c, env)
if sx_truthy((not sx_truthy(is_spread(r)))):
parts.append(r)
local['children'] = make_raw_html(join('', parts))
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:
content_parts = []
for c in children:
result = render_to_html(c, env)
if sx_truthy(is_spread(result)):
merge_spread_attrs(attrs, spread_attrs(result))
else:
content_parts.append(result)
return sx_str('<', tag, render_attrs(attrs), '>', join('', content_parts), '</', 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 '')}
content_parts = []
for c in children:
result = render_to_html(c, env)
if sx_truthy(is_spread(result)):
merge_spread_attrs(lake_attrs, spread_attrs(result))
else:
content_parts.append(result)
return sx_str('<', _cells['lake_tag'], render_attrs(lake_attrs), '>', join('', content_parts), '</', _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 '')}
content_parts = []
for c in children:
result = render_to_html(c, env)
if sx_truthy(is_spread(result)):
merge_spread_attrs(marsh_attrs, spread_attrs(result))
else:
content_parts.append(result)
return sx_str('<', _cells['marsh_tag'], render_attrs(marsh_attrs), '>', join('', content_parts), '</', _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)):
parts = []
for c in children:
r = render_to_html(c, env)
if sx_truthy((not sx_truthy(is_spread(r)))):
parts.append(r)
local['children'] = make_raw_html(join('', parts))
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)
_match = type_of(expr)
if _match == 'number':
return expr
elif _match == 'string':
return expr
elif _match == 'boolean':
return expr
elif _match == 'nil':
return NIL
elif _match == 'symbol':
name = symbol_name(expr)
if sx_truthy(env_has(env, name)):
return env_get(env, name)
elif sx_truthy(is_primitive(name)):
return get_primitive(name)
elif sx_truthy((name == 'true')):
return True
elif sx_truthy((name == 'false')):
return False
elif sx_truthy((name == 'nil')):
return NIL
else:
return error(sx_str('Undefined symbol: ', name))
elif _match == 'keyword':
return keyword_name(expr)
elif _match == 'list':
if sx_truthy(empty_p(expr)):
return []
else:
return aser_list(expr, env)
elif _match == 'spread':
return expr
else:
return expr
# 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 = {}
parts = [name]
_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 = aser(nth(args, (_cells['i'] + 1)), env)
if sx_truthy((not sx_truthy(is_nil(val)))):
parts.append(sx_str(':', keyword_name(arg)))
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)))):
parts.append(serialize(item))
else:
parts.append(serialize(val))
_cells['i'] = (_cells['i'] + 1)
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', '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 invoke(f, item)), coll)
elif sx_truthy((name == 'map-indexed')):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
return map_indexed(lambda i, item: ((lambda local: _sx_begin(_sx_dict_set(local, first(lambda_params(f)), i), _sx_dict_set(local, nth(lambda_params(f), 1), item), aser(lambda_body(f), local)))(env_merge(lambda_closure(f), env)) if sx_truthy(is_lambda(f)) else invoke(f, i, item)), coll)
elif sx_truthy((name == 'for-each')):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
results = []
for item in coll:
if sx_truthy(is_lambda(f)):
local = env_merge(lambda_closure(f), env)
local[first(lambda_params(f))] = item
results.append(aser(lambda_body(f), local))
else:
invoke(f, item)
if sx_truthy(empty_p(results)):
return NIL
else:
return results
elif sx_truthy((name == 'defisland')):
trampoline(eval_expr(expr, env))
return serialize(expr)
elif sx_truthy(((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else ((name == 'defhandler') if sx_truthy((name == 'defhandler')) else ((name == 'defpage') if sx_truthy((name == 'defpage')) else ((name == 'defquery') if sx_truthy((name == 'defquery')) else ((name == 'defaction') if sx_truthy((name == 'defaction')) else ((name == 'defrelation') 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 == 'provide')):
prov_name = trampoline(eval_expr(first(args), env))
prov_val = trampoline(eval_expr(nth(args, 1), env))
_cells['result'] = NIL
provide_push(prov_name, prov_val)
for body in slice(args, 2):
_cells['result'] = aser(body, env)
provide_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')):
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')):
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 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 signals (reactive signal runtime) ===
# 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 = get_tracking_context()
if sx_truthy(ctx):
tracking_context_add_dep(ctx, s)
signal_add_sub(s, tracking_context_notify_fn(ctx))
return signal_value(s)
# reset!
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: (lambda prev: _sx_begin(set_tracking_context(ctx), (lambda new_val: _sx_begin(set_tracking_context(prev), signal_set_deps(s, tracking_context_deps(ctx)), (lambda old: _sx_begin(signal_set_value(s, new_val), (notify_subscribers(s) if sx_truthy((not sx_truthy(is_identical(old, new_val)))) else NIL)))(signal_value(s))))(invoke(compute_fn))))(get_tracking_context()))(make_tracking_context(recompute))
)[-1])
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((invoke(_cells['cleanup_fn']) 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: (lambda prev: _sx_begin(set_tracking_context(ctx), (lambda result: _sx_begin(set_tracking_context(prev), _sx_cell_set(_cells, 'deps', tracking_context_deps(ctx)), (_sx_cell_set(_cells, 'cleanup_fn', result) if sx_truthy(is_callable(result)) else NIL)))(invoke(effect_fn))))(get_tracking_context()))(make_tracking_context(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),
(invoke(_cells['cleanup_fn']) 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)
invoke(thunk)
_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
# *island-scope*
_island_scope = NIL
# with-island-scope
def with_island_scope(scope_fn, body_fn):
prev = _island_scope
_island_scope = scope_fn
result = body_fn()
_island_scope = prev
return result
# register-in-scope
def register_in_scope(disposable):
if sx_truthy(_island_scope):
return _island_scope(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:
invoke(d)
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, invoke(init_fn))
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))((invoke(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(invoke(fetch_fn), lambda data: reset_b(state, {'loading': False, 'data': data, 'error': NIL}), lambda err: reset_b(state, {'loading': False, 'data': NIL, 'error': err}))
return state
# === 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':
return expr
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('', filter(lambda r: (not sx_truthy(is_spread(r))), (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 = []
for c in children:
result = (await async_render(c, env, ctx))
if sx_truthy(is_spread(result)):
merge_spread_attrs(attrs, spread_attrs(result))
else:
content_parts.append(result)
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:
r = (await async_render(c, env, ctx))
if sx_truthy((not sx_truthy(is_spread(r)))):
parts.append(r)
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:
r = (await async_render(c, env, ctx))
if sx_truthy((not sx_truthy(is_spread(r)))):
parts.append(r)
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', '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:
results = (await async_map_render(slice(expr, 2), env, ctx))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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:
results = (await async_map_render(slice(expr, 2), local, ctx))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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:
results = (await async_map_render(rest(expr), env, ctx))
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
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('', filter(lambda r: (not sx_truthy(is_spread(r))), (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('', filter(lambda r: (not sx_truthy(is_spread(r))), (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('', filter(lambda r: (not sx_truthy(is_spread(r))), (await async_map_fn_render(f, coll, env, ctx))))
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)
provide_push(prov_name, prov_val)
result = ((await async_render(nth(expr, body_start), env, ctx)) if sx_truthy((body_count == 1)) else (lambda results: join('', filter(lambda r: (not sx_truthy(is_spread(r))), results)))((await async_map_render(slice(expr, body_start), env, ctx))))
provide_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):
_match = type_of(expr)
if _match == 'number':
return expr
elif _match == 'string':
return expr
elif _match == 'boolean':
return expr
elif _match == 'nil':
return NIL
elif _match == 'symbol':
name = symbol_name(expr)
if sx_truthy(env_has(env, name)):
return env_get(env, name)
elif sx_truthy(is_primitive(name)):
return get_primitive(name)
elif sx_truthy((name == 'true')):
return True
elif sx_truthy((name == 'false')):
return False
elif sx_truthy((name == 'nil')):
return NIL
else:
return error(sx_str('Undefined symbol: ', name))
elif _match == 'keyword':
return keyword_name(expr)
elif _match == 'dict':
return (await async_aser_dict(expr, env, ctx))
elif _match == 'spread':
return expr
elif _match == 'list':
if sx_truthy(empty_p(expr)):
return []
else:
return (await async_aser_list(expr, env, ctx))
else:
return expr
# 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)
parts = [name]
_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))
if sx_truthy((not sx_truthy(is_nil(val)))):
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)):
parts.append('nil')
else:
items = map(serialize, live)
if sx_truthy(some(lambda v: is_sx_expr(v), live)):
parts.append(sx_str('(<> ', join(' ', items), ')'))
else:
parts.append(sx_str('(list ', join(' ', items), ')'))
else:
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)))):
parts.append(serialize(item))
else:
parts.append(serialize(result))
_cells['i'] = (_cells['i'] + 1)
if sx_truthy(token):
svg_context_reset(token)
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', '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 == 'provide')):
prov_name = (await async_eval(first(args), env, ctx))
prov_val = (await async_eval(nth(args, 1), env, ctx))
_cells['result'] = NIL
provide_push(prov_name, prov_val)
for body in slice(args, 2):
_cells['result'] = (await async_aser(body, env, ctx))
provide_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
# =========================================================================
# 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))