diff options
| author | Marc Vertes <mvertes@free.fr> | 2023-11-17 16:45:58 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2023-11-17 16:45:58 +0100 |
| commit | ee21c324ce8c41b589e5a39e5715223ffd154315 (patch) | |
| tree | 9dfe692c787ceb65d85f3a9e04524759fab31200 | |
| parent | a79e558d825c5b777c95c5e098b01391ee36781e (diff) | |
parser: support selector expression to get / set struct fields
The structures are reresented by reflect values. New instructions
`Field` and `Vassign` have been added to the VM to assign reflect
values and access struct fields.
| -rw-r--r-- | parser/README.md | 2 | ||||
| -rw-r--r-- | parser/compiler.go | 47 | ||||
| -rw-r--r-- | parser/expr.go | 16 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 9 | ||||
| -rw-r--r-- | parser/symbol.go | 7 | ||||
| -rw-r--r-- | parser/type.go | 5 | ||||
| -rw-r--r-- | vm/vm.go | 17 |
7 files changed, 91 insertions, 12 deletions
diff --git a/parser/README.md b/parser/README.md index 3fbbf61..6a1a0e0 100644 --- a/parser/README.md +++ b/parser/README.md @@ -77,7 +77,7 @@ Go language support: - [x] parenthesis expressions - [x] call expressions - [ ] index expressions -- [ ] selector expressions +- [x] selector expressions - [ ] type convertions - [ ] type assertions - [ ] parametric types (generic) diff --git a/parser/compiler.go b/parser/compiler.go index ed950cb..36fc9d4 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -36,10 +36,13 @@ func (c *Compiler) AddSym(name string, value any) int { } func (c *Compiler) Codegen(tokens Tokens) (err error) { - fixList := Tokens{} log.Println("Codegen tokens:", tokens) + fixList := Tokens{} // list of tokens to fix after we gathered all necessary information + stack := []*symbol{} // for symbolic evaluation, type checking, etc emit := func(op ...int64) { c.Code = append(c.Code, op) } + push := func(s *symbol) { stack = append(stack, s) } + pop := func() *symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s } for i, t := range tokens { switch t.Id { @@ -48,6 +51,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if err != nil { return err } + push(&symbol{kind: symConst, value: n}) emit(int64(t.Pos), vm.Push, int64(n)) case lang.String: @@ -58,15 +62,19 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { c.Data = append(c.Data, s) c.strings[s] = i } + push(&symbol{kind: symConst, value: s}) emit(int64(t.Pos), vm.Dup, int64(i)) case lang.Add: + push(&symbol{Type: arithmeticOpType(pop(), pop())}) emit(int64(t.Pos), vm.Add) case lang.Mul: + push(&symbol{Type: arithmeticOpType(pop(), pop())}) emit(int64(t.Pos), vm.Mul) case lang.Sub: + push(&symbol{Type: arithmeticOpType(pop(), pop())}) emit(int64(t.Pos), vm.Sub) case lang.Greater: @@ -95,19 +103,23 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.Assign: st := tokens[i-1] + if st.Id == lang.Period { + emit(int64(t.Pos), vm.Vassign) + break + } s, ok := c.symbols[st.Str] if !ok { return fmt.Errorf("symbol not found: %s", st.Str) } if s.local { emit(int64(st.Pos), vm.Fassign, int64(s.index)) - } else { - if s.index == unsetAddr { - s.index = len(c.Data) - c.Data = append(c.Data, s.value) - } - emit(int64(st.Pos), vm.Assign, int64(s.index)) + break + } + if s.index == unsetAddr { + s.index = len(c.Data) + c.Data = append(c.Data, s.value) } + emit(int64(st.Pos), vm.Assign, int64(s.index)) case lang.Equal: emit(int64(t.Pos), vm.Equal) @@ -126,6 +138,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if !ok { return fmt.Errorf("symbol not found: %s", t.Str) } + push(s) if s.local { emit(int64(t.Pos), vm.Fdup, int64(s.index)) } else { @@ -199,6 +212,13 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } emit(int64(t.Pos), vm.Jump, int64(i)) + case lang.Period: + if f, ok := pop().Type.FieldByName("X" + t.Str[1:]); ok { + emit(append([]int64{int64(t.Pos), vm.Field}, slint64(f.Index)...)...) + break + } + return fmt.Errorf("field or method not found: %s", t.Str[1:]) + case lang.Return: emit(int64(t.Pos), vm.Return, int64(t.Beg), int64(t.End)) @@ -231,6 +251,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { return err } +func arithmeticOpType(s1, s2 *symbol) reflect.Type { + // TODO: make it complete + return symtype(s1) +} + func (c *Compiler) PrintCode() { labels := map[int][]string{} // labels indexed by code location data := map[int]string{} // data indexed by frame location @@ -296,3 +321,11 @@ func (c *Compiler) NumIn(i int) (int, bool) { } return -1, false } + +func slint64(a []int) []int64 { + r := make([]int64, len(a)) + for i, v := range a { + r[i] = int64(v) + } + return r +} diff --git a/parser/expr.go b/parser/expr.go index 2cd19c6..c2d5eeb 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -10,8 +10,9 @@ import ( func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { log.Println("ParseExpr in:", in) - var ops Tokens + var ops, selectors Tokens var vl int + var selectorId string // // Process tokens from last to first, the goal is to reorder the tokens in // a stack machine processing order, so it can be directly interpreted. @@ -21,8 +22,11 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { // temporary assumptions: binary operators, returning 1 value switch t.Id { case lang.Ident: + if i > 0 && in[i-1].Id == lang.Period { + selectorId = t.Str + continue + } // resolve symbol if not a selector rhs. - // TODO: test for selector expr. _, sc, ok := p.getSym(t.Str, p.scope) if ok { if sc != "" { @@ -31,6 +35,10 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { } out = append(out, t) vl++ + case lang.Period: + t.Str += selectorId + selectors = append(Tokens{t}, selectors...) + continue case lang.Int, lang.String: out = append(out, t) vl++ @@ -64,6 +72,10 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { } ops = append(ops, scanner.Token{Str: "call", Id: lang.Call, Pos: t.Pos}) } + if len(selectors) > 0 { + out = append(out, selectors...) + selectors = nil + } if lops, lout := len(ops), len(out); lops > 0 && vl > lops { op := ops[lops-1] ops = ops[:lops-1] diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index ac8dfc4..255f7c5 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -180,6 +180,14 @@ func TestConst(t *testing.T) { }) } +func TestStruct(t *testing.T) { + run(t, []etest{ + {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"}, + {src: "type T struct {a int}; var t T; t.a", res: "0"}, + {src: "type T struct {a int}; var t T; t.a = 1; t.a", res: "1"}, + }) +} + func TestType(t *testing.T) { src0 := `type( I int @@ -190,7 +198,6 @@ func TestType(t *testing.T) { {src: "type t int; var a t = 1; a", res: "1"}, {src: "type t = int; var a t = 1; a", res: "1"}, {src: src0 + `var s S = "xx"; s`, res: "xx"}, - {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"}, }) } diff --git a/parser/symbol.go b/parser/symbol.go index f707feb..7edcef3 100644 --- a/parser/symbol.go +++ b/parser/symbol.go @@ -30,6 +30,13 @@ type symbol struct { used bool } +func symtype(s *symbol) reflect.Type { + if s.Type != nil { + return s.Type + } + return reflect.TypeOf(s.value) +} + func (p *Parser) AddSym(i int, name string, v any) { p.addSym(i, name, v, symValue, nil, false) } func (p *Parser) addSym(i int, name string, v any, k symKind, t reflect.Type, local bool) { diff --git a/parser/type.go b/parser/type.go index f39acea..f1a2642 100644 --- a/parser/type.go +++ b/parser/type.go @@ -142,7 +142,10 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type } func (p *Parser) addSymVar(index int, name string, typ reflect.Type, flag typeFlag, local bool) { - zv := reflect.New(typ).Elem().Interface() + var zv any = reflect.New(typ).Elem() + if typ.Kind() != reflect.Struct { + zv = zv.(reflect.Value).Interface() + } switch flag { case parseTypeIn: p.addSym(-index-2, name, zv, symVar, typ, true) @@ -16,6 +16,7 @@ const ( Add // n1 n2 -- sum ; sum = n1+n2 Assign // val -- ; mem[$1] = val Fassign // val -- ; mem[$1] = val + Vassign // val dest -- ; dest.Set(val) Call // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...) Calli // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...) CallX // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = mem[f](a1, ...) @@ -24,6 +25,7 @@ const ( Equal // n1 n2 -- cond ; cond = n1 == n2 EqualSet // n1 n2 -- n1 cond ; cond = n1 == n2 Exit // -- ; + Field // s -- f ; f = s.FieldIndex($1, ...) Greater // n1 n2 -- cond; cond = n1 > n2 Grow // -- ; sp += $1 Jump // -- ; ip += $1 @@ -54,6 +56,7 @@ var strop = [...]string{ // for VM tracing. Exit: "Exit", Fassign: "Fassign", Fdup: "Fdup", + Field: "Field", Greater: "Greater", Grow: "Grow", Jump: "Jump", @@ -69,6 +72,7 @@ var strop = [...]string{ // for VM tracing. Return: "Return", Sub: "Sub", Subi: "Subi", + Vassign: "Vassign", } type Code [][]int64 @@ -161,6 +165,8 @@ func (m *Machine) Run() (err error) { return err case Fdup: mem = append(mem, mem[int(op[2])+fp-1]) + case Field: + mem[sp-1] = mem[sp-1].(reflect.Value).FieldByIndex(slint(op[2:])) case Jump: ip += int(op[2]) continue @@ -219,6 +225,9 @@ func (m *Machine) Run() (err error) { mem = mem[:sp-1] case Subi: mem[sp-1] = mem[sp-1].(int) - int(op[2]) + case Vassign: + mem[sp-1].(reflect.Value).Set(reflect.ValueOf(mem[sp-2])) + mem = mem[:sp-2] } ip++ } @@ -265,3 +274,11 @@ func Disassemble(code [][]int64) (asm string) { } return asm } + +func slint(a []int64) []int { + r := make([]int, len(a)) + for i, v := range a { + r[i] = int(v) + } + return r +} |
