diff options
| author | Marc Vertes <mvertes@free.fr> | 2023-09-04 16:58:15 +0200 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2023-09-04 16:58:15 +0200 |
| commit | 4241593b42bffac2f8fcb63f1e88621fe025e360 (patch) | |
| tree | 48680879982947524e1b028e179608bb5cbcda23 | |
| parent | 8feddd9b3eefb920c4a0c7a5b4c3b8ae160f3c71 (diff) | |
codegen: add interpreter tests
Also simplify project structure. The executable is now produced in
the root directory. Work in progress.
| -rw-r--r-- | codegen/compiler.go | 36 | ||||
| -rw-r--r-- | codegen/compiler_test.go | 8 | ||||
| -rw-r--r-- | codegen/interpreter_test.go | 47 | ||||
| -rw-r--r-- | main.go (renamed from cmd/gint/main.go) | 0 | ||||
| -rw-r--r-- | scanner/scan.go | 2 | ||||
| -rw-r--r-- | testdata/add (renamed from samples/add) | 0 | ||||
| -rw-r--r-- | testdata/fib (renamed from samples/fib) | 0 | ||||
| -rw-r--r-- | testdata/p00 (renamed from samples/p00) | 0 | ||||
| -rw-r--r-- | testdata/p01 (renamed from samples/p01) | 0 | ||||
| -rw-r--r-- | testdata/p02 (renamed from samples/p02) | 0 | ||||
| -rw-r--r-- | testdata/p03 (renamed from samples/p03) | 0 | ||||
| -rw-r--r-- | testdata/p04 (renamed from samples/p04) | 0 | ||||
| -rw-r--r-- | testdata/p05 (renamed from samples/p05) | 0 | ||||
| -rw-r--r-- | testdata/p06 (renamed from samples/p06) | 0 | ||||
| -rw-r--r-- | testdata/p07 | 5 | ||||
| -rw-r--r-- | vm1/vm.go | 3 |
16 files changed, 92 insertions, 9 deletions
diff --git a/codegen/compiler.go b/codegen/compiler.go index 3952166..30fc070 100644 --- a/codegen/compiler.go +++ b/codegen/compiler.go @@ -2,6 +2,7 @@ package codegen import ( "fmt" + "log" "strings" "github.com/gnolang/parscan/parser" @@ -84,8 +85,14 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) { c.Emit(n, vm1.Assign, int64(l)) case parser.DeclFunc: + fun := frameNode[len(frameNode)-1] + if len(fun.Child) == 3 { // no return values + if c.Code[len(c.Code)-1][1] != vm1.Return { + c.Emit(n, vm1.Return, 0, int64(len(fun.Child[1].Child))) + } + } scope = popScope(scope) - fnote = notes[frameNode[len(frameNode)-1]] + fnote = notes[fun] case parser.Ident: ident := n.Content() @@ -121,7 +128,15 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) { case parser.StmtReturn: fun := frameNode[len(frameNode)-1] - c.Emit(n, vm1.Return, int64(len(n.Child)), int64(len(fun.Child[1].Child))) + nret := 0 + if len(fun.Child) > 3 { + if ret := fun.Child[2]; ret.Kind == parser.BlockParen { + nret = len(ret.Child) + } else { + nret = 1 + } + } + c.Emit(n, vm1.Return, int64(nret), int64(len(fun.Child[1].Child))) case parser.BlockStmt: nd.ipend = len(c.Code) @@ -151,6 +166,17 @@ func (c *Compiler) CodeGen(node *parser.Node) (err error) { } return true }) + + log.Println("main:", c.symbols["main"]) + if s, _, ok := c.getSym("main", ""); ok { + if i, ok := c.codeIndex(s); ok { + // Internal call is always relative to instruction pointer. + c.Emit(nil, vm1.Call, int64(i-len(c.Code))) + c.Entry = len(c.Code) - 1 + } + log.Println(vm1.Disassemble(c.Code)) + } + return } @@ -168,7 +194,11 @@ func (c *Compiler) addSym(v any, name string, local bool, n *parser.Node) int { } func (c *Compiler) Emit(n *parser.Node, op ...int64) int { - op = append([]int64{int64(n.Pos())}, op...) + var pos int64 + if n != nil { + pos = int64(n.Pos()) + } + op = append([]int64{pos}, op...) l := len(c.Code) c.Code = append(c.Code, op) return l diff --git a/codegen/compiler_test.go b/codegen/compiler_test.go index a876d52..fbe8bb2 100644 --- a/codegen/compiler_test.go +++ b/codegen/compiler_test.go @@ -12,7 +12,7 @@ import ( func TestCodeGen(t *testing.T) { log.SetFlags(log.Lshortfile) - for _, test := range tests { + for _, test := range codeGenTests { test := test t.Run("", func(t *testing.T) { c := NewCompiler() @@ -32,13 +32,13 @@ func TestCodeGen(t *testing.T) { t.Log("data:", c.Data) t.Log("code:", vm1.Disassemble(c.Code)) if s := vm1.Disassemble(c.Code); s != test.asm { - t.Errorf("got error %#v, want error %#v", s, test.asm) + t.Errorf("got %#v, want %#v", s, test.asm) } }) } } -var tests = []struct { +var codeGenTests = []struct { src, asm, sym, err string }{{ // #00 src: "1+2", @@ -55,4 +55,4 @@ var tests = []struct { }, { // #04 src: "func add(a int, b int) int { return a + b }", asm: "Fdup -2\nFdup -3\nAdd\nReturn 1 2\n", -}, {}} +}} diff --git a/codegen/interpreter_test.go b/codegen/interpreter_test.go new file mode 100644 index 0000000..1d8a6b5 --- /dev/null +++ b/codegen/interpreter_test.go @@ -0,0 +1,47 @@ +package codegen + +import ( + "fmt" + "log" + "testing" + + "github.com/gnolang/parscan/lang/golang" +) + +func TestEval(t *testing.T) { + for _, test := range evalTests { + test := test + t.Run("", func(t *testing.T) { + interp := NewInterpreter(golang.GoParser) + errStr := "" + r, e := interp.Eval(test.src) + if e != nil { + errStr = e.Error() + } + if errStr != test.err { + t.Errorf("got error %#v, want error %#v", errStr, test.err) + } + res := fmt.Sprintf("%v", r) + if res != test.res { + t.Errorf("got %#v, want %#v", res, test.res) + } + log.Println(r, e) + }) + } +} + +var evalTests = []struct { + name, src, res, err string +}{{ /* #00 */ + src: "1 + 2", + res: "3", +}, { // #01 + src: "a := 2; a = a + 3", + res: "5", +}, { // #02 + src: "func f(a int) int { return a + 1 }; f(5)", + res: "6", +}, { // #03 + src: "func f(a int) (b int) { b = a + 1; return }; f(5)", + res: "6", +}} diff --git a/cmd/gint/main.go b/main.go index 037614c..037614c 100644 --- a/cmd/gint/main.go +++ b/main.go diff --git a/scanner/scan.go b/scanner/scan.go index 914f4bc..1cc36a7 100644 --- a/scanner/scan.go +++ b/scanner/scan.go @@ -108,7 +108,7 @@ func (sc *Scanner) IsId(r rune) bool { } func (sc *Scanner) Init() { - // Build a regular expression to match all string delimiters. + // Build a regular expression to match all string start delimiters at once. re := "(" for s, p := range sc.BlockProp { if p&CharStr == 0 { diff --git a/samples/add b/testdata/add index a403485..a403485 100644 --- a/samples/add +++ b/testdata/add diff --git a/samples/fib b/testdata/fib index 654c5c0..654c5c0 100644 --- a/samples/fib +++ b/testdata/fib diff --git a/samples/p00 b/testdata/p00 index 19f5084..19f5084 100644 --- a/samples/p00 +++ b/testdata/p00 diff --git a/samples/p01 b/testdata/p01 index baafaa9..baafaa9 100644 --- a/samples/p01 +++ b/testdata/p01 diff --git a/samples/p02 b/testdata/p02 index 1aeb4a9..1aeb4a9 100644 --- a/samples/p02 +++ b/testdata/p02 diff --git a/samples/p03 b/testdata/p03 index 39a3cda..39a3cda 100644 --- a/samples/p03 +++ b/testdata/p03 diff --git a/samples/p04 b/testdata/p04 index f2a85c8..f2a85c8 100644 --- a/samples/p04 +++ b/testdata/p04 diff --git a/samples/p05 b/testdata/p05 index 51c2c9b..51c2c9b 100644 --- a/samples/p05 +++ b/testdata/p05 diff --git a/samples/p06 b/testdata/p06 index 88029cc..88029cc 100644 --- a/samples/p06 +++ b/testdata/p06 diff --git a/testdata/p07 b/testdata/p07 new file mode 100644 index 0000000..2fa7ee0 --- /dev/null +++ b/testdata/p07 @@ -0,0 +1,5 @@ +func f1() { println("in f1") } + +func f2() { println("in f2"); f1() } + +f2() @@ -2,6 +2,7 @@ package vm1 import ( "fmt" // for tracing only + "log" // for tracing only "reflect" // for optional CallX only "strconv" // for tracing only ) @@ -79,7 +80,7 @@ func (m *Machine) Run() (err error) { op3 = strconv.Itoa(int(c[3])) } } - fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-9s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, mem) + log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-9s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, mem) } for { |
