diff options
| -rw-r--r-- | lang/token.go | 9 | ||||
| -rw-r--r-- | parser/compiler.go | 106 | ||||
| -rw-r--r-- | parser/decl.go | 20 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 7 | ||||
| -rw-r--r-- | parser/symbol.go | 26 | ||||
| -rw-r--r-- | parser/type.go | 7 | ||||
| -rw-r--r-- | vm/vm.go | 40 | ||||
| -rw-r--r-- | vm/vm_test.go | 5 |
8 files changed, 140 insertions, 80 deletions
diff --git a/lang/token.go b/lang/token.go index 40e6bfc..5205efc 100644 --- a/lang/token.go +++ b/lang/token.go @@ -59,7 +59,7 @@ const ( // Unary operations Plus // unary + Minus // unary - - Address // unary & + Addr // unary & Deref // unary * BitComp // unary ^ Arrow // unary -> @@ -107,19 +107,20 @@ const ( // Internal virtual machine tokens (no corresponding keyword) Call CallX + EqualSet Grow Index - Label JumpFalse JumpSetFalse JumpSetTrue - EqualSet + Label + New ) // TODO: define UnaryOp per language var UnaryOp = map[TokenId]TokenId{ Add: Plus, // + - And: Address, // & + And: Addr, // & Not: Not, // ! Mul: Deref, // * Sub: Minus, // - 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: "<nil>"}, + {src: "", res: "<invalid reflect.Value>"}, {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: "<nil>"}, - //{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) @@ -14,7 +14,7 @@ const ( // instruction effect on stack: values consumed -- values produced Nop = iota // -- Add // n1 n2 -- sum ; sum = n1+n2 - Address // a -- &a ; + Addr // a -- &a ; Assign // val -- ; mem[$1] = val Fassign // val -- ; mem[$1] = val Vassign // val dest -- ; dest.Set(val) @@ -39,6 +39,7 @@ const ( Lower // n1 n2 -- cond ; cond = n1 < n2 Loweri // n1 -- cond ; cond = n1 < $1 Mul // n1 n2 -- prod ; prod = n1*n2 + New // -- x; mem[fp+$1] = new mem[$2] Not // c -- r ; r = !c Pop // v -- Push // -- v @@ -50,7 +51,7 @@ const ( var strop = [...]string{ // for VM tracing. Nop: "Nop", Add: "Add", - Address: "Address", + Addr: "Addr", Assign: "Assign", Call: "Call", Calli: "Calli", @@ -74,6 +75,7 @@ var strop = [...]string{ // for VM tracing. Lower: "Lower", Loweri: "Loweri", Mul: "Mul", + New: "New", Not: "Not", Pop: "Pop", Push: "Push", @@ -87,8 +89,7 @@ type Code [][]int64 // Machine represents a virtual machine. type Machine struct { - code Code // code to execute - // mem []any // memory, as a stack + code Code // code to execute mem []reflect.Value // memory, as a stack ip, fp int // instruction and frame pointer ic uint64 // instruction counter, incremented at each instruction executed @@ -113,7 +114,7 @@ func (m *Machine) Run() (err error) { op3 = strconv.Itoa(int(c[3])) } } - log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-12s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, mem) + log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-12s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, Vstring(mem)) } for { @@ -125,15 +126,15 @@ func (m *Machine) Run() (err error) { mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() + mem[sp-1].Int())) mem = mem[:sp-1] case Mul: - mem[sp-2].SetInt(mem[sp-2].Int() * mem[sp-1].Int()) + mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() * mem[sp-1].Int())) mem = mem[:sp-1] - case Address: + case Addr: mem[sp-1] = mem[sp-1].Addr() case Assign: - mem[op[2]] = mem[sp-1] + mem[op[2]].Set(mem[sp-1]) mem = mem[:sp-1] case Fassign: - mem[fp+int(op[2])-1] = mem[sp-1] + mem[fp+int(op[2])-1].Set(mem[sp-1]) mem = mem[:sp-1] case Call: nip := int(mem[sp-1].Int()) @@ -161,6 +162,8 @@ func (m *Machine) Run() (err error) { mem[sp-1] = mem[sp-1].Elem() case Dup: mem = append(mem, mem[int(op[2])]) + case New: + mem[int(op[2])+fp-1] = reflect.New(mem[int(op[3])].Type()).Elem() case Equal: mem[sp-2] = reflect.ValueOf(mem[sp-2].Equal(mem[sp-1])) mem = mem[:sp-1] @@ -226,7 +229,9 @@ func (m *Machine) Run() (err error) { case Pop: mem = mem[:sp-int(op[2])] case Push: - mem = append(mem, reflect.ValueOf(int(op[2]))) + //mem = append(mem, reflect.ValueOf(int(op[2]))) + mem = append(mem, reflect.New(reflect.TypeOf(0)).Elem()) + mem[sp].SetInt(op[2]) case Grow: mem = append(mem, make([]reflect.Value, op[2])...) case Return: @@ -236,10 +241,10 @@ func (m *Machine) Run() (err error) { mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...) continue case Sub: - mem[sp-2].SetInt(mem[sp-1].Int() - mem[sp-2].Int()) + mem[sp-2] = reflect.ValueOf(int(mem[sp-1].Int() - mem[sp-2].Int())) mem = mem[:sp-1] case Subi: - mem[sp-1].SetInt(mem[sp-1].Int() - op[2]) + mem[sp-1] = reflect.ValueOf(int(mem[sp-1].Int() - op[2])) case Index: mem[sp-2] = mem[sp-1].Index(int(mem[sp-2].Int())) mem = mem[:sp-1] @@ -309,3 +314,14 @@ func slint(a []int64) []int { } return r } + +func Vstring(lv []reflect.Value) string { + s := "[" + for _, v := range lv { + if s != "[" { + s += " " + } + s += fmt.Sprintf("%v", v) + } + return s + "]" +} diff --git a/vm/vm_test.go b/vm/vm_test.go index c9540d2..07c063e 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -23,9 +23,8 @@ func TestVM(t *testing.T) { if err := m.Run(); err != nil { t.Errorf("run error: %v", err) } - t.Log(m.mem) - r := fmt.Sprintf("%v", m.mem[test.start:test.end]) - if r != test.mem { + t.Log(Vstring(m.mem)) + if r := Vstring(m.mem[test.start:test.end]); r != test.mem { t.Errorf("got %v, want %v", r, test.mem) } }) |
