diff options
| author | Marc Vertes <mvertes@free.fr> | 2025-12-02 15:45:14 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2025-12-02 15:45:14 +0100 |
| commit | 3d64f909cfb55d8886ac4b4839a98f8f6cdc98e7 (patch) | |
| tree | c0e4149ef652f2d422d2f598da1651fde4009496 | |
| parent | f40a1c23467eef36f53635e525f8b25f591e8a45 (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-- | Makefile | 3 | ||||
| -rw-r--r-- | comp/compiler.go | 56 | ||||
| -rw-r--r-- | interp/interpreter_test.go | 3 | ||||
| -rw-r--r-- | lang/token_string.go | 5 | ||||
| -rw-r--r-- | vm/op_string.go | 52 | ||||
| -rw-r--r-- | vm/vm.go | 18 |
6 files changed, 76 insertions, 61 deletions
@@ -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 @@ -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] |
