summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2025-12-02 15:45:14 +0100
committerMarc Vertes <mvertes@free.fr>2025-12-02 15:45:14 +0100
commit3d64f909cfb55d8886ac4b4839a98f8f6cdc98e7 (patch)
treec0e4149ef652f2d422d2f598da1651fde4009496
parentf40a1c23467eef36f53635e525f8b25f591e8a45 (diff)
feat: support of struct literal composite
Added missing vm instructions to allocate a typed value on the stack and to set a structure field. It's possible now to generate struct literal composites for non keyed struct fields.
-rw-r--r--Makefile3
-rw-r--r--comp/compiler.go56
-rw-r--r--interp/interpreter_test.go3
-rw-r--r--lang/token_string.go5
-rw-r--r--vm/op_string.go52
-rw-r--r--vm/vm.go18
6 files changed, 76 insertions, 61 deletions
diff --git a/Makefile b/Makefile
index 1d4e5c8..187a67a 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,9 @@
lint:
golangci-lint run
+generate:
+ go generate ./...
+
# Run tests with race detector, measure coverage.
test:
go test -race -covermode=atomic -coverpkg=./... -coverprofile=cover.out ./interp
diff --git a/comp/compiler.go b/comp/compiler.go
index 610d4a1..7b12086 100644
--- a/comp/compiler.go
+++ b/comp/compiler.go
@@ -56,6 +56,15 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) {
}
push := func(s *parser.Symbol) { stack = append(stack, s) }
pop := func() *parser.Symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s }
+ top := func() *parser.Symbol { return stack[len(stack)-1] }
+
+ showStack := func() {
+ _, file, line, _ := runtime.Caller(1)
+ fmt.Fprintf(os.Stderr, "%s%d: showstack: %d\n", path.Base(file), line, len(stack))
+ for i, s := range stack {
+ fmt.Fprintf(os.Stderr, "%s:%d: stack[%d]: %v\n", path.Base(file), line, i, s)
+ }
+ }
for i, t := range tokens {
switch t.Tok {
@@ -144,23 +153,17 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) {
emit(t, vm.CallX, t.Beg)
case lang.Composite:
- log.Println("COMPOSITE")
- /*
- d := pop()
- switch d.Type.Rtype.Kind() {
- case reflect.Struct:
- // nf := d.typ.Rtype.NumField()
- // emit(t.Pos, vm.New, d.index, c.typeSym(d.typ).index)
- emit(t, vm.Field, 0)
- emit(t, vm.Vassign)
- emit(t, vm.Fdup, 2)
- emit(t, vm.Field, 1)
- emit(t, vm.Vassign)
- emit(t, vm.Pop, 1)
- // emit(t, vm.Fdup, 2)
- // Assume an element list with no keys, one per struct field in order
+ d := top() // let the type symbol on the stack, may be required for assignment
+ switch d.Type.Rtype.Kind() {
+ case reflect.Struct:
+ emit(t, vm.Fnew, d.Index)
+ nf := d.Type.Rtype.NumField()
+ for i := 0; i < nf; i++ {
+ emit(t, vm.FieldSet, i)
}
- */
+ default:
+ return fmt.Errorf("composite kind not supported yet: %v", d.Type.Rtype.Kind())
+ }
case lang.Grow:
emit(t, vm.Grow, t.Beg)
@@ -169,6 +172,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) {
// TODO: support assignment to local, composite objects.
st := tokens[i-1]
l := len(c.Data)
+ showStack()
d := pop()
typ := d.Type
if typ == nil {
@@ -242,7 +246,9 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) {
s.Index = len(c.Data)
c.Data = append(c.Data, s.Value)
}
- emit(t, vm.Dup, s.Index)
+ if s.Kind != parser.SymType {
+ emit(t, vm.Dup, s.Index)
+ }
}
case lang.Label:
@@ -389,7 +395,7 @@ func (c *Compiler) PrintCode() {
extra = "// " + d
}
}
- fmt.Fprintf(os.Stderr, "%4d %-14v %v\n", i, l, extra)
+ fmt.Fprintf(os.Stderr, "%4d %-18v %v\n", i, l, extra)
}
for _, label := range labels[len(c.Code)] {
@@ -403,19 +409,7 @@ type entry struct {
*parser.Symbol
}
-func (e entry) String() string {
- if e.Symbol != nil {
- return fmt.Sprintf("name: %s,local: %t, i: %d, k: %d, t: %s, v: %v",
- e.name,
- e.Local,
- e.Index,
- e.Kind,
- e.Type,
- e.Value,
- )
- }
- return e.name
-}
+func (e entry) String() string { return fmt.Sprintf("name: %s, sym: %v", e.name, e.Symbol) }
// PrintData pretty prints the generated global data symbols in compiler.
func (c *Compiler) PrintData() {
diff --git a/interp/interpreter_test.go b/interp/interpreter_test.go
index 21e6274..42cb7e2 100644
--- a/interp/interpreter_test.go
+++ b/interp/interpreter_test.go
@@ -258,6 +258,7 @@ func TestComposite(t *testing.T) {
run(t, []etest{
{src: "type T struct{}; t := T{}; t", res: "{}"},
{src: "t := struct{}{}; t", res: "{}"},
- // {src: `type T struct{N int; S string}; t := T{2, "foo"}`, res: `{2 foo}`},
+ {src: `type T struct {}; var t T; t = T{}; t`, res: "{}"},
+ {src: `type T struct{N int; S string}; var t T; t = T{2, "foo"}; t`, res: `{2 foo}`},
})
}
diff --git a/lang/token_string.go b/lang/token_string.go
index 97961e3..fcceaeb 100644
--- a/lang/token_string.go
+++ b/lang/token_string.go
@@ -109,8 +109,9 @@ const _Token_name = "IllegalCommentIdentCharFloatImagIntStringAddSubMulQuoRemAnd
var _Token_index = [...]uint16{0, 7, 14, 19, 23, 28, 32, 35, 41, 44, 47, 50, 53, 56, 59, 61, 64, 67, 70, 76, 82, 87, 94, 106, 110, 114, 123, 126, 134, 140, 146, 155, 164, 173, 182, 191, 200, 208, 217, 226, 235, 247, 250, 253, 257, 262, 266, 271, 278, 283, 291, 294, 299, 304, 313, 318, 328, 340, 350, 355, 359, 363, 368, 376, 383, 388, 392, 403, 406, 410, 412, 416, 418, 424, 433, 436, 443, 448, 454, 460, 466, 472, 476, 479, 483, 488, 497, 505, 509, 514, 523, 535, 546, 551, 554}
func (i Token) String() string {
- if i < 0 || i >= Token(len(_Token_index)-1) {
+ idx := int(i) - 0
+ if i < 0 || idx >= len(_Token_index)-1 {
return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
}
- return _Token_name[_Token_index[i]:_Token_index[i+1]]
+ return _Token_name[_Token_index[idx]:_Token_index[idx+1]]
}
diff --git a/vm/op_string.go b/vm/op_string.go
index ac5b557..63bf738 100644
--- a/vm/op_string.go
+++ b/vm/op_string.go
@@ -20,34 +20,36 @@ func _() {
_ = x[Deref-9]
_ = x[Dup-10]
_ = x[Fdup-11]
- _ = x[Equal-12]
- _ = x[EqualSet-13]
- _ = x[Exit-14]
- _ = x[Field-15]
- _ = x[Greater-16]
- _ = x[Grow-17]
- _ = x[Index-18]
- _ = x[Jump-19]
- _ = x[JumpTrue-20]
- _ = x[JumpFalse-21]
- _ = x[JumpSetTrue-22]
- _ = x[JumpSetFalse-23]
- _ = x[Lower-24]
- _ = x[Loweri-25]
- _ = x[Mul-26]
- _ = x[New-27]
- _ = x[Not-28]
- _ = x[Pop-29]
- _ = x[Push-30]
- _ = x[Return-31]
- _ = x[Sub-32]
- _ = x[Subi-33]
- _ = x[Swap-34]
+ _ = x[Fnew-12]
+ _ = x[Equal-13]
+ _ = x[EqualSet-14]
+ _ = x[Exit-15]
+ _ = x[Field-16]
+ _ = x[FieldSet-17]
+ _ = x[Greater-18]
+ _ = x[Grow-19]
+ _ = x[Index-20]
+ _ = x[Jump-21]
+ _ = x[JumpTrue-22]
+ _ = x[JumpFalse-23]
+ _ = x[JumpSetTrue-24]
+ _ = x[JumpSetFalse-25]
+ _ = x[Lower-26]
+ _ = x[Loweri-27]
+ _ = x[Mul-28]
+ _ = x[New-29]
+ _ = x[Not-30]
+ _ = x[Pop-31]
+ _ = x[Push-32]
+ _ = x[Return-33]
+ _ = x[Sub-34]
+ _ = x[Subi-35]
+ _ = x[Swap-36]
}
-const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupEqualEqualSetExitFieldGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubiSwap"
+const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubiSwap"
-var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 61, 69, 73, 78, 85, 89, 94, 98, 106, 115, 126, 138, 143, 149, 152, 155, 158, 161, 165, 171, 174, 178, 182}
+var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 97, 101, 106, 110, 118, 127, 138, 150, 155, 161, 164, 167, 170, 173, 177, 183, 186, 190, 194}
func (i Op) String() string {
idx := int(i) - 0
diff --git a/vm/vm.go b/vm/vm.go
index 7afe374..d035226 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -31,10 +31,12 @@ const (
Deref // x -- *x ;
Dup // addr -- value ; value = mem[addr]
Fdup // addr -- value ; value = mem[addr]
+ Fnew // -- x; x = new mem[$1]
Equal // n1 n2 -- cond ; cond = n1 == n2
EqualSet // n1 n2 -- n1 cond ; cond = n1 == n2
Exit // -- ;
Field // s -- f ; f = s.FieldIndex($1, ...)
+ FieldSet // s d -- s ; s.FieldIndex($1, ...) = d
Greater // n1 n2 -- cond; cond = n1 > n2
Grow // -- ; sp += $1
Index // a i -- a[i] ;
@@ -98,7 +100,7 @@ func (m *Machine) Run() (err error) {
sp = len(mem) // stack pointer
c := m.code[ip]
if debug {
- log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-14v] mem:%v\n", ip, sp, fp, c, Vstring(mem))
+ log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-18v] mem:%v\n", ip, sp, fp, c, Vstring(mem))
}
ic++
switch c.Op {
@@ -160,6 +162,8 @@ func (m *Machine) Run() (err error) {
return err
case Fdup:
mem = append(mem, mem[c.Arg[0]+fp-1])
+ case Fnew:
+ mem = append(mem, NewValue(mem[c.Arg[0]].Type))
case Field:
fv := mem[sp-1].FieldByIndex(c.Arg)
if !fv.CanSet() {
@@ -167,6 +171,15 @@ func (m *Machine) Run() (err error) {
fv = reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem()
}
mem[sp-1].Value = fv
+ case FieldSet:
+ fv := mem[sp-1].FieldByIndex(c.Arg)
+ if !fv.CanSet() {
+ // Normally private fields can not bet set via reflect. Override this limitation.
+ fv = reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem()
+ }
+ fv.Set(mem[sp-2].Value)
+ mem[sp-2] = mem[sp-1]
+ mem = mem[:sp-1]
case Jump:
ip += c.Arg[0]
continue
@@ -229,7 +242,8 @@ func (m *Machine) Run() (err error) {
case Subi:
mem[sp-1] = ValueOf(int(mem[sp-1].Int()) - c.Arg[0])
case Swap:
- mem[sp-2], mem[sp-1] = mem[sp-1], mem[sp-2]
+ a, b := sp-c.Arg[0]-1, sp-c.Arg[1]-1
+ mem[a], mem[b] = mem[b], mem[a]
case Index:
mem[sp-2].Value = mem[sp-1].Index(int(mem[sp-2].Int()))
mem = mem[:sp-1]