summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--parser/README.md2
-rw-r--r--parser/compiler.go15
-rw-r--r--parser/expr.go11
-rw-r--r--parser/interpreter.go10
-rw-r--r--parser/interpreter_test.go1
-rw-r--r--parser/parse.go9
-rw-r--r--vm/type.go3
-rw-r--r--vm/vm.go2
-rw-r--r--vm/vm_test.go8
10 files changed, 41 insertions, 22 deletions
diff --git a/go.mod b/go.mod
index bd9551c..9c02fa8 100644
--- a/go.mod
+++ b/go.mod
@@ -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 b233248..6f0f734 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 e4cef1a..0b2ad2a 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -18,6 +18,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
diff --git a/vm/type.go b/vm/type.go
index d29de48..16e3733 100644
--- a/vm/type.go
+++ b/vm/type.go
@@ -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()}
}
diff --git a/vm/vm.go b/vm/vm.go
index a8c1f28..6472dae 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -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]",