Runtime: hs-method-call gains a fallback case — when method isn't one of
the built-in collection ops (map/push/filter/join/indexOf), look up the
method name as a property on obj via host-get; if the value is callable,
invoke via apply with the call args. This makes namespaced calls like
`utils.foo()` work when utils is an SX dict whose foo entry is an SX fn.
Generator: hand-rolled deftests for the 3 cluster-35 tests:
- `is called synchronously` and `can call asynchronously`: pre-evaluate
the script-tag def via `(eval-expr-cek (hs-to-sx (first (hs-parse
(hs-tokenize "def foo() ... end")))))` so foo lands in the global eval
env, then build a click div via dom-set-attr + hs-boot-subtree! and
exercise it via dom-dispatch click.
- `functions can be namespaced`: hand-build `(define utils (dict))` then
`(host-set! utils "foo" __utils_foo)` (the def is registered under a
fresh sym since the parser doesn't yet support `def utils.foo()` dotted
names), and rely on the new hs-method-call fallback to dispatch
`utils.foo()` through host-get/apply.
Removed the 3 def entries from SKIP_TEST_NAMES.
hs-upstream-def: 24/27 → 27/27. Smoke 0-195 unchanged at 172/195.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Parser: parse-on-feat now consumes `elsewhere` (or `from elsewhere`) as
a modifier between event-name and source. When matched, sets a flag and
emits :elsewhere true on parts. The `from elsewhere` form peeks one
token ahead before consuming both keywords so plain `from #x` continues
to parse as a source expression.
Compiler: scan-on threads elsewhere?; when present, target becomes
(dom-body) (so the listener attaches to body and bubbles see all clicks)
and the handler body is wrapped with `(when (not (host-call me "contains"
(host-get event "target"))) BODY)` so the handler fires only when the
click originated outside the activated element.
Generator: dropped supports "elsewhere" modifier and supports "from
elsewhere" modifier from skip-list.
hs-upstream-on: 48/70 → 50/70. Smoke 0-195 unchanged at 172/195.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 5 new built-in predicates to the Prolog runtime with 15 tests.
390 → 405 tests across 20 suites (all passing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Parser: parse-on-feat now consumes `first` keyword before event-name (sets
count-min/max to 1) and a count expression after event-name — `N` (single),
`N to M` (range), `N and on` (unbounded above). Number tokens are coerced
via parse-number. Emits :count-filter {"min" N "max" M | -1} part.
Compiler: scan-on threads count-filter-info; the handler binding wraps the
fn body in a let-bound __hs-count counter. Each event fire increments the
counter and (when count is in range) executes the original body. Each
on-clause registers an independent handler with its own counter, so
`on click 1 ... on click 2 ... on click 3` produces three handlers that
fire on their respective Nth click (mix-ranges test).
Generator: dropped 5 cluster-34 tests from skip-list — `can filter events
based on count`, `... count range`, `... unbounded count range`, `can mix
ranges`, `on first click fires only once`.
hs-upstream-on: 43/70 → 48/70. Smoke 0-195 unchanged at 172/195.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds pl-apply-goal helper for safe call/N goal construction (atom or compound),
five solver helpers (pl-solve-forall!, pl-solve-maplist2!, pl-solve-maplist3!,
pl-solve-include!, pl-solve-exclude!), five cond clauses in pl-solve!, and a
new test suite (15/15 passing). Total conformance: 390/390.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
integration.sx hs-activate! now wraps the activation block in a cancelable
hyperscript:before:init event (dispatched on the el via dom-dispatch which
returns the dispatchEvent boolean — true unless preventDefault was called).
On success it dispatches hyperscript:after:init at the end. Both events
bubble so listeners on a containing wa work-area receive them. Generator
gets two hand-rolled deftests that exercise the new dispatch via
hs-boot-subtree!: one captures both events into a list, the other
preventDefaults before:init and asserts data-hyperscript-powered is absent.
hs-upstream-core/bootstrap: 20/26 → 22/26. Smoke 0-195: 170 → 172.
Remaining 4 cluster-29 tests need stricter parser error-rejection
(hs-upstream-core/parser, parse-error event); larger than a single
cluster budget — leave as untranslated for now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Parser: parse-on-feat now consumes `of FILTER` after `mutation` event-name,
where FILTER is `attributes`/`childList`/`characterData` ident or `@a [or @b]*`
attr-token chain. Emits :of-filter dict on parts. Compiler: scan-on threads
of-filter-info; mutation event-name emits `(do (hs-on …) (hs-on-mutation-attach!
TARGET MODE ATTRS))`. Runtime: hs-on-mutation-attach! constructs a real
MutationObserver with config matched to filter and dispatches "mutation" event
with records detail. Runner: HsMutationObserver mock with global registry;
prototype hooks on El.setAttribute/appendChild/removeChild/_setInnerHTML fire
matching observers synchronously, with __hsMutationActive guard preventing
recursion. Generator: dropped 7 mutation tests from skip-list, added
evaluate(setAttribute) and evaluate(appendChild) body patterns.
hs-upstream-on: 36/70 → 43/70. Smoke 0-195 unchanged at 170/195.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>