go: eval.sx — for / break / continue / inc-dec + 7 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s

Phase 4 cont. go-eval-for handles all three for-header shapes:

  for { ... }                          — infinite (cond defaults to true)
  for cond { ... }                     — while-like (init=nil, post=nil)
  for init ; cond ; post { ... }       — C-style

Implementation:
  * Run INIT (if any), extending env.
  * Loop: eval COND. If false, exit with current env.
    Eval body (a :block). Catch sentinels:
      :return-value → propagate up
      :break        → exit loop with pre-break env
      :continue     → still runs POST, then re-loops
    Otherwise: run POST, re-loop.

:break and :continue propagate as keyword sentinels through
go-eval-block alongside the existing :return-value sentinel. The
block returns whichever sentinel hit first; control-flow constructs
(for, switch, select) catch them.

inc-dec (x++ / x--) updates env via the same shadowing model used by
assign — `(go-env-extend env name (+ current 1))`.

**Iterative fact(5) = 120 and the classic sum-to-9 = 45 both
evaluate.** Demonstrates the for-loop machinery is solid enough for
real programs.

eval 40/40, total 417/417.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 21:22:34 +00:00
parent 1340c2626b
commit a019aa1edc
5 changed files with 149 additions and 8 deletions

View File

@@ -348,6 +348,78 @@
(body (nth stmt 4)))
(go-env-extend env name (list :go-fn params body)))))
(define
go-eval-inc-dec
;; (:inc-dec OP EXPR) where OP is "++" or "--". EXPR should be (:var NAME).
(fn (env stmt)
(let ((op (nth stmt 1)) (operand (nth stmt 2)))
(cond
(not (and (list? operand) (= (first operand) :var)))
(list :eval-error :unsupported-lhs operand)
:else
(let ((current (go-eval env operand)))
(cond
(go-eval-error? current) current
:else
(let ((new-val
(cond
(= op "++") (+ current 1)
(= op "--") (- current 1)
:else current)))
(go-env-extend env (nth operand 1) new-val))))))))
(define
go-eval-for
;; (:for INIT COND POST BODY). Any may be nil.
(fn (env stmt)
(let ((init (nth stmt 1)) (cnd (nth stmt 2))
(post (nth stmt 3)) (body (nth stmt 4)))
(let ((env0
(cond
(= init nil) env
:else (go-eval-stmt env init))))
(cond
(go-eval-error? env0) env0
:else (go-for-loop env0 cnd post body))))))
(define
go-for-loop
(fn (env cnd post body)
(let ((c
(cond
(= cnd nil) true
:else (go-eval env cnd))))
(cond
(go-eval-error? c) c
(not c) env
:else
(let ((r
(cond
(= body nil) env
(and (list? body) (= (first body) :block))
(go-eval-block env (nth body 1))
:else env)))
(cond
(and (list? r) (= (first r) :return-value)) r
(= r :break) env
(= r :continue)
(let ((env1
(cond
(= post nil) env
:else (go-eval-stmt env post))))
(cond
(go-eval-error? env1) env1
:else (go-for-loop env1 cnd post body)))
(go-eval-error? r) r
:else
(let ((env1
(cond
(= post nil) r
:else (go-eval-stmt r post))))
(cond
(go-eval-error? env1) env1
:else (go-for-loop env1 cnd post body)))))))))
(define
go-eval-stmt
(fn (env stmt)
@@ -372,6 +444,12 @@
(go-eval-block env (nth stmt 1))
(and (list? stmt) (= (first stmt) :if))
(go-eval-if env stmt)
(and (list? stmt) (= (first stmt) :for))
(go-eval-for env stmt)
(and (list? stmt) (= (first stmt) :break)) :break
(and (list? stmt) (= (first stmt) :continue)) :continue
(and (list? stmt) (= (first stmt) :inc-dec))
(go-eval-inc-dec env stmt)
(and (list? stmt) (= (first stmt) :func-decl))
(go-eval-func-decl env stmt)
:else
@@ -389,6 +467,8 @@
(let ((r (go-eval-stmt env (first stmts))))
(cond
(and (list? r) (= (first r) :return-value)) r
(= r :break) r
(= r :continue) r
(go-eval-error? r) r
:else (go-eval-block r (rest stmts)))))))

View File

