diff options
| author | Marc Vertes <mvertes@free.fr> | 2026-01-14 18:44:17 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2026-01-14 18:44:17 +0100 |
| commit | 31e3793202402fda21905027c18ebfa5c8d8c832 (patch) | |
| tree | dbe52ae31fada099bc65fcd9c7d61f6266c6ba78 | |
| parent | de3baf0e06862f0420950f025b3328068f3b6df2 (diff) | |
fix: improve handling of indirections
Added 2 VM instructions, FnewE variant of Fnew with a dereference,
and FieldE, variant of Field with dereference.
It's now possible to pointers in structs and literal composites.
| -rw-r--r-- | comp/compiler.go | 55 | ||||
| -rw-r--r-- | interp/interpreter_test.go | 4 | ||||
| -rw-r--r-- | lang/golang/go.go | 2 | ||||
| -rw-r--r-- | symbol/symbol.go | 10 | ||||
| -rw-r--r-- | vm/op_string.go | 64 | ||||
| -rw-r--r-- | vm/type.go | 3 | ||||
| -rw-r--r-- | vm/vm.go | 11 |
7 files changed, 97 insertions, 52 deletions
diff --git a/comp/compiler.go b/comp/compiler.go index 8a136e1..8a7db22 100644 --- a/comp/compiler.go +++ b/comp/compiler.go @@ -99,15 +99,15 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.Dup, i) case lang.Add: - push(&symbol.Symbol{Type: arithmeticOpType(pop(), pop())}) + push(&symbol.Symbol{Kind: symbol.Value, Type: arithmeticOpType(pop(), pop())}) emit(t, vm.Add) case lang.Mul: - push(&symbol.Symbol{Type: arithmeticOpType(pop(), pop())}) + push(&symbol.Symbol{Kind: symbol.Value, Type: arithmeticOpType(pop(), pop())}) emit(t, vm.Mul) case lang.Sub: - push(&symbol.Symbol{Type: arithmeticOpType(pop(), pop())}) + push(&symbol.Symbol{Kind: symbol.Value, Type: arithmeticOpType(pop(), pop())}) emit(t, vm.Sub) case lang.Minus: @@ -120,11 +120,11 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { // Unary '+' is idempotent. Nothing to do. case lang.Addr: - push(&symbol.Symbol{Type: vm.PointerTo(pop().Type)}) + push(&symbol.Symbol{Kind: symbol.Value, Type: vm.PointerTo(pop().Type)}) emit(t, vm.Addr) case lang.Deref: - push(&symbol.Symbol{Type: pop().Type.Elem()}) + push(&symbol.Symbol{Kind: symbol.Value, Type: pop().Type.Elem()}) emit(t, vm.Deref) case lang.Index: @@ -136,14 +136,14 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { } else { emit(t, vm.Index) } - push(&symbol.Symbol{Type: s.Type.Elem()}) + push(&symbol.Symbol{Kind: symbol.Value, Type: s.Type.Elem()}) case lang.Greater: - push(&symbol.Symbol{Type: booleanOpType(pop(), pop())}) + push(&symbol.Symbol{Kind: symbol.Value, Type: booleanOpType(pop(), pop())}) emit(t, vm.Greater) case lang.Less: - push(&symbol.Symbol{Type: booleanOpType(pop(), pop())}) + push(&symbol.Symbol{Kind: symbol.Value, Type: booleanOpType(pop(), pop())}) emit(t, vm.Lower) case lang.Call: @@ -159,7 +159,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { pop() } for i := 0; i < typ.Rtype.NumOut(); i++ { - push(&symbol.Symbol{Type: typ.Out(i)}) + push(&symbol.Symbol{Kind: symbol.Value, Type: typ.Out(i)}) } emit(t, vm.Call, narg) @@ -173,7 +173,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { rtyp := s.Value.Value.Type() // TODO: pop input types (careful with variadic function). for i := 0; i < rtyp.NumOut(); i++ { - push(&symbol.Symbol{Type: &vm.Type{Rtype: rtyp.Out(i)}}) + push(&symbol.Symbol{Kind: symbol.Value, Type: &vm.Type{Rtype: rtyp.Out(i)}}) } emit(t, vm.CallX, t.Beg) @@ -182,6 +182,10 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { pop() ks := pop() ts := top() + if ts.IsPtr() { + // Resolve index on the element type + ts = &symbol.Symbol{Kind: symbol.Value, Type: &vm.Type{Rtype: ts.Type.Rtype.Elem()}} + } switch ks.Kind { case symbol.Const: switch ts.Type.Rtype.Kind() { @@ -190,9 +194,10 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.FieldFset) } case reflect.Slice: - if v := ks.Value.Value; v.CanInt() { - emit(t, vm.IndexSet) + if ts.Type.Elem().IsPtr() { + emit(t, vm.Addr) } + emit(t, vm.IndexSet) case reflect.Map: emit(t, vm.MapSet) } @@ -276,9 +281,12 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { c.Data = append(c.Data, s.Value) } if s.Kind == symbol.Type { - if s.Type.Rtype.Kind() == reflect.Slice { + switch s.Type.Rtype.Kind() { + case reflect.Slice: emit(t, vm.Fnew, s.Index, s.SliceLen) - } else { + case reflect.Pointer: + emit(t, vm.FnewE, s.Index, 1) + default: emit(t, vm.Fnew, s.Index, 1) } } else { @@ -357,6 +365,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { if len(stack) < 1 { return errorf("missing symbol") } + showStack() s := pop() switch s.Kind { case symbol.Pkg: @@ -384,9 +393,21 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { case symbol.Unset: return errorf("invalid symbol: %s", s.Name) default: - if f, ok := s.Type.Rtype.FieldByName(t.Str[1:]); ok { - emit(t, vm.Field, f.Index...) - push(&symbol.Symbol{Type: s.Type.FieldType(t.Str[1:])}) + // FIXME: handle pointer indirection here + log.Println("## XXX", s.Type, s.Type.IsPtr()) + typ := s.Type.Rtype + isPtr := typ.Kind() == reflect.Pointer + if isPtr { + typ = typ.Elem() + } + if f, ok := typ.FieldByName(t.Str[1:]); ok { + if isPtr { + emit(t, vm.FieldE, f.Index...) + push(&symbol.Symbol{Type: s.Type.Elem().FieldType(t.Str[1:])}) + } else { + emit(t, vm.Field, f.Index...) + push(&symbol.Symbol{Type: s.Type.FieldType(t.Str[1:])}) + } break } return fmt.Errorf("field or method not found: %s", t.Str[1:]) diff --git a/interp/interpreter_test.go b/interp/interpreter_test.go index 0cb4160..bf90357 100644 --- a/interp/interpreter_test.go +++ b/interp/interpreter_test.go @@ -214,6 +214,8 @@ func TestStruct(t *testing.T) { {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"}, // #00 {src: "type T struct {a int}; var t T; t.a", res: "0"}, // #01 {src: "type T struct {a int}; var t T; t.a = 1; t.a", res: "1"}, // #02 + {src: "type T struct {a int}; var t T = T{1}; t.a", res: "1"}, // #03 + {src: "type T struct {a int}; var t *T = &T{1}; t.a", res: "1"}, // #04 }) } @@ -289,5 +291,7 @@ func TestComposite(t *testing.T) { {src: `type T struct {b bool}; m := []T{{true}}; m`, res: `[{true}]`}, // #12 {src: `m := []struct{b bool}{{true}}; m`, res: `[{true}]`}, // #13 {src: `m := map[int]struct{b bool}{1:{true}}; m`, res: `map[1:{true}]`}, // #14 + {src: `type T *struct {b bool}; m := []T{{true}}; m[0]`, res: `&{true}`}, // #15 + {src: `type T *struct {b bool}; m := []T{{true}}; m[0].b`, res: `true`}, // #16 }) } diff --git a/lang/golang/go.go b/lang/golang/go.go index 2085c3b..0d60e8b 100644 --- a/lang/golang/go.go +++ b/lang/golang/go.go @@ -136,7 +136,7 @@ var GoSpec = &lang.Spec{ lang.Minus: {Precedence: 6}, lang.Not: {Precedence: 6}, lang.Call: {Precedence: 6}, - lang.Index: {Precedence: 6}, + lang.Index: {Precedence: 8}, lang.Period: {Precedence: 7}, lang.Colon: {Precedence: 7}, lang.Inc: {SkipSemi: true}, diff --git a/symbol/symbol.go b/symbol/symbol.go index 2ed8907..5e8610a 100644 --- a/symbol/symbol.go +++ b/symbol/symbol.go @@ -4,6 +4,7 @@ package symbol import ( "fmt" "go/constant" + "reflect" "strings" "github.com/mvertes/parscan/vm" @@ -47,15 +48,18 @@ type Symbol struct { // return fmt.Sprintf("{Kind: %v, Name: %v, Index: %v, Type: %v}\n", s.Kind, s.Name, s.Index, s.Type) //} -// IsConst return true if symbol is a constant. +// IsConst returns true if symbol is a constant. func (s *Symbol) IsConst() bool { return s.Kind == Const } -// IsType return true if symbol is a type. +// IsType returns true if symbol is a type. func (s *Symbol) IsType() bool { return s.Kind == Type } -// IsFunc return true if symbol is a function. +// IsFunc returns true if symbol is a function. func (s *Symbol) IsFunc() bool { return s.Kind == Func } +// IsPtr returns true if symbol is a pointer. +func (s *Symbol) IsPtr() bool { return s.Type.Rtype.Kind() == reflect.Pointer } + // Vtype returns the VM type of a symbol. func Vtype(s *Symbol) *vm.Type { if s.Type != nil { diff --git a/vm/op_string.go b/vm/op_string.go index e838628..5542c6e 100644 --- a/vm/op_string.go +++ b/vm/op_string.go @@ -21,40 +21,42 @@ func _() { _ = x[Dup-10] _ = x[Fdup-11] _ = x[Fnew-12] - _ = x[Equal-13] - _ = x[EqualSet-14] - _ = x[Exit-15] - _ = x[Field-16] - _ = x[FieldSet-17] - _ = x[FieldFset-18] - _ = x[Greater-19] - _ = x[Grow-20] - _ = x[Index-21] - _ = x[IndexSet-22] - _ = x[MapIndex-23] - _ = x[MapSet-24] - _ = x[Jump-25] - _ = x[JumpTrue-26] - _ = x[JumpFalse-27] - _ = x[JumpSetTrue-28] - _ = x[JumpSetFalse-29] - _ = x[Lower-30] - _ = x[Loweri-31] - _ = x[Mul-32] - _ = x[New-33] - _ = x[Negate-34] - _ = x[Not-35] - _ = x[Pop-36] - _ = x[Push-37] - _ = x[Return-38] - _ = x[Sub-39] - _ = x[Subi-40] - _ = x[Swap-41] + _ = x[FnewE-13] + _ = x[Equal-14] + _ = x[EqualSet-15] + _ = x[Exit-16] + _ = x[Field-17] + _ = x[FieldE-18] + _ = x[FieldSet-19] + _ = x[FieldFset-20] + _ = x[Greater-21] + _ = x[Grow-22] + _ = x[Index-23] + _ = x[IndexSet-24] + _ = x[MapIndex-25] + _ = x[MapSet-26] + _ = x[Jump-27] + _ = x[JumpTrue-28] + _ = x[JumpFalse-29] + _ = x[JumpSetTrue-30] + _ = x[JumpSetFalse-31] + _ = x[Lower-32] + _ = x[Loweri-33] + _ = x[Mul-34] + _ = x[New-35] + _ = x[Negate-36] + _ = x[Not-37] + _ = x[Pop-38] + _ = x[Push-39] + _ = x[Return-40] + _ = x[Sub-41] + _ = x[Subi-42] + _ = x[Swap-43] } -const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetFieldFsetGreaterGrowIndexIndexSetMapIndexMapSetJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNegateNotPopPushReturnSubSubiSwap" +const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewFnewEEqualEqualSetExitFieldFieldEFieldSetFieldFsetGreaterGrowIndexIndexSetMapIndexMapSetJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNegateNotPopPushReturnSubSubiSwap" -var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 99, 106, 110, 115, 123, 131, 137, 141, 149, 158, 169, 181, 186, 192, 195, 198, 204, 207, 210, 214, 220, 223, 227, 231} +var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 70, 78, 82, 87, 93, 101, 110, 117, 121, 126, 134, 142, 148, 152, 160, 169, 180, 192, 197, 203, 206, 209, 215, 218, 221, 225, 231, 234, 238, 242} func (i Op) String() string { idx := int(i) - 0 @@ -132,3 +132,6 @@ func (t *Type) FieldType(name string) *Type { } return nil } + +// IsPtr returns true if type t is of pointer kind. +func (t *Type) IsPtr() bool { return t.Rtype.Kind() == reflect.Pointer } @@ -32,10 +32,12 @@ const ( Dup // addr -- value ; value = mem[addr] Fdup // addr -- value ; value = mem[addr] Fnew // -- x; x = new mem[$1] + FnewE // -- x; x = new mem[$1].Elem() Equal // n1 n2 -- cond ; cond = n1 == n2 EqualSet // n1 n2 -- n1 cond ; cond = n1 == n2 Exit // -- ; Field // s -- f ; f = s.FieldIndex($1, ...) + FieldE // s -- f ; f = s.FieldIndex($1, ...) FieldSet // s d -- s ; s.FieldIndex($1, ...) = d FieldFset // s i v -- s; s.FieldIndex(i) = v Greater // n1 n2 -- cond; cond = n1 > n2 @@ -173,6 +175,8 @@ func (m *Machine) Run() (err error) { mem = append(mem, mem[c.Arg[0]+fp-1]) case Fnew: mem = append(mem, NewValue(mem[c.Arg[0]].Type, c.Arg[1:]...)) + case FnewE: + mem = append(mem, NewValue(mem[c.Arg[0]].Type.Elem(), c.Arg[1:]...)) case Field: fv := mem[sp-1].FieldByIndex(c.Arg) if !fv.CanSet() { @@ -180,6 +184,13 @@ func (m *Machine) Run() (err error) { fv = reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem() } mem[sp-1].Value = fv + case FieldE: + fv := mem[sp-1].Value.Elem().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() + } + mem[sp-1].Value = fv case FieldSet: fv := mem[sp-2].FieldByIndex(c.Arg) if !fv.CanSet() { |
