HS: send can reference sender (+1 test)

Three-part fix: (a) emit-send now builds detail=(dict "sender" me) on
(send NAME target) and bare (send NAME) instead of nil, so the receiving
handler has access to the sending element. (b) parser parse-atom now
recognises the `sender` keyword (previously swallowed as noise) and
emits it as (sender). (c) compiler translates bare `sender` symbol and
(sender) list-head to (hs-sender event) — a new runtime helper that
reads (get (host-get event "detail") "sender").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 22:37:18 +00:00
parent 15c310cdc1
commit ed8d71c9b8
6 changed files with 60 additions and 14 deletions

View File

@@ -264,8 +264,17 @@
name name
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= (len ast) 3) ((= (len ast) 3)
(list (quote dom-dispatch) (hs-to-sx (nth ast 2)) name nil)) (list
(true (list (quote dom-dispatch) (quote me) name nil)))))) (quote dom-dispatch)
(hs-to-sx (nth ast 2))
name
(list (quote dict) "sender" (quote me))))
(true
(list
(quote dom-dispatch)
(quote me)
name
(list (quote dict) "sender" (quote me))))))))
(define (define
emit-repeat emit-repeat
(fn (fn
@@ -650,11 +659,15 @@
((number? ast) ast) ((number? ast) ast)
((string? ast) ast) ((string? ast) ast)
((boolean? ast) ast) ((boolean? ast) ast)
((and (symbol? ast) (= (str ast) "sender"))
(list (quote hs-sender) (quote event)))
((not (list? ast)) ast) ((not (list? ast)) ast)
(true (true
(let (let
((head (first ast))) ((head (first ast)))
(cond (cond
((= head (quote sender))
(list (quote hs-sender) (quote event)))
((= head (quote null-literal)) nil) ((= head (quote null-literal)) nil)
((= head (quote not)) ((= head (quote not))
(list (quote not) (hs-to-sx (nth ast 1)))) (list (quote not) (hs-to-sx (nth ast 1))))

View File

@@ -151,6 +151,8 @@
(do (adv!) (parse-the-expr))) (do (adv!) (parse-the-expr)))
((and (= typ "keyword") (= val "me")) ((and (= typ "keyword") (= val "me"))
(do (adv!) (list (quote me)))) (do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "sender"))
(do (adv!) (list (quote sender))))
((and (= typ "keyword") (= val "I")) ((and (= typ "keyword") (= val "I"))
(do (adv!) (list (quote me)))) (do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "it")) ((and (= typ "keyword") (= val "it"))

View File

@@ -687,6 +687,14 @@
(dom-insert-adjacent-html target "beforeend" (str value))) (dom-insert-adjacent-html target "beforeend" (str value)))
(true nil))))) (true nil)))))
;; Property-based is — check obj.key truthiness ;; Property-based is — check obj.key truthiness
(define
hs-sender
(fn
(event)
(let
((detail (host-get event "detail")))
(if detail (host-get detail "sender") nil))))
;; Array slicing (inclusive both ends)
(define (define
hs-host-to-sx hs-host-to-sx
(fn (fn
@@ -740,7 +748,7 @@
(dict-set! out k (hs-host-to-sx (host-get v k)))) (dict-set! out k (hs-host-to-sx (host-get v k))))
(host-call (host-global "Object") "keys" v)) (host-call (host-global "Object") "keys" v))
out))))))))))) out)))))))))))
;; Array slicing (inclusive both ends) ;; Collection: sorted by
(define (define
hs-fetch hs-fetch
(fn (fn
@@ -750,7 +758,7 @@
(let (let
((raw (perform (list "io-fetch" url fmt)))) ((raw (perform (list "io-fetch" url fmt))))
(cond ((= fmt "json") (hs-host-to-sx raw)) (true raw)))))) (cond ((= fmt "json") (hs-host-to-sx raw)) (true raw))))))
;; Collection: sorted by ;; Collection: sorted by descending
(define (define
hs-json-escape hs-json-escape
(fn (fn
@@ -781,7 +789,7 @@
(walk) (walk)
out) out)
"\""))) "\"")))
;; Collection: sorted by descending ;; Collection: split by
(define (define
hs-json-stringify hs-json-stringify
(fn (fn
@@ -815,7 +823,7 @@
ks))) ks)))
"}"))) "}")))
(true (hs-json-escape (str v)))))) (true (hs-json-escape (str v))))))
;; Collection: split by ;; Collection: joined by
(define (define
hs-coerce hs-coerce
(fn (fn
@@ -929,7 +937,7 @@
(map (fn (k) (list k (get value k))) (keys value)) (map (fn (k) (list k (get value k))) (keys value))
value)) value))
(true value)))) (true value))))
;; Collection: joined by
(define (define
hs-gather-form-nodes hs-gather-form-nodes
(fn (fn

View File

@@ -264,8 +264,17 @@
name name
(hs-to-sx (nth ast 2)))) (hs-to-sx (nth ast 2))))
((= (len ast) 3) ((= (len ast) 3)
(list (quote dom-dispatch) (hs-to-sx (nth ast 2)) name nil)) (list
(true (list (quote dom-dispatch) (quote me) name nil)))))) (quote dom-dispatch)
(hs-to-sx (nth ast 2))
name
(list (quote dict) "sender" (quote me))))
(true
(list
(quote dom-dispatch)
(quote me)
name
(list (quote dict) "sender" (quote me))))))))
(define (define
emit-repeat emit-repeat
(fn (fn
@@ -650,11 +659,15 @@
((number? ast) ast) ((number? ast) ast)
((string? ast) ast) ((string? ast) ast)
((boolean? ast) ast) ((boolean? ast) ast)
((and (symbol? ast) (= (str ast) "sender"))
(list (quote hs-sender) (quote event)))
((not (list? ast)) ast) ((not (list? ast)) ast)
(true (true
(let (let
((head (first ast))) ((head (first ast)))
(cond (cond
((= head (quote sender))
(list (quote hs-sender) (quote event)))
((= head (quote null-literal)) nil) ((= head (quote null-literal)) nil)
((= head (quote not)) ((= head (quote not))
(list (quote not) (hs-to-sx (nth ast 1)))) (list (quote not) (hs-to-sx (nth ast 1))))

View File

@@ -151,6 +151,8 @@
(do (adv!) (parse-the-expr))) (do (adv!) (parse-the-expr)))
((and (= typ "keyword") (= val "me")) ((and (= typ "keyword") (= val "me"))
(do (adv!) (list (quote me)))) (do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "sender"))
(do (adv!) (list (quote sender))))
((and (= typ "keyword") (= val "I")) ((and (= typ "keyword") (= val "I"))
(do (adv!) (list (quote me)))) (do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "it")) ((and (= typ "keyword") (= val "it"))

View File

@@ -687,6 +687,14 @@
(dom-insert-adjacent-html target "beforeend" (str value))) (dom-insert-adjacent-html target "beforeend" (str value)))
(true nil))))) (true nil)))))
;; Property-based is — check obj.key truthiness ;; Property-based is — check obj.key truthiness
(define
hs-sender
(fn
(event)
(let
((detail (host-get event "detail")))
(if detail (host-get detail "sender") nil))))
;; Array slicing (inclusive both ends)
(define (define
hs-host-to-sx hs-host-to-sx
(fn (fn
@@ -740,7 +748,7 @@
(dict-set! out k (hs-host-to-sx (host-get v k)))) (dict-set! out k (hs-host-to-sx (host-get v k))))
(host-call (host-global "Object") "keys" v)) (host-call (host-global "Object") "keys" v))
out))))))))))) out)))))))))))
;; Array slicing (inclusive both ends) ;; Collection: sorted by
(define (define
hs-fetch hs-fetch
(fn (fn
@@ -750,7 +758,7 @@
(let (let
((raw (perform (list "io-fetch" url fmt)))) ((raw (perform (list "io-fetch" url fmt))))
(cond ((= fmt "json") (hs-host-to-sx raw)) (true raw)))))) (cond ((= fmt "json") (hs-host-to-sx raw)) (true raw))))))
;; Collection: sorted by ;; Collection: sorted by descending
(define (define
hs-json-escape hs-json-escape
(fn (fn
@@ -781,7 +789,7 @@
(walk) (walk)
out) out)
"\""))) "\"")))
;; Collection: sorted by descending ;; Collection: split by
(define (define
hs-json-stringify hs-json-stringify
(fn (fn
@@ -815,7 +823,7 @@
ks))) ks)))
"}"))) "}")))
(true (hs-json-escape (str v)))))) (true (hs-json-escape (str v))))))
;; Collection: split by ;; Collection: joined by
(define (define
hs-coerce hs-coerce
(fn (fn
@@ -929,7 +937,7 @@
(map (fn (k) (list k (get value k))) (keys value)) (map (fn (k) (list k (get value k))) (keys value))
value)) value))
(true value)))) (true value))))
;; Collection: joined by
(define (define
hs-gather-form-nodes hs-gather-form-nodes
(fn (fn