HS parser/compiler/runtime: fix 8 parse errors, add/remove arrays, return guard

Parser:
- `add VALUE to :var` → (add-value) for array append
- `remove VALUE from :var` → (remove-value) for array removal
- `toggle .foo for 10ms` → (toggle-class-for) with duration
- `append VALUE` without `to` → implicit target (it)
- `set {obj} on target` → (set-on) for object property spread
- `repeat in` body: remove spurious nil (body at index 3→2)
- Keywords followed by `(` parsed as function calls (fixes `increment()`)

Compiler:
- Handle add-value, remove-value, toggle-class-for, set-on AST nodes
- Local variables (`set :var`) use `define` instead of `set!`

Runtime:
- hs-add-to!: append value to list
- hs-remove-from!: filter value from list
- hs-set-on!: spread dict properties onto target
- `as String` for lists: comma-join (JS Array.toString compat)

Tests:
- eval-hs/eval-hs-with-me: guard for hs-return exceptions
  (return compiles to raise, needs handler to extract value)

Parse errors: 20→12 (8 fixed). Remaining: 6 embedded HTML quotes
(tokenizer), 6 transition template values `(expr)px`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 11:06:46 +00:00
parent ac65666f6f
commit 3ba819d9ae
7 changed files with 767 additions and 941 deletions

View File

@@ -283,6 +283,12 @@
var-name
collection
(parse-expr))))))))
((and (= typ "keyword") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "paren-open"))
(do
(adv!)
(let
((name val) (args (parse-call-args)))
(list (quote call) (list (quote ref) name) args))))
(true nil)))))
(define
parse-poss
@@ -805,7 +811,14 @@
(cons
(quote multi-add-class)
(cons tgt (cons cls extra-classes))))))))
nil)))
(let
((value (parse-expr)))
(if
(match-kw "to")
(let
((tgt (parse-expr)))
(list (quote add-value) value tgt))
nil)))))
(define
parse-remove-cmd
(fn
@@ -870,8 +883,13 @@
(list (quote remove-css) props tgt)))))
(true
(let
((target (parse-expr)))
(list (quote remove-element) target))))))
((value (parse-expr)))
(if
(match-kw "from")
(let
((tgt (parse-expr)))
(list (quote remove-value) value tgt))
(list (quote remove-element) value)))))))
(define
parse-toggle-cmd
(fn
@@ -897,7 +915,12 @@
((cls (do (let ((v (tp-val))) (adv!) v))))
(let
((tgt (parse-tgt-kw "on" (list (quote me)))))
(list (quote toggle-class) cls tgt))))
(if
(match-kw "for")
(let
((dur (parse-expr)))
(list (quote toggle-class-for) cls tgt dur))
(list (quote toggle-class) cls tgt)))))
((= (tp-type) "style")
(let
((prop (do (let ((v (tp-val))) (adv!) v))))
@@ -1006,8 +1029,14 @@
()
(let
((tgt (parse-expr)))
(expect-kw! "to")
(let ((value (parse-expr))) (list (quote set!) tgt value)))))
(cond
((match-kw "to")
(let ((value (parse-expr))) (list (quote set!) tgt value)))
((match-kw "on")
(let
((target (parse-expr)))
(list (quote set-on) tgt target)))
(true (error (str "Expected to/on at position " p)))))))
(define
parse-put-cmd
(fn
@@ -1244,7 +1273,7 @@
(let
((body (parse-cmd-list)))
(match-kw "end")
(list (quote for) "it" collection nil body)))))
(list (quote for) "it" collection body)))))
(true
(let
((mode (cond ((match-kw "forever") (list (quote forever))) ((match-kw "while") (list (quote while) (parse-expr))) ((match-kw "until") (list (quote until) (parse-expr))) (true (let ((n (parse-expr))) (if (match-kw "times") (list (quote times) n) (list (quote forever))))))))
@@ -1464,10 +1493,12 @@
()
(let
((value (parse-expr)))
(expect-kw! "to")
(let
((target (parse-expr)))
(list (quote append!) value target)))))
(if
(match-kw "to")
(let
((target (parse-expr)))
(list (quote append!) value target))
(list (quote append!) value (list (quote it)))))))
(define
parse-tell-cmd
(fn