@@ -1,12 +1,12 @@
{
"language": "go",
"total_pass": 410,
"total": 410,
"total_pass": 417,
"total": 417,
"suites": [
{"name":"lex","pass":129,"total":129,"status":"ok"},
{"name":"parse","pass":176,"total":176,"status":"ok"},
{"name":"types","pass":72,"total":72,"status":"ok"},
{"name":"eval","pass":33,"total":33,"status":"ok"},
{"name":"eval","pass":40,"total":40,"status":"ok"},
{"name":"runtime","pass":0,"total":0,"status":"pending"},
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
{"name":"e2e","pass":0,"total":0,"status":"pending"}

View File

@@ -1,13 +1,13 @@
# Go-on-SX Scoreboard
**Total: 410 / 410 tests passing**
**Total: 417 / 417 tests passing**
| | Suite | Pass | Total |
|---|---|---|---|
| ✅ | lex | 129 | 129 |
| ✅ | parse | 176 | 176 |
| ✅ | types | 72 | 72 |
| ✅ | eval | 33 | 33 |
| ✅ | eval | 40 | 40 |
| ⬜ | runtime | 0 | 0 |
| ⬜ | stdlib | 0 | 0 |
| ⬜ | e2e | 0 | 0 |

View File

@@ -146,6 +146,55 @@
(go-eval env (go-parse "fib(5)")))
5)
(go-eval-test
"for: count to 10 with sum"
(let
((env (go-eval-program go-env-empty (list (go-parse "var sum = 0") (go-parse "for i := 0; i < 10; i++ { sum = sum + i }")))))
(go-env-lookup env "sum"))
45)
(go-eval-test
"inc-dec: x++ updates env"
(let
((env (go-eval-program (go-env-extend go-env-empty "x" 5) (list (go-parse "x++")))))
(go-env-lookup env "x"))
6)
(go-eval-test
"inc-dec: x-- updates env"
(let
((env (go-eval-program (go-env-extend go-env-empty "x" 5) (list (go-parse "x--")))))
(go-env-lookup env "x"))
4)
(go-eval-test
"for: break exits the loop"
(let
((env (go-eval-program go-env-empty (list (go-parse "var i = 0") (go-parse "for i < 100 { if i == 5 { break } ; i++ }")))))
(go-env-lookup env "i"))
5)
(go-eval-test
"for: continue skips body but runs post"
(let
((env (go-eval-program go-env-empty (list (go-parse "var sum = 0") (go-parse "for i := 0; i < 5; i++ { if i == 2 { continue } ; sum = sum + i }")))))
(go-env-lookup env "sum"))
8)
(go-eval-test
"for: infinite + break with sum"
(let
((env (go-eval-program go-env-empty (list (go-parse "var s = 0") (go-parse "var i = 1") (go-parse "for { if i > 4 { break } ; s = s + i ; i++ }")))))
(go-env-lookup env "s"))
10)
(go-eval-test
"fn: iterative factorial via for-loop"
(let
((env (go-eval-program go-env-empty (list (go-parse "func fact(n int) int { r := 1 ; for i := 2 ; i <= n ; i++ { r = r * i } ; return r }")))))
(go-eval env (go-parse "fact(5)")))
120)
(define
go-eval-test-summary
(str "eval " go-eval-test-pass "/" go-eval-test-count))

View File

@@ -270,8 +270,10 @@ Progress-log line → push `origin/loops/go`.
- [x] Scaffold: env-as-value, literal decoding (decimal/hex/oct/bin
with underscores), variable lookup (incl. predeclared true/false/nil),
arithmetic + comparison + logical binops. eval suite at 25/25.
- [/] Statement evaluation: block / return / short-decl / assign /
var-decl / if done; for / break / continue pending.
- [x] Statement evaluation: block / return / short-decl / assign /
var-decl / if / for (all three header shapes) / break / continue /
inc-dec all done. `:break` and `:continue` propagate as sentinel
keywords through `go-eval-block` until `go-for-loop` catches them.
- [ ] Variables as mutable cells; pointer semantics: `&x` returns the
cell, `*p` dereferences.
- [ ] Slices: triple (length, capacity, backing-vector). `append`
@@ -283,7 +285,7 @@ Progress-log line → push `origin/loops/go`.
- [ ] Channels: stub (Phase 5 wires them).
- Tests: arithmetic, control flow, recursion, closures, slices, maps,
structs, methods, pointer semantics, multiple-return.
- **Acceptance:** eval/ suite at 80+ tests. Current: 33/33. No concurrency yet.
- **Acceptance:** eval/ suite at 80+ tests. Current: 40/40. No concurrency yet.
### Phase 5 — Goroutines + channels + select (`lib/go/sched.sx`) ⬜
- **Independent implementation.** Do NOT use lib/guest/scheduler/ — that
@@ -567,6 +569,16 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
_Newest first. Append one dated entry per commit._
- 2026-05-27 — Phase 4 cont.: for-loops, break, continue, inc-dec.
`go-eval-for` handles all three for-header shapes (infinite, while-
like, C-style) including init+post stmts and missing-cond defaulting
to true. `:break` and `:continue` propagate as keyword sentinels
through `go-eval-block` (alongside the existing `:return-value`
sentinel) until `go-for-loop` catches them — break exits, continue
runs post and re-loops. Inc-dec `x++`/`x--` updates env via the
same shadowing model as assign. **Iterative `fact(5) = 120` and the
classic for-loop sum-to-9 (= 45) both evaluate.** +7 tests, eval
40/40, total 417/417. `[nothing]`.
- 2026-05-27 — Phase 4 cont.: statements + function application.
`go-eval-stmt` handles `:return` (propagates a `:return-value`
sentinel up through blocks), `:var-decl`, `:short-decl`, `:assign`