Commit Graph

257 Commits

Author SHA1 Message Date
719dfbf732 Debug: log each isGetLink condition individually
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:59:58 +00:00
5ea0f5c546 Debug: log mods.delay and isGetLink result
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:59:17 +00:00
74428cc433 Debug: log verbInfo method and url in click handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:58:43 +00:00
d1a47e1e52 Restore click handler logging + use logInfo for errors (SES-safe)
SES lockdown may suppress console.error. Use logInfo for error
reporting since we know it works ([sx-ref] prefix visible).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:57:44 +00:00
3d191099e0 Surface all errors: wrap event handlers in try/catch with console.error
- domAddListener wraps callbacks so exceptions always log to console
- Add "sx:route trying <url>" log before tryClientRoute call
- Remove redundant bind-event fired log (replaced with route-specific logs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:53:54 +00:00
70cf501c49 Debug: log every bind-event click handler firing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:51:04 +00:00
2a978e6e9f Add explicit logging for route decisions in bind-event
- Log "sx:route server fetch <url>" when falling back to network
- Use console.error for eval errors (not console.warn)
- Restructure bind-event to separate client route check from &&-chain

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:44:55 +00:00
3a8ee0dbd6 Fix router.sx: use len not length (correct SX primitive name)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:38:23 +00:00
c346f525d2 Include router spec module in sx-browser.js bootstrap
parseRoutePattern was undefined because the router module
wasn't included in the build. Now passing --spec-modules router.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:37:41 +00:00
79ee3bc46e Fix page registry: process page scripts before mount scripts
The data-mount="body" script replaces the entire body content,
destroying the <script type="text/sx-pages"> tag. Moving
processPageScripts before processSxScripts ensures the page
registry is read before the body is replaced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:35:59 +00:00
c80b5d674f Add debug logging to page registry pipeline
Server-side: log page count, output size, and first 200 chars in _build_pages_sx.
Client-side: log script tag count, text length, parsed entry count in processPageScripts.
Helps diagnose why pages: 0 routes loaded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:31:53 +00:00
f08bd403de Remove wrong brace escaping from pages_sx
str.format() doesn't re-process braces inside substituted values,
so the escaping was producing literal doubled braces {{:name...}}
in the output, which the SX parser couldn't parse.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:26:38 +00:00
227444a026 Tighten exception handling in helpers.py
- Remove silent except around content serialization (was hiding bugs)
- Narrow cookie/request-context catches to RuntimeError
- Narrow script hash to OSError
- Log warnings for pretty-print failures instead of silent pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:19:26 +00:00
2660d37f9e Remove try/except around page registry build
This was silently masking the str.format() braces bug. If page
registry building fails, it should crash visibly, not serve a
broken page with 0 routes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:17:37 +00:00
d850f7c9c1 Fix page registry: escape braces for str.format()
pages_sx contains SX dict literals with {} (empty closures) which
Python's str.format() interprets as positional placeholders, causing
a KeyError that was silently caught. Escape braces before formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:16:35 +00:00
bc9d9e51c9 Log page registry build errors instead of silently swallowing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:14:19 +00:00
eb70e7237e Log route count on boot and no-match on route attempts
Shows "pages: N routes loaded" at startup and
"sx:route no match (N routes) /path" when no route matches,
so we can see if routes loaded and why matching fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:10:24 +00:00
a7d09291b8 Add version logging and route decision logging to sx-browser
boot-init prints SX_VERSION (build timestamp) to console on startup.
tryClientRoute logs why it falls through: has-data, no content, eval
failed, #main-panel not found. tryEvalContent logs the actual error.
Added logWarn platform function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:05:39 +00:00
2d5096be6c Add console logging for client-side routing decisions
tryClientRoute now logs why it falls through: has-data, no content,
eval failed, or #main-panel not found. tryEvalContent logs the actual
error on catch. Added logWarn platform function (console.warn).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:43:27 +00:00
f70861c175 Try client-side routing for all sx-get link clicks, not just boost links
bind-event now checks tryClientRoute before executeRequest for GET
clicks on links. Previously only boost links (inside [sx-boost]
containers) attempted client routing — explicit sx-get links like
~nav-link always hit the network. Now essay/doc nav links render
client-side when possible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:31:23 +00:00
20ac0fe948 Spec eval-cond and process-bindings in render.sx (remove platform implementations)
eval-cond and process-bindings were hand-written platform JS in
bootstrap_js.py rather than specced in .sx files. This violated the
SX host architecture principle. Now specced in render.sx as shared
render adapter helpers, bootstrapped to both JS and Python.

eval-cond handles both scheme-style ((test body) ...) and clojure-style
(test body test body ...) cond clauses. Returns unevaluated body
expression for the adapter to render in its own mode.

process-bindings evaluates let-binding pairs and returns extended env.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:58:53 +00:00
a2d0a8a0fa Fix evalCond in HTML/DOM renderers: handle scheme-style cond clauses
The platform evalCond helper (used by render-to-html and render-to-dom)
only handled clojure-style (test body test body ...) but components use
scheme-style ((test body) (test body) ...). This caused "Not callable:
true" errors when rendering cond with nested clause pairs, breaking the
test dashboard and any page using scheme-style cond.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:49:42 +00:00
3749fe9625 Fix bootstrapper dict literal transpilation: emit values through emit()
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m51s
The SX parser produces native Python dicts for {:key val} syntax, but
both JSEmitter and PyEmitter had no dict case in emit() — falling through
to str(expr) which output raw AST. This broke client-side routing because
process-page-scripts used {"parsed" (parse-route-pattern ...)} and the
function call was emitted as a JS array of Symbols instead of an actual
function call.

Add _emit_native_dict() to both bootstrappers + 8 unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:24:44 +00:00
cf5e767510 Phase 3: Client-side routing with SX page registry + routing analyzer demo
Add client-side route matching so pure pages (no IO deps) can render
instantly without a server roundtrip. Page metadata serialized as SX
dict literals (not JSON) in <script type="text/sx-pages"> blocks.

- New router.sx spec: route pattern parsing and matching (6 pure functions)
- boot.sx: process page registry using SX parser at startup
- orchestration.sx: intercept boost links for client routing with
  try-first/fallback — client attempts local eval, falls back to server
- helpers.py: _build_pages_sx() serializes defpage metadata as SX
- Routing analyzer demo page showing per-page client/server classification
- 32 tests for Phase 2 IO detection (scan_io_refs, transitive_io_refs,
  compute_all_io_refs, component_pure?) + fallback/ref parity
- 37 tests for Phase 3 router functions + page registry serialization
- Fix bootstrap_py.py _emit_let cell variable initialization bug
- Fix missing primitive aliases (split, length, merge) in bootstrap_py.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:47:56 +00:00
3482cbdaa6 Document host obligation for selective expansion in deps.sx
The spec classifies components as pure vs IO-dependent. Each host's
async partial evaluator must act on this: expand IO-dependent server-
side, serialize pure for client. This is host infrastructure, not SX
semantics — documented as a contract in the spec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:34:09 +00:00
0ba7ebe349 Phase 2: IO detection & selective expansion in deps.sx
Extend the spec with IO scanning functions (scan-io-refs, transitive-io-refs,
compute-all-io-refs, component-pure?) that detect IO primitive references in
component ASTs. Components are classified as pure (no IO deps, safe for client
rendering) or IO-dependent (must expand server-side).

The partial evaluator (_aser) now uses per-component IO metadata instead of
the global _expand_components toggle: IO-dependent components expand server-
side, pure components serialize for client. Layout slot context still expands
all components for backwards compat.

Spec: 5 new functions + 2 platform interface additions in deps.sx
Host: io_refs field + is_pure property on Component, compute_all_io_refs()
Bootstrap: both sx_ref.py and sx-ref.js updated with IO functions
Bundle analyzer: shows pure/IO classification per page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:19:17 +00:00
8ff9827d7b Skip boundary.sx in component loader
boundary.sx files use define-page-helper which isn't an SX eval form —
they're parsed by boundary_parser.py. Exclude them from load_sx_dir()
to prevent EvalError on startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:51:49 +00:00
07a73821e7 Fix boundary parser Docker path: handle /app/sx/boundary.sx layout
In Docker, each service's sx/ dir is copied directly to /app/sx/,
not /app/{service}/sx/. Add fallback search for /app/sx/boundary.sx
alongside the dev glob pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:47:50 +00:00
44d5414bc6 Split boundary.sx: separate language contract from app-specific declarations
boundary.sx was mixing three concerns in one file:
- Core SX I/O primitives (the language contract)
- Deployment-specific layout I/O (app architecture)
- Per-service page helpers (fully app-specific)

Now split into three tiers:
1. shared/sx/ref/boundary.sx — core I/O only (frag, query, current-user, etc.)
2. shared/sx/ref/boundary-app.sx — deployment layout contexts (*-header-ctx, *-ctx)
3. {service}/sx/boundary.sx — per-service page helpers

The boundary parser loads all three tiers automatically. Validation error
messages now point to the correct file for each tier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:41:38 +00:00
0191948b6e Declare bundle-analyzer-data page helper in boundary.sx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:28:32 +00:00
e36a036873 Add live bundle analyzer page to sx-docs
Demonstrates Phase 1 dep analysis in action: computes per-page component
bundles for all sx-docs pages using the deps.sx transitive closure
algorithm, showing needed vs total components with visual progress bars.

- New page at /plans/bundle-analyzer with Python data helper
- New components: ~bundle-analyzer-content, ~analyzer-stat, ~analyzer-row
- Linked from Phase 1 section and Plans nav
- Added sx/sx/ to tailwind content paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:23:58 +00:00
4c97b03dda Wire deps.sx into both bootstrappers, rebootstrap Python + JS
deps.sx is now a spec module that both bootstrap_py.py and bootstrap_js.py
can include via --spec-modules deps. Platform functions (component-deps,
component-set-deps!, component-css-classes, env-components, regex-find-all,
scan-css-classes) implemented natively in both Python and JS.

- Fix deps.sx: env-get-or → env-get, extract nested define to top-level
- bootstrap_py.py: SPEC_MODULES, PLATFORM_DEPS_PY, mangle entries, CLI arg
- bootstrap_js.py: SPEC_MODULES, PLATFORM_DEPS_JS, mangle entries, CLI arg
- Regenerate sx_ref.py and sx-ref.js with deps module
- deps.py: thin dispatcher (SX_USE_REF=1 → bootstrapped, else fallback)
- scan_components_from_sx now returns ~prefixed names (consistent with spec)

Verified: 541 Python tests pass, JS deps tested with Node.js, both code
paths (fallback + bootstrapped) produce identical results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:55:32 +00:00
6739343a06 Add deps.sx spec: component dependency analysis
Canonical specification for per-page component bundling. Pure functions
for AST scanning, transitive closure, page bundle computation, and
per-page CSS class collection. deps.py becomes a thin host wrapper;
future hosts (Go, Rust, Haskell, etc.) bootstrap from this spec.

Defines 8 functions: scan-refs, scan-refs-walk, transitive-deps,
compute-all-deps, scan-components-from-source, components-needed,
page-component-bundle, page-css-classes.

Platform interface: component-body, component-name, component-deps,
component-set-deps!, component-css-classes, macro-body, env-components,
regex-find-all, scan-css-classes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:31:43 +00:00
2866bcbfc3 Implement isomorphic Phase 1: per-page component bundling
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 4m43s
Add component dependency analyzer (shared/sx/deps.py) that walks
component AST bodies to compute transitive dependency sets. sx_page()
and sx_response() now send only the components each page needs instead
of the entire registry.

Changes:
- New: shared/sx/deps.py — transitive_deps(), components_needed(),
  scan_components_from_sx(), compute_all_deps()
- shared/sx/types.py — Add deps: set[str] field to Component
- shared/sx/jinja_bridge.py — Compute deps on registration, add
  components_for_page() and css_classes_for_page()
- shared/sx/helpers.py — sx_page() uses per-page bundle + hash,
  sx_response() passes source to components_for_request() for
  page-scoped component diffing
- New: shared/sx/tests/test_deps.py — 15 tests covering AST scanning,
  transitive deps, circular refs, per-page bundling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:19:10 +00:00
e112bffe5c Add index-of string primitive: spec, Python, JS, rebootstrap
(index-of s needle from?) returns first index of needle in s, or -1.
Optional start offset. Specced in primitives.sx, implemented in both
hand-written primitives.py and bootstrapper templates, rebootstrapped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:48:44 +00:00
6aa2f3f6bd Add Special Forms docs page at /docs/special-forms
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m54s
Parses special-forms.sx spec into categorized form cards with syntax,
description, tail-position info, and highlighted examples. Follows the
same pattern as the Primitives page: Python helper returns structured
data, .sx components render it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:59:47 +00:00
6c27ebd3b4 Merge branch 'worktree-refactor-primitives' into macros
# Conflicts:
#	shared/sx/ref/bootstrap_js.py
#	shared/sx/ref/bootstrap_py.py
2026-03-06 01:50:29 +00:00
f77d7350dd Refactor SX primitives: modular, isomorphic, general-purpose
Spec modularization:
- Add (define-module :name) markers to primitives.sx creating 11 modules
  (7 core, 4 stdlib). Bootstrappers can now selectively include modules.
- Add parse_primitives_by_module() to boundary_parser.py.
- Remove split-ids primitive; inline at 4 call sites in blog/market queries.

Python file split:
- primitives.py: slimmed to registry + core primitives only (~350 lines)
- primitives_stdlib.py: NEW — stdlib primitives (format, text, style, debug)
- primitives_ctx.py: NEW — extracted 12 page context builders from IO
- primitives_io.py: add register_io_handler decorator, auto-derive
  IO_PRIMITIVES from registry, move sync IO bridges here

JS parity fixes:
- = uses === (strict equality), != uses !==
- round supports optional ndigits parameter
- concat uses nil-check not falsy-check (preserves 0, "", false)
- escape adds single quote entity (&#x27;) matching Python/markupsafe
- assert added (was missing from JS entirely)

Bootstrapper modularization:
- PRIMITIVES_JS_MODULES / PRIMITIVES_PY_MODULES dicts keyed by module
- --modules CLI flag for selective inclusion (core.* always included)
- Regenerated sx-ref.js and sx_ref.py with all fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:45:29 +00:00
ca8de3be1a Make continuations an optional extension, add special-forms.sx, ellipsis parsing
- Both bootstrappers (JS + Python) now gate shift/reset behind --extensions
  continuations flag. Without it, using reset/shift errors at runtime.
- JS bootstrapper: extracted Continuation/ShiftSignal types, sfReset/sfShift,
  continuation? primitive, and typeOf handling into CONTINUATIONS_JS constant.
  Extension wraps evalList, aserSpecial, and typeOf post-transpilation.
- Python bootstrapper: added special-forms.sx validation cross-check against
  eval.sx dispatch, warns on mismatches.
- Added shared/sx/ref/special-forms.sx: 36 declarative form specs with syntax,
  docs, tail-position, and examples. Used by bootstrappers for validation.
- Added ellipsis (...) support to both parser.py and parser.sx spec.
- Updated continuations essay to reflect optional extension architecture.
- Updated specs page and nav with special-forms.sx entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:44:50 +00:00
31ace8768e Merge scheme-forms into macros: named let, letrec, dynamic-wind, eq?/eqv?/equal?
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:16:16 +00:00
f34e55aa9b Add Scheme forms: named let, letrec, dynamic-wind, three-tier equality
Spec (eval.sx, primitives.sx):
- Named let: (let loop ((i 0)) body) — self-recursive lambda with TCO
- letrec: mutually recursive local bindings with closure patching
- dynamic-wind: entry/exit guards with wind stack for future continuations
- eq?/eqv?/equal?: identity, atom-value, and deep structural equality

Implementation (evaluator.py, async_eval.py, primitives.py):
- Both sync and async evaluators implement all four forms
- 33 new tests covering all forms including TCO at 10k depth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:11:31 +00:00
102a27e845 Implement delimited continuations (shift/reset) across all evaluators
Bootstrap shift/reset to both Python and JS targets. The implementation
uses exception-based capture with re-evaluation: reset wraps in try/catch
for ShiftSignal, shift raises to the nearest reset, and continuation
invocation pushes a resume value and re-evaluates the body.

- Add Continuation type and _ShiftSignal to shared/sx/types.py
- Add sf_reset/sf_shift to hand-written evaluator.py
- Add async versions to async_eval.py
- Add shift/reset dispatch to eval.sx spec
- Bootstrap to Python: FIXUPS_PY with sf_reset/sf_shift, regenerate sx_ref.py
- Bootstrap to JS: Continuation/ShiftSignal types, sfReset/sfShift in fixups
- Add continuation? primitive to both bootstrappers and primitives.sx
- Allow callables (including Continuation) in hand-written HO map
- 44 unit tests (22 per evaluator) covering: passthrough, abort, invoke,
  double invoke, predicate, stored continuation, nested reset, practical patterns
- Update continuations essay to reflect implemented status with examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:58:50 +00:00
12fe93bb55 Add continuation specs: delimited (shift/reset) and full (call/cc)
Optional bolt-on extensions to the SX spec. continuations.sx defines
delimited continuations for all targets. callcc.sx defines full call/cc
for targets where it's native (Scheme, Haskell). Shared continuation
type if both are loaded. Wired into specs section of sx-docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:41:28 +00:00
cfde5bc491 Fix sync IO primitives unreachable from sx_ref.py evaluator
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m49s
app-url, asset-url, config, jinja-global, relations-from are declared
as IO in boundary.sx but called inline in .sx code (inside let/filter).
async_eval_ref.py only intercepts IO at the top level — nested calls
fall through to sx_ref.eval_expr which couldn't find them.

Register sync bridge wrappers directly in _PRIMITIVES (bypassing
@register_primitive validation since they're boundary.sx, not
primitives.sx). Both async and sync eval paths now work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:06:09 +00:00
04366990ec Enforce SX boundary contract via boundary.sx spec + runtime validation
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Add boundary.sx declaring all 34 I/O primitives, 32 page helpers, and 9
allowed boundary types. Runtime validation in boundary.py checks every
registration against the spec — undeclared primitives/helpers crash at
startup with SX_BOUNDARY_STRICT=1 (now set in both dev and prod).

Key changes:
- Move 5 I/O-in-disguise primitives (app-url, asset-url, config,
  jinja-global, relations-from) from primitives.py to primitives_io.py
- Remove duplicate url-for/route-prefix from primitives.py (already in IO)
- Fix parse-datetime to return ISO string instead of raw datetime
- Add datetime→isoformat conversion in _convert_result at the edge
- Wrap page helper return values with boundary type validation
- Replace all SxExpr(f"...") patterns with sx_call() or _sx_fragment()
- Add assert declaration to primitives.sx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:50:02 +00:00
54adc9c216 Add TCO for parser loops in JS bootstrapper, enable SX_USE_REF
The JS parser transpiled from parser.sx used tail-recursive functions
(readStrLoop, skipWs, readListLoop, etc.) which overflow the stack on
large inputs — the bootstrapper page highlights 100KB of Python and
143KB of JavaScript, producing 7620 spans in a 907KB response.

The bootstrapper now detects zero-arg self-tail-recursive functions and
emits them as while(true) loops with continue instead of recursive
calls. Tested with 150K char strings and 8000 sibling elements.

Also enables SX_USE_REF=1 in dev via x-dev-env anchor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:09:02 +00:00
38f1f82988 Merge branch 'worktree-bootstrap-py' into macros 2026-03-05 22:48:14 +00:00
bb5c7e8444 Fall through to shared primitive registry for external primitives
is_primitive/get_primitive now check the shared registry
(shared.sx.primitives) when a name isn't in the transpiled PRIMITIVES
dict. Fixes Undefined symbol errors for register_primitive'd functions
like relations-from.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 22:48:14 +00:00
a40dd06811 Merge branch 'worktree-bootstrap-py' into macros 2026-03-05 22:45:05 +00:00
ef04beba00 Add call-fn dispatch for HO forms: handle both Lambda and native callable
HO forms (map, filter, reduce, etc.) now use call-fn which dispatches
Lambda → call-lambda, native callable → apply, else → clear EvalError.
Previously call-lambda crashed with AttributeError on native functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 22:45:01 +00:00