From 6a32a7bc5f6320902cd5c2910a1353a0f7039237 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Thu, 23 Nov 2023 17:56:35 +0100 Subject: parser: fix allocation of local variables A 'New' instruction is added in VM to manage initialisation of typed variables in the stack. The instantiated type symbols are now added to global data. Accessing and setting values by address is now working. --- parser/compiler.go | 106 ++++++++++++++++++++++++++++++--------------- parser/decl.go | 20 ++++++--- parser/interpreter_test.go | 7 +-- parser/symbol.go | 26 ++++++----- parser/type.go | 7 +-- 5 files changed, 105 insertions(+), 61 deletions(-) (limited to 'parser') diff --git a/parser/compiler.go b/parser/compiler.go index 36aa9d8..bb88026 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -13,9 +13,9 @@ import ( type Compiler struct { *Parser - vm.Code // produced code, to fill VM with - Data []any // produced data, will be at the bottom of VM stack - Entry int // offset in Code to start execution from (skip function defintions) + vm.Code // produced code, to fill VM with + Data []reflect.Value // produced data, will be at the bottom of VM stack + Entry int // offset in Code to start execution from (skip function defintions) strings map[string]int // locations of strings in Data } @@ -28,7 +28,7 @@ func NewCompiler(scanner *scanner.Scanner) *Compiler { } } -func (c *Compiler) AddSym(name string, value any) int { +func (c *Compiler) AddSym(name string, value reflect.Value) int { p := len(c.Data) c.Data = append(c.Data, value) c.Parser.AddSym(p, name, value) @@ -51,7 +51,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if err != nil { return err } - push(&symbol{kind: symConst, value: n}) + push(&symbol{kind: symConst, value: reflect.ValueOf(n), Type: reflect.TypeOf(0)}) emit(int64(t.Pos), vm.Push, int64(n)) case lang.String: @@ -59,10 +59,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { i, ok := c.strings[s] if !ok { i = len(c.Data) - c.Data = append(c.Data, s) + c.Data = append(c.Data, reflect.ValueOf(s)) c.strings[s] = i } - push(&symbol{kind: symConst, value: s}) + push(&symbol{kind: symConst, value: reflect.ValueOf(s)}) emit(int64(t.Pos), vm.Dup, int64(i)) case lang.Add: @@ -87,25 +87,40 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.Plus: // Nothing to do. - case lang.Address: - emit(int64(t.Pos), vm.Address) + case lang.Addr: + push(&symbol{Type: reflect.PointerTo(pop().Type)}) + emit(int64(t.Pos), vm.Addr) case lang.Deref: + push(&symbol{Type: pop().Type.Elem()}) emit(int64(t.Pos), vm.Deref) case lang.Index: + push(&symbol{Type: pop().Type.Elem()}) emit(int64(t.Pos), vm.Index) case lang.Greater: + push(&symbol{Type: booleanOpType(pop(), pop())}) emit(int64(t.Pos), vm.Greater) case lang.Less: + push(&symbol{Type: booleanOpType(pop(), pop())}) emit(int64(t.Pos), vm.Lower) case lang.Call: + typ := pop().Type + // TODO: pop input types (careful with variadic function) + for i := 0; i < typ.NumOut(); i++ { + push(&symbol{Type: typ.Out(i)}) + } emit(int64(t.Pos), vm.Call) case lang.CallX: + typ := pop().Type + // TODO: pop input types (careful with variadic function) + for i := 0; i < typ.NumOut(); i++ { + push(&symbol{Type: typ.Out(i)}) + } emit(int64(t.Pos), vm.CallX, int64(t.Beg)) case lang.Grow: @@ -115,9 +130,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { // TODO: support assignment to local, composite objects st := tokens[i-1] l := len(c.Data) - c.Data = append(c.Data, nil) - // TODO: symbol should be added at parse, not here. - c.addSym(l, st.Str, nil, symVar, nil, false) + typ := pop().Type + v := reflect.New(typ).Elem() + c.addSym(l, st.Str, v, symVar, typ, false) + c.Data = append(c.Data, v) emit(int64(st.Pos), vm.Assign, int64(l)) case lang.Assign: @@ -130,7 +146,16 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if !ok { return fmt.Errorf("symbol not found: %s", st.Str) } + typ := pop().Type + if s.Type == nil { + s.Type = typ + s.value = reflect.New(typ).Elem() + } if s.local { + if !s.used { + emit(int64(st.Pos), vm.New, int64(s.index), int64(c.typeSym(s.Type).index)) + s.used = true + } emit(int64(st.Pos), vm.Fassign, int64(s.index)) break } @@ -141,9 +166,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { emit(int64(st.Pos), vm.Assign, int64(s.index)) case lang.Equal: + push(&symbol{Type: booleanOpType(pop(), pop())}) emit(int64(t.Pos), vm.Equal) case lang.EqualSet: + push(&symbol{Type: booleanOpType(pop(), pop())}) emit(int64(t.Pos), vm.EqualSet) case lang.Ident: @@ -172,64 +199,64 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { lc := len(c.Code) s, ok := c.symbols[t.Str] if ok { - s.value = lc + s.value = reflect.ValueOf(lc) if s.kind == symFunc { // label is a function entry point, register its code address in data. s.index = len(c.Data) - c.Data = append(c.Data, lc) + c.Data = append(c.Data, s.value) } else { - c.Data[s.index] = lc + c.Data[s.index] = s.value } } else { - c.symbols[t.Str] = &symbol{kind: symLabel, value: lc} + c.symbols[t.Str] = &symbol{kind: symLabel, value: reflect.ValueOf(lc)} } case lang.JumpFalse: label := t.Str[10:] - i := 0 + var i int64 if s, ok := c.symbols[label]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) - len(c.Code) + i = s.value.Int() - int64(len(c.Code)) } - emit(int64(t.Pos), vm.JumpFalse, int64(i)) + emit(int64(t.Pos), vm.JumpFalse, i) case lang.JumpSetFalse: label := t.Str[13:] - i := 0 + var i int64 if s, ok := c.symbols[label]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) - len(c.Code) + i = s.value.Int() - int64(len(c.Code)) } - emit(int64(t.Pos), vm.JumpSetFalse, int64(i)) + emit(int64(t.Pos), vm.JumpSetFalse, i) case lang.JumpSetTrue: label := t.Str[12:] - i := 0 + var i int64 if s, ok := c.symbols[label]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) - len(c.Code) + i = s.value.Int() - int64(len(c.Code)) } emit(int64(t.Pos), vm.JumpSetTrue, int64(i)) case lang.Goto: label := t.Str[5:] - i := 0 + var i int64 if s, ok := c.symbols[label]; !ok { t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) - len(c.Code) + i = s.value.Int() - int64(len(c.Code)) } - emit(int64(t.Pos), vm.Jump, int64(i)) + emit(int64(t.Pos), vm.Jump, i) case lang.Period: if f, ok := pop().Type.FieldByName("X" + t.Str[1:]); ok { @@ -264,16 +291,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if !ok { return fmt.Errorf("label not found: %q", label) } - c.Code[t.Beg][2] = int64(s.value.(int) - t.Beg) + c.Code[t.Beg][2] = s.value.Int() - int64(t.Beg) } return err } -func arithmeticOpType(s1, s2 *symbol) reflect.Type { - // TODO: make it complete - return symtype(s1) -} +func arithmeticOpType(s1, s2 *symbol) reflect.Type { return symtype(s1) } +func booleanOpType(s1, s2 *symbol) reflect.Type { return reflect.TypeOf(true) } func (c *Compiler) PrintCode() { labels := map[int][]string{} // labels indexed by code location @@ -281,7 +306,7 @@ func (c *Compiler) PrintCode() { for name, sym := range c.symbols { if sym.kind == symLabel || sym.kind == symFunc { - i := sym.value.(int) + i := int(sym.value.Int()) labels[i] = append(labels[i], name) } if sym.used { @@ -329,7 +354,7 @@ func (c *Compiler) PrintData() { } fmt.Println("# Data:") for i, d := range c.Data { - fmt.Printf("%4d %T %v %v\n", i, d, d, dict[i]) + fmt.Printf("%4d %T %v %v\n", i, d.Interface(), d, dict[i]) } } @@ -341,6 +366,19 @@ func (c *Compiler) NumIn(i int) (int, bool) { return -1, false } +func (c *Compiler) typeSym(t reflect.Type) *symbol { + tsym, ok := c.symbols[t.String()] + if !ok { + tsym = &symbol{index: unsetAddr, kind: symType, Type: t} + c.symbols[t.String()] = tsym + } + if tsym.index == unsetAddr { + tsym.index = len(c.Data) + c.Data = append(c.Data, reflect.New(t).Elem()) + } + return tsym +} + func slint64(a []int) []int64 { r := make([]int64, len(a)) for i, v := range a { diff --git a/parser/decl.go b/parser/decl.go index 782b57a..54a2989 100644 --- a/parser/decl.go +++ b/parser/decl.go @@ -4,12 +4,15 @@ import ( "errors" "go/constant" "go/token" + "reflect" "strings" "github.com/gnolang/parscan/lang" "github.com/gnolang/parscan/scanner" ) +var nilValue = reflect.ValueOf(nil) + func (p *Parser) ParseConst(in Tokens) (out Tokens, err error) { if len(in) < 2 { return out, errors.New("missing expression") @@ -51,8 +54,8 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) { if errors.Is(err, MissingTypeErr) { for _, lt := range decl.Split(lang.Comma) { vars = append(vars, lt[0].Str) - // TODO: compute type from rhs - p.addSym(unsetAddr, strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/"), nil, symConst, nil, false) + name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/") + p.addSym(unsetAddr, name, nilValue, symConst, nil, false) } } else { return out, err @@ -75,7 +78,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) { kind: symConst, index: unsetAddr, cval: cval, - value: constValue(cval), + value: reflect.ValueOf(constValue(cval)), local: p.funcScope != "", used: true, } @@ -222,7 +225,7 @@ func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) { if err != nil { return out, err } - p.addSym(unsetAddr, in[0].Str, nil, symType, typ, p.funcScope != "") + p.addSym(unsetAddr, in[0].Str, reflect.New(typ).Elem(), symType, typ, p.funcScope != "") return out, err } @@ -257,8 +260,13 @@ func (p *Parser) parseVarLine(in Tokens) (out Tokens, err error) { if errors.Is(err, MissingTypeErr) { for _, lt := range decl.Split(lang.Comma) { vars = append(vars, lt[0].Str) - // TODO: compute type from rhs - p.addSym(unsetAddr, strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/"), nil, symVar, nil, false) + name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/") + if p.funcScope == "" { + p.addSym(unsetAddr, name, nilValue, symVar, nil, false) + continue + } + p.addSym(p.framelen[p.funcScope], name, nilValue, symVar, nil, false) + p.framelen[p.funcScope]++ } } else { return out, err diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index 9555475..9110e3c 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -52,7 +52,7 @@ func run(t *testing.T, tests []etest) { func TestExpr(t *testing.T) { run(t, []etest{ - {src: "", res: ""}, + {src: "", res: ""}, {src: "1+2", res: "3"}, {src: "1+", err: "block not terminated"}, {src: "a := 1 + 2; b := 0; a + 1", res: "4"}, @@ -92,7 +92,7 @@ func TestFunc(t *testing.T) { {src: "func f() int {return 2}; a := f(); a", res: "2"}, {src: "func f() int {return 2}; f()", res: "2"}, {src: "func f(a int) int {return a+2}; f(3)", res: "5"}, - {src: "func f(a int) int {if a < 4 {a = 5}; return a }; f(3)", res: "5"}, + {src: "func f(a int) int {if a < 4 {a = 5}; return a}; f(3)", res: "5"}, {src: "func f(a int) int {return a+2}; 7 - f(3)", res: "2"}, {src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"}, {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"}, @@ -195,7 +195,8 @@ func TestArray(t *testing.T) { func TestPointer(t *testing.T) { run(t, []etest{ {src: "var a *int; a", res: ""}, - //{src: "var a int = 2; var b *int = &a; b", res: "2"}, + {src: "var a int; var b *int = &a; *b", res: "0"}, + {src: "var a int = 2; var b *int = &a; *b", res: "2"}, }) } diff --git a/parser/symbol.go b/parser/symbol.go index 7edcef3..6c5ab9b 100644 --- a/parser/symbol.go +++ b/parser/symbol.go @@ -22,12 +22,12 @@ const unsetAddr = -65535 type symbol struct { kind symKind - index int // address of symbol in frame - value any - cval constant.Value - Type reflect.Type - local bool // if true address is relative to local frame, otherwise global - used bool + index int // address of symbol in frame + value reflect.Value // + cval constant.Value // + Type reflect.Type // + local bool // if true address is relative to local frame, otherwise global + used bool // } func symtype(s *symbol) reflect.Type { @@ -37,11 +37,13 @@ func symtype(s *symbol) reflect.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 reflect.Value) { + 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) { +func (p *Parser) addSym(i int, name string, v reflect.Value, k symKind, t reflect.Type, local bool) { name = strings.TrimPrefix(name, "/") - p.symbols[name] = &symbol{kind: k, index: i, local: local, value: v, Type: t, used: true} + p.symbols[name] = &symbol{kind: k, index: i, local: local, value: v, Type: t} } // getSym searches for an existing symbol starting from the deepest scope. @@ -72,9 +74,9 @@ func initUniverse() map[string]*symbol { "nil": {index: unsetAddr}, "iota": {kind: symConst, index: unsetAddr}, - "true": {index: unsetAddr, value: true, Type: reflect.TypeOf(true)}, - "false": {index: unsetAddr, value: false, Type: reflect.TypeOf(false)}, + "true": {index: unsetAddr, value: reflect.ValueOf(true), Type: reflect.TypeOf(true)}, + "false": {index: unsetAddr, value: reflect.ValueOf(false), Type: reflect.TypeOf(false)}, - "println": {index: unsetAddr, value: func(v ...any) { fmt.Println(v...) }}, + "println": {index: unsetAddr, value: reflect.ValueOf(func(v ...any) { fmt.Println(v...) })}, } } diff --git a/parser/type.go b/parser/type.go index 31af1e3..ff16e4f 100644 --- a/parser/type.go +++ b/parser/type.go @@ -170,12 +170,7 @@ 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) { - var zv any = reflect.New(typ).Elem() - switch typ.Kind() { - case reflect.Struct, reflect.Array, reflect.Slice, reflect.Pointer: - default: - zv = zv.(reflect.Value).Interface() - } + zv := reflect.New(typ).Elem() switch flag { case parseTypeIn: p.addSym(-index-2, name, zv, symVar, typ, true) -- cgit v1.2.3