go: eval.sx scaffold — literals + vars + binops + 25 tests; Phase 3 closed [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Phase 3 — bidirectional type checker — is fully ticked (short-decl
was already implemented). Phase 4 starts here.
lib/go/eval.sx single judgment:
(go-eval ENV EXPR) → VALUE | (list :eval-error TAG ...)
ENV is an association list of (NAME VALUE) bindings — same shape as
the type checker's ctx, but the entries are runtime values. Values
are represented directly in SX: integers/floats as SX numbers,
strings as SX strings, booleans as true/false, nil as nil. Composite
values (slices/maps/structs/pointers/channels) arrive in later slices.
First-slice coverage:
* go-env-empty / -lookup / -extend
* Literal decoding:
decimal (with underscores)
hex (0x.. / 0X..)
oct (0o.. / 0O..)
bin (0b.. / 0B..)
via go-hex-digit-value (explicit char equality — SX's nth on
strings returns single-char strings, not numeric codes; the
arithmetic-on-char-codes pattern from the OCaml kernel ports
doesn't work here).
* Identifier lookup with predeclared true / false / nil.
* Binops: + - * / and the six comparison ops and && / ||.
* Errors as (:eval-error TAG ...) sentinels.
Statements (block / return / short-decl / assign), control flow
(if / for), and function application / closures arrive in subsequent
slices.
eval 25/25, total 402/402.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ SUITES=(
|
||||
"lex|go-test-pass|go-test-count"
|
||||
"parse|go-parse-test-pass|go-parse-test-count"
|
||||
"types|go-types-test-pass|go-types-test-count"
|
||||
"eval|go-eval-test-pass|go-eval-test-count"
|
||||
)
|
||||
|
||||
cat > "$TMPFILE" <<'EPOCHS'
|
||||
@@ -40,9 +41,11 @@ cat > "$TMPFILE" <<'EPOCHS'
|
||||
(load "lib/go/lex.sx")
|
||||
(load "lib/go/parse.sx")
|
||||
(load "lib/go/types.sx")
|
||||
(load "lib/go/eval.sx")
|
||||
(load "lib/go/tests/lex.sx")
|
||||
(load "lib/go/tests/parse.sx")
|
||||
(load "lib/go/tests/types.sx")
|
||||
(load "lib/go/tests/eval.sx")
|
||||
EPOCHS
|
||||
|
||||
idx=0
|
||||
@@ -107,7 +110,6 @@ cat > lib/go/scoreboard.json <<JSON
|
||||
"total_pass": $TOTAL_PASS,
|
||||
"total": $TOTAL_COUNT,
|
||||
"suites": [$JSON_SUITES,
|
||||
{"name":"eval","pass":0,"total":0,"status":"pending"},
|
||||
{"name":"runtime","pass":0,"total":0,"status":"pending"},
|
||||
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
|
||||
{"name":"e2e","pass":0,"total":0,"status":"pending"}
|
||||
@@ -122,8 +124,7 @@ cat > lib/go/scoreboard.md <<MD
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
$MD_ROWS| ⬜ | eval | 0 | 0 |
|
||||
| ⬜ | runtime | 0 | 0 |
|
||||
$MD_ROWS| ⬜ | runtime | 0 | 0 |
|
||||
| ⬜ | stdlib | 0 | 0 |
|
||||
| ⬜ | e2e | 0 | 0 |
|
||||
|
||||
|
||||
215
lib/go/eval.sx
Normal file
215
lib/go/eval.sx
Normal file
@@ -0,0 +1,215 @@
|
||||
;; lib/go/eval.sx — Go tree-walk evaluator.
|
||||
;;
|
||||
;; (go-eval ENV EXPR) → VALUE | (list :eval-error TAG ...)
|
||||
;;
|
||||
;; ENV is an association list of (NAME VALUE) bindings. Per-block scope
|
||||
;; via fresh extension. Values:
|
||||
;; integers → SX numbers (decimal/hex/oct/bin literals all decoded)
|
||||
;; strings → SX strings
|
||||
;; booleans → SX true/false
|
||||
;; nil → SX nil
|
||||
;; Composite Go values (slices, maps, structs, pointers, channels)
|
||||
;; arrive in later slices.
|
||||
|
||||
(define go-env-empty (list))
|
||||
|
||||
(define
|
||||
go-env-lookup
|
||||
(fn
|
||||
(env name)
|
||||
(cond
|
||||
(= (len env) 0)
|
||||
nil
|
||||
(= (first (first env)) name)
|
||||
(nth (first env) 1)
|
||||
:else (go-env-lookup (rest env) name))))
|
||||
|
||||
(define go-env-extend (fn (env name value) (cons (list name value) env)))
|
||||
|
||||
(define
|
||||
go-eval-error?
|
||||
(fn
|
||||
(x)
|
||||
(and
|
||||
(list? x)
|
||||
(not (= (len x) 0))
|
||||
(= (first x) :eval-error))))
|
||||
|
||||
;; ── literal parsing ──────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-hex-digit-value
|
||||
(fn
|
||||
(c)
|
||||
(cond
|
||||
(= c "0")
|
||||
0
|
||||
(= c "1")
|
||||
1
|
||||
(= c "2")
|
||||
2
|
||||
(= c "3")
|
||||
3
|
||||
(= c "4")
|
||||
4
|
||||
(= c "5")
|
||||
5
|
||||
(= c "6")
|
||||
6
|
||||
(= c "7")
|
||||
7
|
||||
(= c "8")
|
||||
8
|
||||
(= c "9")
|
||||
9
|
||||
(= c "a")
|
||||
10
|
||||
(= c "b")
|
||||
11
|
||||
(= c "c")
|
||||
12
|
||||
(= c "d")
|
||||
13
|
||||
(= c "e")
|
||||
14
|
||||
(= c "f")
|
||||
15
|
||||
(= c "A")
|
||||
10
|
||||
(= c "B")
|
||||
11
|
||||
(= c "C")
|
||||
12
|
||||
(= c "D")
|
||||
13
|
||||
(= c "E")
|
||||
14
|
||||
(= c "F")
|
||||
15
|
||||
:else -1)))
|
||||
|
||||
(define
|
||||
go-parse-radix-from
|
||||
(fn
|
||||
(v start radix)
|
||||
(define
|
||||
grf-loop
|
||||
(fn
|
||||
(i acc)
|
||||
(cond
|
||||
(>= i (len v))
|
||||
acc
|
||||
(= (nth v i) "_")
|
||||
(grf-loop (+ i 1) acc)
|
||||
:else (let
|
||||
((d (go-hex-digit-value (nth v i))))
|
||||
(cond
|
||||
(or (< d 0) (>= d radix))
|
||||
acc
|
||||
:else (grf-loop (+ i 1) (+ (* acc radix) d)))))))
|
||||
(grf-loop start 0)))
|
||||
|
||||
(define
|
||||
go-parse-int-literal
|
||||
(fn
|
||||
(v)
|
||||
(cond
|
||||
(and
|
||||
(>= (len v) 2)
|
||||
(= (nth v 0) "0")
|
||||
(or (= (nth v 1) "x") (= (nth v 1) "X")))
|
||||
(go-parse-radix-from v 2 16)
|
||||
(and
|
||||
(>= (len v) 2)
|
||||
(= (nth v 0) "0")
|
||||
(or (= (nth v 1) "b") (= (nth v 1) "B")))
|
||||
(go-parse-radix-from v 2 2)
|
||||
(and
|
||||
(>= (len v) 2)
|
||||
(= (nth v 0) "0")
|
||||
(or (= (nth v 1) "o") (= (nth v 1) "O")))
|
||||
(go-parse-radix-from v 2 8)
|
||||
:else (go-parse-radix-from v 0 10))))
|
||||
|
||||
(define
|
||||
go-eval-literal
|
||||
(fn
|
||||
(v)
|
||||
(let
|
||||
((k (go-classify-literal-string v)))
|
||||
(cond (= k :int) (go-parse-int-literal v) (= k :string) v :else v))))
|
||||
|
||||
;; ── binary ops ───────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-eval-binop
|
||||
(fn
|
||||
(op l r)
|
||||
(cond
|
||||
(= op "+")
|
||||
(+ l r)
|
||||
(= op "-")
|
||||
(- l r)
|
||||
(= op "*")
|
||||
(* l r)
|
||||
(= op "/")
|
||||
(/ l r)
|
||||
(= op "==")
|
||||
(= l r)
|
||||
(= op "!=")
|
||||
(not (= l r))
|
||||
(= op "<")
|
||||
(< l r)
|
||||
(= op "<=")
|
||||
(<= l r)
|
||||
(= op ">")
|
||||
(> l r)
|
||||
(= op ">=")
|
||||
(>= l r)
|
||||
(= op "&&")
|
||||
(and l r)
|
||||
(= op "||")
|
||||
(or l r)
|
||||
:else (list :eval-error :unsupported-binop op))))
|
||||
|
||||
;; ── main eval ────────────────────────────────────────────────────
|
||||
|
||||
(define
|
||||
go-eval
|
||||
(fn
|
||||
(env expr)
|
||||
(cond
|
||||
(and (list? expr) (= (first expr) :literal))
|
||||
(go-eval-literal (nth expr 1))
|
||||
(and (list? expr) (= (first expr) :var))
|
||||
(let
|
||||
((name (nth expr 1)))
|
||||
(cond
|
||||
(= name "true")
|
||||
true
|
||||
(= name "false")
|
||||
false
|
||||
(= name "nil")
|
||||
nil
|
||||
:else (let
|
||||
((v (go-env-lookup env name)))
|
||||
(cond (= v nil) (list :eval-error :unbound name) :else v))))
|
||||
(and
|
||||
(list? expr)
|
||||
(= (first expr) :app)
|
||||
(list? (nth expr 1))
|
||||
(= (first (nth expr 1)) :var)
|
||||
(= (len (nth expr 2)) 2))
|
||||
(let
|
||||
((op (nth (nth expr 1) 1))
|
||||
(args (nth expr 2)))
|
||||
(let
|
||||
((lv (go-eval env (first args)))
|
||||
(rv (go-eval env (nth args 1))))
|
||||
(cond
|
||||
(go-eval-error? lv)
|
||||
lv
|
||||
(go-eval-error? rv)
|
||||
rv
|
||||
:else (go-eval-binop op lv rv))))
|
||||
:else (list :eval-error :unsupported-eval expr))))
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"language": "go",
|
||||
"total_pass": 377,
|
||||
"total": 377,
|
||||
"total_pass": 402,
|
||||
"total": 402,
|
||||
"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":0,"total":0,"status":"pending"},
|
||||
{"name":"eval","pass":25,"total":25,"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"}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Go-on-SX Scoreboard
|
||||
|
||||
**Total: 377 / 377 tests passing**
|
||||
**Total: 402 / 402 tests passing**
|
||||
|
||||
| | Suite | Pass | Total |
|
||||
|---|---|---|---|
|
||||
| ✅ | lex | 129 | 129 |
|
||||
| ✅ | parse | 176 | 176 |
|
||||
| ✅ | types | 72 | 72 |
|
||||
| ⬜ | eval | 0 | 0 |
|
||||
| ✅ | eval | 25 | 25 |
|
||||
| ⬜ | runtime | 0 | 0 |
|
||||
| ⬜ | stdlib | 0 | 0 |
|
||||
| ⬜ | e2e | 0 | 0 |
|
||||
|
||||
95
lib/go/tests/eval.sx
Normal file
95
lib/go/tests/eval.sx
Normal file
@@ -0,0 +1,95 @@
|
||||
;; Go evaluator tests.
|
||||
|
||||
(define go-eval-test-count 0)
|
||||
(define go-eval-test-pass 0)
|
||||
(define go-eval-test-fails (list))
|
||||
|
||||
(define
|
||||
go-eval-test
|
||||
(fn
|
||||
(name actual expected)
|
||||
(set! go-eval-test-count (+ go-eval-test-count 1))
|
||||
(if
|
||||
(= actual expected)
|
||||
(set! go-eval-test-pass (+ go-eval-test-pass 1))
|
||||
(append! go-eval-test-fails {:name name :expected expected :actual actual}))))
|
||||
|
||||
(define gtev (fn (env src) (go-eval env (go-parse src))))
|
||||
|
||||
;; ── env ──────────────────────────────────────────────────────────
|
||||
(go-eval-test
|
||||
"env: empty lookup returns nil"
|
||||
(go-env-lookup go-env-empty "x")
|
||||
nil)
|
||||
|
||||
(go-eval-test
|
||||
"env: extend then lookup"
|
||||
(go-env-lookup (go-env-extend go-env-empty "x" 42) "x")
|
||||
42)
|
||||
|
||||
;; ── literals ────────────────────────────────────────────────────
|
||||
(go-eval-test "lit: 42 → 42" (gtev go-env-empty "42") 42)
|
||||
|
||||
(go-eval-test "lit: 0 → 0" (gtev go-env-empty "0") 0)
|
||||
|
||||
(go-eval-test "lit: 0xFF → 255" (gtev go-env-empty "0xFF") 255)
|
||||
|
||||
(go-eval-test "lit: 0b1010 → 10" (gtev go-env-empty "0b1010") 10)
|
||||
|
||||
(go-eval-test "lit: 0o17 → 15" (gtev go-env-empty "0o17") 15)
|
||||
|
||||
(go-eval-test
|
||||
"lit: underscore separator 1_000 → 1000"
|
||||
(gtev go-env-empty "1_000")
|
||||
1000)
|
||||
|
||||
(go-eval-test "lit: string" (gtev go-env-empty "\"hello\"") "hello")
|
||||
|
||||
;; ── predeclared ─────────────────────────────────────────────────
|
||||
(go-eval-test "var: true" (gtev go-env-empty "true") true)
|
||||
(go-eval-test "var: false" (gtev go-env-empty "false") false)
|
||||
(go-eval-test "var: nil" (gtev go-env-empty "nil") nil)
|
||||
|
||||
;; ── variable lookup ─────────────────────────────────────────────
|
||||
(go-eval-test
|
||||
"var: bound x → 5"
|
||||
(go-eval (go-env-extend go-env-empty "x" 5) (go-parse "x"))
|
||||
5)
|
||||
|
||||
(go-eval-test
|
||||
"var: unbound y → :eval-error"
|
||||
(gtev go-env-empty "y")
|
||||
(list :eval-error :unbound "y"))
|
||||
|
||||
;; ── binary ops ─────────────────────────────────────────────────
|
||||
(go-eval-test "binop: 1 + 2 → 3" (gtev go-env-empty "1 + 2") 3)
|
||||
(go-eval-test "binop: 10 - 4 → 6" (gtev go-env-empty "10 - 4") 6)
|
||||
(go-eval-test "binop: 3 * 7 → 21" (gtev go-env-empty "3 * 7") 21)
|
||||
(go-eval-test "binop: 42 / 7 → 6" (gtev go-env-empty "42 / 7") 6)
|
||||
(go-eval-test
|
||||
"binop: 2 + 3 * 4 → 14 (prec)"
|
||||
(gtev go-env-empty "2 + 3 * 4")
|
||||
14)
|
||||
(go-eval-test
|
||||
"binop: a + b uses env"
|
||||
(go-eval
|
||||
(go-env-extend (go-env-extend go-env-empty "a" 3) "b" 4)
|
||||
(go-parse "a + b"))
|
||||
7)
|
||||
|
||||
(go-eval-test "binop: 1 < 2 → true" (gtev go-env-empty "1 < 2") true)
|
||||
(go-eval-test "binop: 5 == 5 → true" (gtev go-env-empty "5 == 5") true)
|
||||
(go-eval-test "binop: 5 != 5 → false" (gtev go-env-empty "5 != 5") false)
|
||||
(go-eval-test
|
||||
"binop: true && false → false"
|
||||
(gtev go-env-empty "true && false")
|
||||
false)
|
||||
(go-eval-test
|
||||
"binop: false || true → true"
|
||||
(gtev go-env-empty "false || true")
|
||||
true)
|
||||
|
||||
;; ── report ──────────────────────────────────────────────────────
|
||||
(define
|
||||
go-eval-test-summary
|
||||
(str "eval " go-eval-test-pass "/" go-eval-test-count))
|
||||
@@ -213,7 +213,7 @@ Progress-log line → push `origin/loops/go`.
|
||||
**Phase 2 complete.** Type-switch is the one syntactic shape still
|
||||
deferred to a follow-up; it doesn't gate Phase 3.
|
||||
|
||||
### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜
|
||||
### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ✅
|
||||
- [x] Scaffold: `go-synth` / `go-check` skeletons; context-as-value
|
||||
(`go-ctx-empty` / `-extend` / `-lookup` / `-extend-field`);
|
||||
predeclared `true`/`false`/`nil`; structural type equality.
|
||||
@@ -255,7 +255,9 @@ Progress-log line → push `origin/loops/go`.
|
||||
value and pointer receivers). `go-iface-satisfies?` walks an
|
||||
interface's `:method` elements and looks each up; partial sets
|
||||
and arity-mismatches fail. Embedded interfaces deferred.
|
||||
- [ ] Short variable declaration `:=` (synth RHS into LHS bindings).
|
||||
- [x] Short variable declaration `:=` (synth RHS into LHS bindings).
|
||||
Handled inline by `go-check-short-decl` since the decl-checking
|
||||
slice; works both at top-level and inside `for`/`if` init clauses.
|
||||
- Defer: generics (Phase 7), full conversion rules, type assertions,
|
||||
type switches.
|
||||
- **Acceptance:** types/ suite at 60+ tests. **Bar crossed: 72/72.**
|
||||
@@ -265,19 +267,22 @@ Progress-log line → push `origin/loops/go`.
|
||||
cross-language record.
|
||||
|
||||
### Phase 4 — Tree-walk evaluator (`lib/go/eval.sx`) ⬜
|
||||
- AST-walking interpreter over CEK. Each Go statement maps to one step
|
||||
function (precedent: `step-sf-if` etc. in spec/evaluator.sx).
|
||||
- Variables: mutable cells. Pointer semantics: `&x` returns the cell,
|
||||
`*p` dereferences.
|
||||
- Slices: triple (length, capacity, backing-vector). `append` honours
|
||||
capacity-grow per spec.
|
||||
- Maps: SX dict + key-type metadata.
|
||||
- Structs: SX dict + type tag. Methods looked up via type's method table.
|
||||
- Functions: closures over enclosing scope; multiple return values.
|
||||
- Channels: stub (Phase 5 wires them).
|
||||
- [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 / for / break / continue.
|
||||
- [ ] Variables as mutable cells; pointer semantics: `&x` returns the
|
||||
cell, `*p` dereferences.
|
||||
- [ ] Slices: triple (length, capacity, backing-vector). `append`
|
||||
honours capacity-grow per spec.
|
||||
- [ ] Maps: SX dict + key-type metadata.
|
||||
- [ ] Structs: SX dict + type tag. Methods looked up via type's table.
|
||||
- [ ] Functions: closures over enclosing scope; multiple return values.
|
||||
- [ ] 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. No concurrency yet.
|
||||
- **Acceptance:** eval/ suite at 80+ tests. Current: 25/25. No concurrency yet.
|
||||
|
||||
### Phase 5 — Goroutines + channels + select (`lib/go/sched.sx`) ⬜
|
||||
- **Independent implementation.** Do NOT use lib/guest/scheduler/ — that
|
||||
@@ -561,6 +566,19 @@ 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 3 ticked; Phase 4 scaffold.** Short-decl `:=`
|
||||
marked done (was already covered by go-check-short-decl from the
|
||||
decl-checking iteration). New `lib/go/eval.sx`: env-as-value (same
|
||||
shape as ctx but bound to runtime values), literal decoding for
|
||||
decimal/hex/oct/bin int literals (with underscores), variable lookup,
|
||||
predeclared `true`/`false`/`nil`, and the full set of arithmetic /
|
||||
comparison / logical binops via `go-eval-binop`. Hex/oct/bin parsing
|
||||
via `go-hex-digit-value` (explicit char-equality dispatch since SX's
|
||||
nth-on-string returns single-char strings, not numeric codes —
|
||||
cleaner than the char-arithmetic the kernel ports use). eval suite
|
||||
25/25, total 402/402. `[nothing]` — pure Go eval mechanics, the
|
||||
cross-language insights are about type-checking which is in the
|
||||
sister-plan diary.
|
||||
- 2026-05-27 — Phase 3 cont.: **interface satisfaction** — the headline
|
||||
Go-distinguishing typing feature this loop set out to validate.
|
||||
Method decls record under `#method/TYPE-NAME/METHOD-NAME` keys in
|
||||
|
||||
Reference in New Issue
Block a user