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