Add sexp.js: client-side s-expression parser, evaluator, and DOM renderer

Vanilla JS (no build tools) counterpart to shared/sexp/ Python modules.
Parses s-expression text, evaluates special forms, and renders to DOM
nodes or HTML strings. Full component system with defcomp/~name.

Includes:
- Parser: tokenizer + parse/parseAll matching Python parser exactly
- Evaluator: all special forms (if, when, cond, let, and, or, lambda,
  defcomp, define, ->, set!), higher-order forms (map, filter, reduce)
- DOM renderer: createElement for HTML tags, SVG namespace support,
  component invocation, raw! for pre-rendered HTML, <> fragments
- String renderer: matches Python html.render output for SSR parity
- ~50 built-in primitives (arithmetic, string, collection, predicates)
- 35 parity tests verifying JS output matches Python output via Node.js

Also fixes Python raw! handler to properly unwrap _RawHTML objects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 23:28:21 +00:00
parent bc7a4a5128
commit e8a991834b
3 changed files with 1331 additions and 1 deletions

View File

@@ -396,7 +396,9 @@ def _render_list(expr: list, env: dict[str, Any]) -> str:
parts = []
for arg in expr[1:]:
val = _eval(arg, env)
if isinstance(val, str):
if isinstance(val, _RawHTML):
parts.append(val.html)
elif isinstance(val, str):
parts.append(val)
elif val is not None and val is not NIL:
parts.append(str(val))