go: eval.sx — structs + selector + selector-assign + 8 tests [nothing]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Phase 4 cont. Adds runtime support for Go's struct type.
Struct representation: (list :go-struct TYPE-NAME FIELDS) where
FIELDS is an association list of (field-name value) pairs.
`type T struct { ... }` is now significant at eval-time. The new
go-eval-type-decl registers field-name lists in env under
(:go-struct-type FIELD-NAMES) so positional composite literals can
map argument positions to field names. Non-struct type aliases are
silent no-ops in v0.
go-eval-composite extended:
* If type is (:var TYPE-NAME), look up in env. Must be a
:go-struct-type entry — error otherwise.
* go-eval-struct-lit branches on whether the first elem is :kv
(keyed) or not (positional). Keyed mode reads key-name from each
:kv's key (which is a :var node). Positional mode arity-checks
against the field-names list and zips positionally.
go-eval-select handles (:select OBJ FIELD-NAME) — field lookup with
go-map-get on the FIELDS assoc list.
go-eval-assign-pairs gets a new (:select OBJ FIELD) LHS branch:
- var-rooted only for v0
- rebuilds the struct via go-map-set, rebinds the var
**Functions taking and returning structs round-trip end-to-end:**
type Point struct { x, y int }
func add(a, b Point) Point { return Point{a.x + b.x, a.y + b.y} }
add(Point{1, 2}, Point{3, 4}) // Point{4, 6}
Method-dispatch (calling p.M() where M is a method on Point's type)
is the next step; needs threading the type checker's #method/T/N
scheme into eval-time so functions can be looked up by receiver type.
eval 66/66, total 443/443.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
129
lib/go/eval.sx
129
lib/go/eval.sx
@@ -267,6 +267,89 @@
|
||||
(= idx 0) (cons value (rest elems))
|
||||
:else (cons (first elems) (go-slice-set (rest elems) (- idx 1) value)))))
|
||||
|
||||
(define
|
||||
go-struct-field-names
|
||||
;; FIELDS is a list of (:field NAMES TYPE) groups; flatten to names.
|
||||
(fn (fields)
|
||||
(cond
|
||||
(or (= fields nil) (= (len fields) 0)) (list)
|
||||
:else
|
||||
(let ((f (first fields)))
|
||||
(let ((names (nth f 1)))
|
||||
(go-name-concat names (go-struct-field-names (rest fields))))))))
|
||||
|
||||
(define
|
||||
go-zip-fields
|
||||
(fn (names vals)
|
||||
(cond
|
||||
(= (len names) 0) (list)
|
||||
:else
|
||||
(cons (list (first names) (first vals))
|
||||
(go-zip-fields (rest names) (rest vals))))))
|
||||
|
||||
(define
|
||||
go-eval-keyed-fields
|
||||
;; Each elem is (:kv (:var FIELD-NAME) VALUE-EXPR).
|
||||
(fn (env elems)
|
||||
(cond
|
||||
(or (= elems nil) (= (len elems) 0)) (list)
|
||||
:else
|
||||
(let ((e (first elems)))
|
||||
(cond
|
||||
(not (and (list? e) (= (first e) :kv)))
|
||||
(list :eval-error :struct-elem-missing-key e)
|
||||
:else
|
||||
(let ((k (nth e 1)) (v (go-eval env (nth e 2))))
|
||||
(cond
|
||||
(go-eval-error? v) v
|
||||
(not (and (list? k) (= (first k) :var)))
|
||||
(list :eval-error :struct-key-not-ident k)
|
||||
:else
|
||||
(let ((rest-fields
|
||||
(go-eval-keyed-fields env (rest elems))))
|
||||
(cond
|
||||
(go-eval-error? rest-fields) rest-fields
|
||||
:else
|
||||
(cons (list (nth k 1) v) rest-fields))))))))))
|
||||
|
||||
(define
|
||||
go-eval-struct-lit
|
||||
(fn (env type-name field-names elems)
|
||||
(cond
|
||||
(or (= elems nil) (= (len elems) 0))
|
||||
(list :go-struct type-name (list))
|
||||
(and (list? (first elems)) (= (first (first elems)) :kv))
|
||||
(let ((fields (go-eval-keyed-fields env elems)))
|
||||
(cond
|
||||
(go-eval-error? fields) fields
|
||||
:else (list :go-struct type-name fields)))
|
||||
:else
|
||||
(cond
|
||||
(not (= (len elems) (len field-names)))
|
||||
(list :eval-error :struct-arity-mismatch type-name
|
||||
(len field-names) (len elems))
|
||||
:else
|
||||
(let ((vals (go-eval-args env elems)))
|
||||
(cond
|
||||
(go-eval-error? vals) vals
|
||||
:else
|
||||
(list :go-struct type-name
|
||||
(go-zip-fields field-names vals))))))))
|
||||
|
||||
(define
|
||||
go-eval-select
|
||||
;; (:select OBJ FIELD-NAME) — struct field access.
|
||||
(fn (env expr)
|
||||
(let ((obj (go-eval env (nth expr 1))) (field-name (nth expr 2)))
|
||||
(cond
|
||||
(go-eval-error? obj) obj
|
||||
(and (list? obj) (= (first obj) :go-struct))
|
||||
(let ((v (go-map-get (nth obj 2) field-name)))
|
||||
(cond
|
||||
(= v nil) (list :eval-error :unknown-field field-name)
|
||||
:else v))
|
||||
:else (list :eval-error :not-selectable obj)))))
|
||||
|
||||
(define
|
||||
go-eval-builtin
|
||||
;; Run Go's predeclared builtins (len, append, print). args are
|
||||
@@ -361,6 +444,18 @@
|
||||
(cond
|
||||
(go-eval-error? entries) entries
|
||||
:else (list :go-map entries)))
|
||||
;; Named struct type (Point{1, 2}). Lookup the type info.
|
||||
(and (list? ty) (= (first ty) :var))
|
||||
(let ((type-info (go-env-lookup env (nth ty 1))))
|
||||
(cond
|
||||
(= type-info nil)
|
||||
(list :eval-error :unknown-struct-type (nth ty 1))
|
||||
(not (and (list? type-info)
|
||||
(= (first type-info) :go-struct-type)))
|
||||
(list :eval-error :not-struct-type (nth ty 1) type-info)
|
||||
:else
|
||||
(go-eval-struct-lit env (nth ty 1)
|
||||
(nth type-info 1) elems)))
|
||||
:else (list :eval-error :unsupported-composite ty)))))
|
||||
|
||||
(define
|
||||
@@ -543,6 +638,23 @@
|
||||
(go-map-set (nth obj 1) idx rhs-val)))
|
||||
(rest lhs-list) (rest vals))
|
||||
:else (list :eval-error :unsupported-lhs lhs)))))
|
||||
;; (:select OBJ FIELD) — struct field assignment
|
||||
(and (list? lhs) (= (first lhs) :select))
|
||||
(let ((obj-expr (nth lhs 1)) (field-name (nth lhs 2)))
|
||||
(cond
|
||||
(not (and (list? obj-expr) (= (first obj-expr) :var)))
|
||||
(list :eval-error :unsupported-lhs lhs)
|
||||
:else
|
||||
(let ((obj (go-eval env obj-expr)))
|
||||
(cond
|
||||
(go-eval-error? obj) obj
|
||||
(and (list? obj) (= (first obj) :go-struct))
|
||||
(go-eval-assign-pairs
|
||||
(go-env-extend env (nth obj-expr 1)
|
||||
(list :go-struct (nth obj 1)
|
||||
(go-map-set (nth obj 2) field-name rhs-val)))
|
||||
(rest lhs-list) (rest vals))
|
||||
:else (list :eval-error :unsupported-lhs lhs)))))
|
||||
:else (list :eval-error :unsupported-lhs lhs))))))
|
||||
|
||||
(define
|
||||
@@ -667,12 +779,27 @@
|
||||
(go-eval-inc-dec env stmt)
|
||||
(and (list? stmt) (= (first stmt) :func-decl))
|
||||
(go-eval-func-decl env stmt)
|
||||
(and (list? stmt) (= (first stmt) :type-decl))
|
||||
(go-eval-type-decl env stmt)
|
||||
:else
|
||||
(let ((v (go-eval env stmt)))
|
||||
(cond
|
||||
(go-eval-error? v) v
|
||||
:else env)))))
|
||||
|
||||
(define
|
||||
go-eval-type-decl
|
||||
;; (:type-decl NAME TYPE). For struct types we register the field-name
|
||||
;; list so positional composite literals like Point{1, 2} can map
|
||||
;; positions to field names. Other type aliases are silent no-ops in v0.
|
||||
(fn (env stmt)
|
||||
(let ((name (nth stmt 1)) (ty (nth stmt 2)))
|
||||
(cond
|
||||
(and (list? ty) (= (first ty) :ty-struct))
|
||||
(go-env-extend env name
|
||||
(list :go-struct-type (go-struct-field-names (nth ty 1))))
|
||||
:else env))))
|
||||
|
||||
(define
|
||||
go-eval-block
|
||||
(fn (env stmts)
|
||||
@@ -724,6 +851,8 @@
|
||||
(go-eval-index env expr)
|
||||
(and (list? expr) (= (first expr) :slice))
|
||||
(go-eval-slice env expr)
|
||||
(and (list? expr) (= (first expr) :select))
|
||||
(go-eval-select 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