Add hs-tokenize-template: scans " as single STRING token, ${ ... }
as dollar+brace+inner-tokens (inner tokenized with hs-tokenize), and
} as brace-close. Update hs-tokens-of to call hs-tokenize-template
when :template keyword arg is passed. Unlocks tests 1 and 15.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add \r \b \f \v and \xNN escape handling to read-string. Use
char-from-code for non-SX-literal chars. Throw "Unterminated string"
on EOF inside a string literal. Throw "Invalid hexadecimal escape: \x"
on bad \xNN. Add hs-hex-digit? and hs-hex-val helpers. Unlocks
tests 2, 6, 13, 14 once generator lands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add hs-eof-sentinel, hs-op-type, hs-raw->api-token, hs-tokens-of,
hs-stream-token, hs-stream-consume, hs-stream-has-more, and the
three token accessors (hs-token-type, hs-token-value, hs-token-op?).
No test delta yet — API-only, generator comes in step 6.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Threads declaration kind ("var"/"let"/"const") through js-transpile-var →
js-vardecl-forms so the transpiler knows which kind is being declared.
Infrastructure for full TDZ enforcement: js-tdz-check can wrap let/const
reads to raise TypeError before initialization.
Updates plans/js-on-sx.md: ticks [x] for TDZ, marks regex blocker RESOLVED,
adds progress log entry for 2026-04-25.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`1 in [1, 2, 3]` must return (list 1) not true. Root cause: in? compiled
to hs-contains? which returns boolean for scalar items. Fix: new hs-in?
returns filtered list; new in-bool? operator for is/am-in comparison
contexts so those still return boolean. Parser generates in-bool? for
`X is in Y` / `X am in Y`; plain `in` keeps in? → list return.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Mark cluster 22 done (+1): can refer to function in init blocks
- Scoreboard: merged 1280→1302 (+22 from stale rows 22/29/32/33/34/35)
- Fix stale rows: clusters 29 partial, 32 done, 33 partial+4, 34 partial+7, 35 done
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove guard wrapper from hs-win-call emit (direct call is sufficient now)
- def command also registers fn on window[name] so hs-win-call finds it
- Generator: fix \"-escaped quotes in hs-compile string literal (was splitting "here" into three SX nodes)
- Hand-rolled deftest for 'can refer to function in init blocks' now passes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pl-eval-arith: add floor, ceiling, truncate, round, sqrt, sign, pow, integer,
float, float_integer_part, float_fractional_part, **, ^ operators
- pl-collect-vars: helper that extracts unbound variables from a term (left-to-right,
deduplicated by var id)
- term_variables/2: dispatches via pl-collect-vars, unifies second arg with var list
- pl-predsort-insert!: inserts one element into a sorted list using a 3-arg comparator
predicate; deduplicates elements where comparator returns '='
- pl-predsort-build!: builds sorted list via fold over pl-predsort-insert!
- predsort/3: full ISO predsort — sorts and deduplicates a list using a caller-supplied
predicate
- lib/prolog/tests/advanced.sx: 21 tests (12 arith, 5 term_variables, 4 predsort)
- conformance.sh: add advanced suite
- scoreboard: 517/517 (was 496/496)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds two new builtins to lib/prolog/runtime.sx:
- sub_atom/5: non-deterministic substring enumeration. Iterates all
(start, length) pairs over the atom string, tries to unify Before,
Length, After, SubAtom for each candidate. Uses CPS loop helpers
pl-substring, pl-sub-atom-try-one!, pl-sub-atom-loop!. Fixed trail
undo semantics: only undo on backtrack (k returns false), not on success.
- aggregate_all/3: collects all solutions via pl-collect-solutions then
reduces. Templates: count, bag(T), sum(E), max(E), min(E), set(T).
max/min fail on empty; count/bag/sum/set always succeed.
New test suite lib/prolog/tests/string_agg.sx: 25 tests, all passing.
Total conformance: 496/496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- parser.sx: add (":-" 1200 "xfx") to pl-op-table so (head :- body) parses
inside paren expressions (parens reset prec to 1200, allowing xfx match)
- parser.sx: extend pl-token-op to accept "op" token type, not just "atom",
since the tokenizer emits :- as {:type "op" :value ":-"}
- tests/assert_rules.sx: 15 new tests covering assertz/asserta with rule
terms, conjunction in rule body, recursive rules, and ordering
- conformance.sh: wire in assert_rules suite
- 456 → 471 tests, all passing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>