diff options
| author | Marc Vertes <mvertes@free.fr> | 2024-03-14 14:37:32 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2024-03-14 14:37:32 +0100 |
| commit | 378683d25bdae89fa446b2b82f8dda5d6b34ea33 (patch) | |
| tree | cbf624e4eb38fafe51a12a718b48f43192cfcfac | |
| parent | 60170bad25f912e0ba44e4c0095b54f6e26e307e (diff) | |
feat: initial support of closures
Also detection and automatic execution of main function.
Make sure that all debug is output to stderr.
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | parser/README.md | 2 | ||||
| -rw-r--r-- | parser/compiler.go | 15 | ||||
| -rw-r--r-- | parser/expr.go | 11 | ||||
| -rw-r--r-- | parser/interpreter.go | 10 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 1 | ||||
| -rw-r--r-- | parser/parse.go | 9 | ||||
| -rw-r--r-- | vm/type.go | 3 | ||||
| -rw-r--r-- | vm/vm.go | 2 | ||||
| -rw-r--r-- | vm/vm_test.go | 8 |
10 files changed, 41 insertions, 22 deletions
@@ -1,3 +1,3 @@ module github.com/mvertes/parscan -go 1.20 +go 1.21 diff --git a/parser/README.md b/parser/README.md index 118d9aa..41a09a6 100644 --- a/parser/README.md +++ b/parser/README.md @@ -27,7 +27,7 @@ to the bytecode instructions set except that: Go language support: - [x] named functions -- [ ] anonymous functions (closures) +- [x] anonymous functions (closures) - [ ] methods - [x] internal function calls - [x] external function calls (calling runtime symbols in interpreter) diff --git a/parser/compiler.go b/parser/compiler.go index 82228ed..886da9b 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -3,6 +3,7 @@ package parser import ( "fmt" "log" + "os" "strconv" "github.com/mvertes/parscan/lang" @@ -313,10 +314,10 @@ func (c *Compiler) PrintCode() { } } - fmt.Println("# Code:") + fmt.Fprintln(os.Stderr, "# Code:") for i, l := range c.Code { for _, label := range labels[i] { - fmt.Println(label + ":") + fmt.Fprintln(os.Stderr, label+":") } extra := "" switch l[1] { @@ -329,13 +330,13 @@ func (c *Compiler) PrintCode() { extra = "// " + d } } - fmt.Printf("%4d %-14v %v\n", i, vm.CodeString(l), extra) + fmt.Fprintf(os.Stderr, "%4d %-14v %v\n", i, vm.CodeString(l), extra) } for _, label := range labels[len(c.Code)] { - fmt.Println(label + ":") + fmt.Fprintln(os.Stderr, label+":") } - fmt.Println("# End code") + fmt.Fprintln(os.Stderr, "# End code") } type entry struct { @@ -351,9 +352,9 @@ func (c *Compiler) PrintData() { } dict[sym.index] = entry{name, sym} } - fmt.Println("# Data:") + fmt.Fprintln(os.Stderr, "# Data:") for i, d := range c.Data { - fmt.Printf("%4d %T %v %v\n", i, d.Data.Interface(), d.Data, dict[i]) + fmt.Fprintf(os.Stderr, "%4d %T %v %v\n", i, d.Data.Interface(), d.Data, dict[i]) } } diff --git a/parser/expr.go b/parser/expr.go index 91c093f..e952f7c 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -17,6 +17,17 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { // 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. // + if len(in) > 1 && in[0].Id == lang.Func { + // Function as value (i.e closure). + if out, err = p.ParseFunc(in); err != nil { + return out, err + } + // Get function label and use it as a symbol ident. + fid := out[1] + fid.Id = lang.Ident + out = append(out, fid) + return out, err + } for i := len(in) - 1; i >= 0; i-- { t := in[i] // temporary assumptions: binary operators, returning 1 value diff --git a/parser/interpreter.go b/parser/interpreter.go index c5e463a..3ecbc46 100644 --- a/parser/interpreter.go +++ b/parser/interpreter.go @@ -34,6 +34,9 @@ func (i *Interpreter) Eval(src string) (res any, err error) { } i.Push(i.Data[dataOffset:]...) i.PushCode(i.Code[codeOffset:]...) + if s, ok := i.symbols["main"]; ok { + i.PushCode([]int64{0, vm.Calli, i.Data[s.index].Data.Int()}) + } i.PushCode([]int64{0, vm.Exit}) i.SetIP(max(codeOffset, i.Entry)) if debug { @@ -43,10 +46,3 @@ func (i *Interpreter) Eval(src string) (res any, err error) { err = i.Run() return i.Top().Data, err } - -func max(a, b int) int { - if a > b { - return a - } - return b -} diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index 8f166df..df44fae 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -98,6 +98,7 @@ func TestFunc(t *testing.T) { {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"}, {src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"}, {src: "var a int; func f() {a = a+2}; f(); a", res: "2"}, + {src: "var f = func(a int) int {return a+3}; f(2)", res: "5"}, }) } diff --git a/parser/parse.go b/parser/parse.go index 547957b..36ff125 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -24,6 +24,7 @@ type Parser struct { labelCount map[string]int breakLabel string continueLabel string + clonum int // closure instance number } func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) { @@ -203,7 +204,13 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { // TODO: handle receiver (methods) // TODO: handle parametric types (generics) // TODO: handle variadic parameters - fname := in[1].Str + var fname string + if in[1].Id == lang.Ident { + fname = in[1].Str + } else { + fname = "#f" + strconv.Itoa(p.clonum) + p.clonum++ + } ofname := p.fname p.fname = fname ofunc := p.function @@ -26,6 +26,9 @@ type Value struct { // NewValue returns an addressable zero value for the specified type. func NewValue(typ *Type) Value { + if typ.Rtype.Kind() == reflect.Func { + typ = TypeOf(0) // Function value is its index in the code segment. + } return Value{Type: typ, Data: reflect.New(typ.Rtype).Elem()} } @@ -145,7 +145,7 @@ func (m *Machine) Run() (err error) { case Calli: mem = append(mem, ValueOf(ip+1), ValueOf(fp)) fp = sp + 2 - ip += int(op[2]) + ip = int(op[2]) continue case CallX: // Should be made optional. l := int(op[2]) diff --git a/vm/vm_test.go b/vm/vm_test.go index fb71176..8fa8e71 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -75,7 +75,7 @@ var tests = []struct { {0, Push, 3}, // 1 {0, Return, 1, 1}, // 2 {0, Push, 1}, // 3 - {0, Calli, -3}, // 4 + {0, Calli, 1}, // 4 {0, Exit}, // 5 }, start: 0, end: 1, mem: "[3]", @@ -138,16 +138,16 @@ var tests = []struct { {0, JumpTrue, 9}, // 3 [], goto 12 {0, Fdup, -2}, // 4 [i] {0, Subi, 2}, // 5 [(i-2)] - {0, Calli, -5}, // 6 [fib(i-2)] + {0, Calli, 1}, // 6 [fib(i-2)] {0, Fdup, -2}, // 7 [fib(i-2) i] {0, Subi, 1}, // 8 [(i-2) (i-1)] - {0, Calli, -8}, // 9 [fib(i-2) fib(i-1)], call 1 + {0, Calli, 1}, // 9 [fib(i-2) fib(i-1)], call 1 {0, Add}, // 10 [fib(i-2)+fib(i-1)] {0, Return, 1, 1}, // 11 return i {0, Fdup, -2}, // 12 [i] {0, Return, 1, 1}, // 13 return i {0, Push, 6}, // 14 [1] - {0, Calli, -14}, // 15 [fib(*1)], call 1 + {0, Calli, 1}, // 15 [fib(*1)], call 1 {0, Exit}, // 16 }, start: 0, end: 1, mem: "[8]", |
