summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lang/token.go9
-rw-r--r--parser/compiler.go106
-rw-r--r--parser/decl.go20
-rw-r--r--parser/interpreter_test.go7
-rw-r--r--parser/symbol.go26
-rw-r--r--parser/type.go7
-rw-r--r--vm/vm.go40
-rw-r--r--vm/vm_test.go5
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)
diff --git a/vm/vm.go b/vm/vm.go
index 52618de..f282367 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -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)
}
})