From 378683d25bdae89fa446b2b82f8dda5d6b34ea33 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Thu, 14 Mar 2024 14:37:32 +0100 Subject: feat: initial support of closures Also detection and automatic execution of main function. Make sure that all debug is output to stderr. --- parser/README.md | 2 +- parser/compiler.go | 15 ++++++++------- parser/expr.go | 11 +++++++++++ parser/interpreter.go | 10 +++------- parser/interpreter_test.go | 1 + parser/parse.go | 9 ++++++++- 6 files changed, 32 insertions(+), 16 deletions(-) (limited to 'parser') 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 -- cgit v1.2.3