go: eval.sx — slices + index + slice expr + len/append builtins + 10 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Phase 4 cont. Adds runtime support for Go's slice type.
Slice representation: (list :go-slice ELEMS) — a simple wrapper around
a list of element values. v0 deferring the full
(length, capacity, backing-vector) triple from the Go spec until
programs need it.
go-eval-composite → for (:composite TYPE-OR-EXPR ELEMS) where
TYPE is :ty-slice / :ty-array, eval each
element (handling :kv index-keyed
shorthand by taking only the value) and
wrap in :go-slice.
go-eval-index → (:index OBJ IDX). Bounds-checked; out-of-
range returns (:eval-error :index-out-of-range).
go-eval-slice → (:slice OBJ LOW HIGH MAX). Two-index slice
with omitted low → 0, omitted high → len.
Returns a new :go-slice.
go-list-slice → primitive list-slicing helper.
Builtins live in a new starter env go-env-builtins:
len(slice|string) → count
append(slice, ...x) → new slice with x appended
print(...) → no-op in v0
Builtins are bound as (:go-builtin NAME); go-eval-call recognises the
shape and routes to go-eval-builtin instead of go-eval-fn.
**Summing a slice via the canonical Go for-loop works end-to-end:**
a := []int{1, 2, 3, 4, 5}
sum := 0
for i := 0; i < len(a); i++ {
sum = sum + a[i]
}
// sum == 15
eval 50/50, total 427/427.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
141
lib/go/eval.sx
141
lib/go/eval.sx
@@ -13,6 +13,16 @@
|
||||
|
||||
(define go-env-empty (list))
|
||||
|
||||
(define
|
||||
go-env-builtins
|
||||
;; A starter env containing the Go builtins eval understands.
|
||||
;; Tests can call (go-env-builtins) instead of go-env-empty when they
|
||||
;; need len/append/print.
|
||||
(list
|
||||
(list "len" (list :go-builtin "len"))
|
||||
(list "append" (list :go-builtin "append"))
|
||||
(list "print" (list :go-builtin "print"))))
|
||||
|
||||
(define
|
||||
go-env-lookup
|
||||
(fn
|
||||
@@ -229,10 +239,129 @@
|
||||
(go-env-extend env (first names) (first vals))
|
||||
(rest names) (rest vals)))))
|
||||
|
||||
(define
|
||||
go-eval-builtin
|
||||
;; Run Go's predeclared builtins (len, append, print). args are
|
||||
;; expressions; we eval them in the caller env then dispatch on NAME.
|
||||
(fn (caller-env name args)
|
||||
(let ((vals (go-eval-args caller-env args)))
|
||||
(cond
|
||||
(go-eval-error? vals) vals
|
||||
(= name "len")
|
||||
(cond
|
||||
(not (= (len vals) 1))
|
||||
(list :eval-error :builtin-arity name 1 (len vals))
|
||||
:else
|
||||
(let ((arg (first vals)))
|
||||
(cond
|
||||
(and (list? arg) (= (first arg) :go-slice)) (len (nth arg 1))
|
||||
(string? arg) (len arg)
|
||||
:else (list :eval-error :len-not-applicable arg))))
|
||||
(= name "append")
|
||||
(cond
|
||||
(< (len vals) 1)
|
||||
(list :eval-error :builtin-arity name 1 (len vals))
|
||||
:else
|
||||
(let ((slc (first vals)) (extra (rest vals)))
|
||||
(cond
|
||||
(and (list? slc) (= (first slc) :go-slice))
|
||||
(list :go-slice (go-name-concat (nth slc 1) extra))
|
||||
:else (list :eval-error :append-not-slice slc))))
|
||||
(= name "print")
|
||||
nil ;; v0: silent. Real impl would write to stdout.
|
||||
:else (list :eval-error :unknown-builtin name)))))
|
||||
|
||||
(define
|
||||
go-extract-composite-vals
|
||||
;; For slice/array composite literals: read each element's value
|
||||
;; (skipping :kv keys, only using values for Go's index-keyed shorthand).
|
||||
(fn (env elems)
|
||||
(cond
|
||||
(or (= elems nil) (= (len elems) 0)) (list)
|
||||
:else
|
||||
(let ((e (first elems)))
|
||||
(let ((v
|
||||
(cond
|
||||
(and (list? e) (= (first e) :kv))
|
||||
(go-eval env (nth e 2))
|
||||
:else (go-eval env e))))
|
||||
(cond
|
||||
(go-eval-error? v) v
|
||||
:else
|
||||
(let ((rest-vs (go-extract-composite-vals env (rest elems))))
|
||||
(cond
|
||||
(go-eval-error? rest-vs) rest-vs
|
||||
:else (cons v rest-vs)))))))))
|
||||
|
||||
(define
|
||||
go-eval-composite
|
||||
;; (:composite TYPE-OR-EXPR ELEMS). v0 supports slice/array; map/struct
|
||||
;; later.
|
||||
(fn (env expr)
|
||||
(let ((ty (nth expr 1)) (elems (nth expr 2)))
|
||||
(cond
|
||||
(and (list? ty)
|
||||
(or (= (first ty) :ty-slice) (= (first ty) :ty-array)))
|
||||
(let ((vals (go-extract-composite-vals env elems)))
|
||||
(cond
|
||||
(go-eval-error? vals) vals
|
||||
:else (list :go-slice vals)))
|
||||
:else (list :eval-error :unsupported-composite ty)))))
|
||||
|
||||
(define
|
||||
go-eval-index
|
||||
;; (:index OBJ IDX-EXPR). v0: slice indexing only.
|
||||
(fn (env expr)
|
||||
(let ((obj (go-eval env (nth expr 1)))
|
||||
(idx (go-eval env (nth expr 2))))
|
||||
(cond
|
||||
(go-eval-error? obj) obj
|
||||
(go-eval-error? idx) idx
|
||||
(and (list? obj) (= (first obj) :go-slice))
|
||||
(let ((elems (nth obj 1)))
|
||||
(cond
|
||||
(or (< idx 0) (>= idx (len elems)))
|
||||
(list :eval-error :index-out-of-range idx (len elems))
|
||||
:else (nth elems idx)))
|
||||
:else (list :eval-error :not-indexable obj)))))
|
||||
|
||||
(define
|
||||
go-eval-slice
|
||||
;; (:slice OBJ LOW HIGH MAX). v0: two-index slice on go-slice values.
|
||||
(fn (env expr)
|
||||
(let ((obj (go-eval env (nth expr 1)))
|
||||
(low (cond
|
||||
(= (nth expr 2) nil) 0
|
||||
:else (go-eval env (nth expr 2))))
|
||||
(high-expr (nth expr 3)))
|
||||
(cond
|
||||
(go-eval-error? obj) obj
|
||||
(go-eval-error? low) low
|
||||
(not (and (list? obj) (= (first obj) :go-slice)))
|
||||
(list :eval-error :not-sliceable obj)
|
||||
:else
|
||||
(let ((elems (nth obj 1)))
|
||||
(let ((high
|
||||
(cond
|
||||
(= high-expr nil) (len elems)
|
||||
:else (go-eval env high-expr))))
|
||||
(cond
|
||||
(go-eval-error? high) high
|
||||
:else
|
||||
(list :go-slice (go-list-slice elems low high)))))))))
|
||||
|
||||
(define
|
||||
go-list-slice
|
||||
(fn (lst low high)
|
||||
(cond
|
||||
(>= low high) (list)
|
||||
(>= low (len lst)) (list)
|
||||
:else
|
||||
(cons (nth lst low)
|
||||
(go-list-slice lst (+ low 1) high)))))
|
||||
|
||||
(define
|
||||
go-eval-call
|
||||
;; Apply a callable VAL to ARG-EXPRS in CALLER-ENV. Result is the
|
||||
;; function's return value or a (:eval-error ...).
|
||||
;;
|
||||
;; Closure semantics: the function value carries no captured env in v0
|
||||
;; (dynamic scope wrt outer bindings). Recursion at top level works
|
||||
@@ -240,6 +369,8 @@
|
||||
;; lexical closures arrive in a later slice.
|
||||
(fn (caller-env callee-val args)
|
||||
(cond
|
||||
(and (list? callee-val) (= (first callee-val) :go-builtin))
|
||||
(go-eval-builtin caller-env (nth callee-val 1) args)
|
||||
(not (and (list? callee-val) (= (first callee-val) :go-fn)))
|
||||
(list :eval-error :not-callable callee-val)
|
||||
:else
|
||||
@@ -503,6 +634,12 @@
|
||||
:else (let
|
||||
((v (go-env-lookup env name)))
|
||||
(cond (= v nil) (list :eval-error :unbound name) :else v))))
|
||||
(and (list? expr) (= (first expr) :composite))
|
||||
(go-eval-composite env expr)
|
||||
(and (list? expr) (= (first expr) :index))
|
||||
(go-eval-index env expr)
|
||||
(and (list? expr) (= (first expr) :slice))
|
||||
(go-eval-slice env expr)
|
||||
(and (list? expr) (= (first expr) :app))
|
||||
(let ((head (nth expr 1)) (args (nth expr 2)))
|
||||
(cond
|
||||
|
||||
Reference in New Issue
Block a user