go: eval.sx — range-over-{slice,map,chan} + 7 tests; break-env fix [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Phase 5 cont. New go-eval-range-for handles the parser's :range-for
AST shape. Dispatches on the collection's runtime type:
:go-slice → bind index + element, iterate by position
:go-map → bind key + value, walk entries assoc list
:go-chan → bind value, drain until buffer empty (v0 limitation)
Each loop carries:
- go-range-extend: handles 0/1/2-name binding patterns uniformly
- go-range-body: evaluates body whether it's a :block or other shape
- per-collection loop helper: threads env, catches :break/:continue/
:return-value/:eval-error sentinels
**Subtle break fix:** loops were previously returning the *pre-loop*
env when break fired, clobbering all assignments made in prior
iterations. Now returns the current iteration's input env (which
carries forward successful iterations' state). Patched for the three
range variants and for the regular for-loop where the same pattern
applied. The shape:
(= r :break) env ;; was: (= r :break) original-env
Tests:
range: slice — sum of 1..5 = 15
range: slice — key only (index)
range: map — sum values
range: channel — collect all buffered
range: slice with break exits early
range: slice with continue skips an element
range: empty slice — body never runs
range: chan + goroutine producer
runtime 26/26, total 483/483.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
116
lib/go/eval.sx
116
lib/go/eval.sx
@@ -823,6 +823,8 @@
|
||||
;; Otherwise r is the env after the selected body ran;
|
||||
;; propagate so assignments inside cases stick.
|
||||
:else r))
|
||||
(and (list? stmt) (= (first stmt) :range-for))
|
||||
(go-eval-range-for env stmt)
|
||||
:else
|
||||
(let ((v (go-eval env stmt)))
|
||||
(cond
|
||||
@@ -926,6 +928,120 @@
|
||||
(fn (env stmt)
|
||||
(go-select-pick env (nth stmt 1) nil)))
|
||||
|
||||
(define
|
||||
go-ast-name
|
||||
;; Extract a name from a (:var NAME) ast, else nil.
|
||||
(fn (ast)
|
||||
(cond
|
||||
(and (list? ast) (= (first ast) :var)) (nth ast 1)
|
||||
:else nil)))
|
||||
|
||||
(define
|
||||
go-range-extend
|
||||
(fn (env key-name value-name k v)
|
||||
(cond
|
||||
(and (not (= key-name nil)) (not (= value-name nil)))
|
||||
(go-env-extend (go-env-extend env key-name k) value-name v)
|
||||
(not (= key-name nil)) (go-env-extend env key-name k)
|
||||
:else env)))
|
||||
|
||||
(define
|
||||
go-range-body
|
||||
;; Evaluate body in env. Returns env-or-sentinel.
|
||||
(fn (env body)
|
||||
(cond
|
||||
(and (list? body) (= (first body) :block))
|
||||
(go-eval-block env (nth body 1))
|
||||
:else env)))
|
||||
|
||||
(define
|
||||
go-range-slice-loop
|
||||
(fn (env elems i key-name value-name body original-env)
|
||||
(cond
|
||||
(>= i (len elems)) env
|
||||
:else
|
||||
(let ((env2 (go-range-extend env key-name value-name i
|
||||
(nth elems i))))
|
||||
(let ((r (go-range-body env2 body)))
|
||||
(cond
|
||||
(and (list? r) (= (first r) :return-value)) r
|
||||
(= r :break) env
|
||||
(= r :continue)
|
||||
(go-range-slice-loop env elems (+ i 1)
|
||||
key-name value-name body original-env)
|
||||
(go-eval-error? r) r
|
||||
:else
|
||||
(go-range-slice-loop r elems (+ i 1)
|
||||
key-name value-name body original-env)))))))
|
||||
|
||||
(define
|
||||
go-range-map-loop
|
||||
(fn (env entries key-name value-name body original-env)
|
||||
(cond
|
||||
(or (= entries nil) (= (len entries) 0)) env
|
||||
:else
|
||||
(let ((entry (first entries)))
|
||||
(let ((k (first entry)) (v (nth entry 1)))
|
||||
(let ((env2 (go-range-extend env key-name value-name k v)))
|
||||
(let ((r (go-range-body env2 body)))
|
||||
(cond
|
||||
(and (list? r) (= (first r) :return-value)) r
|
||||
(= r :break) env
|
||||
(= r :continue)
|
||||
(go-range-map-loop env (rest entries)
|
||||
key-name value-name body original-env)
|
||||
(go-eval-error? r) r
|
||||
:else
|
||||
(go-range-map-loop r (rest entries)
|
||||
key-name value-name body original-env)))))))))
|
||||
|
||||
(define
|
||||
go-range-chan-loop
|
||||
;; For chan: KEY-NAME receives each value. v0 stops when chan is
|
||||
;; empty (no preemption to wait for new values). Real Go waits on
|
||||
;; the chan until closed AND empty.
|
||||
(fn (env coll key-name body original-env)
|
||||
(cond
|
||||
(= (go-chan-len coll) 0) env
|
||||
:else
|
||||
(let ((v (go-chan-recv! coll)))
|
||||
(let ((env2
|
||||
(cond
|
||||
(not (= key-name nil)) (go-env-extend env key-name v)
|
||||
:else env)))
|
||||
(let ((r (go-range-body env2 body)))
|
||||
(cond
|
||||
(and (list? r) (= (first r) :return-value)) r
|
||||
(= r :break) env
|
||||
(= r :continue)
|
||||
(go-range-chan-loop env coll key-name body original-env)
|
||||
(go-eval-error? r) r
|
||||
:else
|
||||
(go-range-chan-loop r coll key-name body original-env))))))))
|
||||
|
||||
(define
|
||||
go-eval-range-for
|
||||
;; (:range-for DECL-KIND KEY VALUE COLL BODY)
|
||||
;; KEY/VALUE: (:var NAME) or nil
|
||||
;; COLL: an expression evaluating to slice / map / chan
|
||||
(fn (env stmt)
|
||||
(let ((key-name (go-ast-name (nth stmt 2)))
|
||||
(value-name (go-ast-name (nth stmt 3)))
|
||||
(coll-expr (nth stmt 4))
|
||||
(body (nth stmt 5)))
|
||||
(let ((coll (go-eval env coll-expr)))
|
||||
(cond
|
||||
(go-eval-error? coll) coll
|
||||
(and (list? coll) (= (first coll) :go-slice))
|
||||
(go-range-slice-loop env (nth coll 1) 0
|
||||
key-name value-name body env)
|
||||
(and (list? coll) (= (first coll) :go-map))
|
||||
(go-range-map-loop env (nth coll 1)
|
||||
key-name value-name body env)
|
||||
(and (list? coll) (= (first coll) :go-chan))
|
||||
(go-range-chan-loop env coll key-name body env)
|
||||
:else (list :eval-error :not-rangeable coll))))))
|
||||
|
||||
(define
|
||||
go-eval-method-decl
|
||||
;; (:method-decl RECV NAME PARAMS RESULTS BODY) — register the method
|
||||
|
||||
Reference in New Issue
Block a user