summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.golangci.yaml3
-rw-r--r--main.go2
-rw-r--r--parser/compiler.go25
-rw-r--r--parser/expr.go2
-rw-r--r--parser/interpreter_test.go2
-rw-r--r--parser/parse.go25
-rw-r--r--parser/type.go16
-rw-r--r--scanner/scan.go11
-rw-r--r--vm/vm_test.go104
9 files changed, 149 insertions, 41 deletions
diff --git a/.golangci.yaml b/.golangci.yaml
index 64e12a5..f2191fb 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -5,6 +5,9 @@ linters:
- gofumpt
- gosec
- misspell
+ - perfsprint
+ - prealloc
- predeclared
- reassign
- revive
+ - unconvert
diff --git a/main.go b/main.go
index f9f125b..276f732 100644
--- a/main.go
+++ b/main.go
@@ -46,7 +46,7 @@ func repl(interp Interpreter, in io.Reader) (err error) {
res, err := interp.Eval(text + "\n")
switch {
case err == nil:
- if !res.IsNil() {
+ if res.IsValid() {
fmt.Println(": ", res)
}
text, prompt = "", "> "
diff --git a/parser/compiler.go b/parser/compiler.go
index 3b9a32d..05462eb 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -61,13 +61,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
case lang.String:
s := t.Block()
+ v := vm.Value{Data: reflect.ValueOf(s), Type: vm.TypeOf(s)}
i, ok := c.strings[s]
if !ok {
i = len(c.Data)
- c.Data = append(c.Data, vm.ValueOf(s))
+ c.Data = append(c.Data, v)
c.strings[s] = i
}
- push(&symbol{kind: symConst, value: vm.ValueOf(s)})
+ push(&symbol{kind: symConst, value: v})
emit(int64(t.Pos), vm.Dup, int64(i))
case lang.Add:
@@ -144,7 +145,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
// TODO: support assignment to local, composite objects.
st := tokens[i-1]
l := len(c.Data)
- typ := pop().Type
+ d := pop()
+ typ := d.Type
+ if typ == nil {
+ typ = d.value.Type
+ }
v := vm.NewValue(typ)
c.addSym(l, st.Str, v, symVar, typ, false)
c.Data = append(c.Data, v)
@@ -160,7 +165,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if !ok {
return fmt.Errorf("symbol not found: %s", st.Str)
}
- typ := pop().Type
+ d := pop()
+ typ := d.Type
+ if typ == nil {
+ typ = d.value.Type
+ }
if s.Type == nil {
s.Type = typ
s.value = vm.NewValue(typ)
@@ -259,7 +268,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
} else {
i = s.value.Data.Int() - int64(len(c.Code))
}
- emit(int64(t.Pos), vm.JumpSetTrue, int64(i))
+ emit(int64(t.Pos), vm.JumpSetTrue, i)
case lang.Goto:
var i int64
@@ -431,17 +440,17 @@ type DumpValue struct {
// This design choice allows the Virtual Machine (VM) to evolve its memory management strategies
// without compromising backward compatibility with dumps generated by previous versions.
func (c *Compiler) Dump() *Dump {
- var dv []*DumpValue
dict := c.symbolsByIndex()
+ dv := make([]*DumpValue, len(c.Data))
for i, d := range c.Data {
e := dict[i]
- dv = append(dv, &DumpValue{
+ dv[i] = &DumpValue{
Index: e.index,
Name: e.name,
Kind: int(e.kind),
Type: e.Type.Name,
Value: d.Data.Interface(),
- })
+ }
}
return &Dump{Values: dv}
diff --git a/parser/expr.go b/parser/expr.go
index cf6ee74..1efb45e 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -110,7 +110,7 @@ func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) {
case lang.Comment:
return out, nil
default:
- return nil, fmt.Errorf("expression not supported yet: %v: %q", t.Tok, t.Str)
+ return nil, fmt.Errorf("invalid expression: %v: %q", t.Tok, t.Str)
}
if len(selectors) > 0 {
out = append(out, selectors...)
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index a6ad246..314f35b 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -69,6 +69,7 @@ func TestExpr(t *testing.T) {
{src: "-2 + 5", res: "3"},
{src: "5 + -2", res: "3"},
{src: "!false", res: "true"},
+ {src: `a := "hello"`, res: "hello"},
})
}
@@ -233,6 +234,7 @@ func TestVar(t *testing.T) {
{src: "var a, b int = 2, 5; a+b", res: "7"},
{src: "var x = 5; x", res: "5"},
{src: "var a = 1; func f() int { var a, b int = 3, 4; return a+b}; a+f()", res: "8"},
+ {src: `var a = "hello"; a`, res: "hello"},
{src: `var (
a, b int = 4+1, 3
c = 8
diff --git a/parser/parse.go b/parser/parse.go
index add1600..537b68e 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -29,6 +29,15 @@ type Parser struct {
clonum int // closure instance number
}
+// Parser errors.
+var (
+ ErrBody = errors.New("missign body")
+ ErrBreak = errors.New("invalid break statement")
+ ErrContinue = errors.New("invalid continue statement")
+ ErrFor = errors.New("invalid for statement")
+ ErrGoto = errors.New("invalid goto statement")
+)
+
// Scan performs lexical analysis on s and returns Tokens or an error.
func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) {
return p.Scanner.Scan(s, endSemi)
@@ -36,11 +45,11 @@ func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) {
// Parse performs syntax analysis on s and return Tokens or an error.
func (p *Parser) Parse(src string) (out Tokens, err error) {
- log.Printf("Parse src: %#v\n", src)
in, err := p.Scan(src, true)
if err != nil {
return out, err
}
+ log.Printf("Parse src: %#v\n", src)
return p.parseStmts(in)
}
@@ -122,12 +131,12 @@ func (p *Parser) parseBreak(in Tokens) (out Tokens, err error) {
label = p.breakLabel
case 2:
if in[1].Tok != lang.Ident {
- return nil, fmt.Errorf("invalid break statement")
+ return nil, ErrBreak
}
// TODO: check validity of user provided label
label = in[1].Str
default:
- return nil, fmt.Errorf("invalid break statement")
+ return nil, ErrBreak
}
out = Tokens{{Tok: lang.Goto, Str: label}}
return out, err
@@ -140,12 +149,12 @@ func (p *Parser) parseContinue(in Tokens) (out Tokens, err error) {
label = p.continueLabel
case 2:
if in[1].Tok != lang.Ident {
- return nil, fmt.Errorf("invalid continue statement")
+ return nil, ErrContinue
}
// TODO: check validity of user provided label
label = in[1].Str
default:
- return nil, fmt.Errorf("invalid continue statement")
+ return nil, ErrContinue
}
out = Tokens{{Tok: lang.Goto, Str: label}}
return out, err
@@ -153,7 +162,7 @@ func (p *Parser) parseContinue(in Tokens) (out Tokens, err error) {
func (p *Parser) parseGoto(in Tokens) (out Tokens, err error) {
if len(in) != 2 || in[1].Tok != lang.Ident {
- return nil, fmt.Errorf("invalid goto statement")
+ return nil, ErrGoto
}
// TODO: check validity of user provided label
return Tokens{{Tok: lang.Goto, Str: p.funcScope + "/" + in[1].Str}}, nil
@@ -171,7 +180,7 @@ func (p *Parser) parseFor(in Tokens) (out Tokens, err error) {
case 3:
init, cond, post = pre[0], pre[1], pre[2]
default:
- return nil, fmt.Errorf("invalild for statement")
+ return nil, ErrFor
}
breakLabel, continueLabel := p.breakLabel, p.continueLabel
p.pushScope("for" + fc)
@@ -247,7 +256,7 @@ func (p *Parser) parseFunc(in Tokens) (out Tokens, err error) {
bi := in.Index(lang.BraceBlock)
if bi < 0 {
- return out, fmt.Errorf("no function body")
+ return out, ErrBody
}
typ, err := p.parseTypeExpr(in[:bi])
if err != nil {
diff --git a/parser/type.go b/parser/type.go
index ba60e8f..e25431a 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -20,10 +20,12 @@ const (
// Type parsing error definitions.
var (
- ErrInvalidType = errors.New("invalid type")
- ErrMissingType = errors.New("missing type")
- ErrSyntax = errors.New("syntax error")
- ErrTypeNotImplemented = errors.New("not implemented")
+ ErrFuncType = errors.New("invalid function type")
+ ErrInvalidType = errors.New("invalid type")
+ ErrMissingType = errors.New("missing type")
+ ErrSize = errors.New("invalid size")
+ ErrSyntax = errors.New("syntax error")
+ ErrNotImplemented = errors.New("not implemented")
)
func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
@@ -44,7 +46,7 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
}
size, ok := constValue(cval).(int)
if !ok {
- return nil, fmt.Errorf("invalid size")
+ return nil, ErrSize
}
return vm.ArrayOf(size, typ), nil
}
@@ -71,7 +73,7 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
case l >= 2 && in1.Tok == lang.ParenBlock:
indexArgs, out = 1, in[2:]
default:
- return nil, fmt.Errorf("invalid func signature")
+ return nil, ErrFuncType
}
// We can now parse function input and output parameter types.
@@ -125,7 +127,7 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
return vm.StructOf(fields), nil
default:
- return nil, fmt.Errorf("%w: %v", ErrTypeNotImplemented, in[0].Name())
+ return nil, fmt.Errorf("%w: %v", ErrNotImplemented, in[0].Name())
}
}
diff --git a/scanner/scan.go b/scanner/scan.go
index 6ea99a9..2782788 100644
--- a/scanner/scan.go
+++ b/scanner/scan.go
@@ -34,14 +34,13 @@ func (t *Token) Prefix() string { return t.Str[:t.Beg] }
// Name return the name of t (short string for debugging).
func (t *Token) Name() string {
- name := t.Str
- if t.Beg > 1 {
- return name[:t.Beg] + ".."
+ if len(t.Str) == 0 {
+ return ""
}
- if t.Beg > 0 {
- return name[:t.Beg] + ".." + name[len(name)-t.End:]
+ if t.Beg > 1 {
+ return t.Str[:t.Beg] + ".."
}
- return name
+ return t.Str[:t.Beg] + ".." + t.Str[len(t.Str)-t.End:]
}
func (t *Token) String() string {
diff --git a/vm/vm_test.go b/vm/vm_test.go
index 8fa8e71..43333ad 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -3,6 +3,7 @@ package vm
import (
"fmt"
"log"
+ "reflect"
"testing"
)
@@ -61,7 +62,90 @@ var tests = []struct {
{0, Exit},
},
start: 0, end: 1, mem: "[3]",
-}, { // #01 -- Calling a function defined outside the VM.
+}, { // #01 -- A simple subtraction.
+ code: [][]int64{
+ {0, Push, 2},
+ {0, Push, 3},
+ {0, Sub},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[1]",
+}, { // #02 -- A simple multiplication.
+ code: [][]int64{
+ {0, Push, 3},
+ {0, Push, 2},
+ {0, Mul},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[6]",
+}, { // #03 -- lower.
+ code: [][]int64{
+ {0, Push, 3},
+ {0, Push, 2},
+ {0, Lower},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[true]",
+}, { // #04 -- greater.
+ code: [][]int64{
+ {0, Push, 2},
+ {0, Push, 3},
+ {0, Greater},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[true]",
+}, { // #05 -- equal.
+ code: [][]int64{
+ {0, Push, 2},
+ {0, Push, 3},
+ {0, Equal},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[false]",
+}, { // #06 -- equalSet.
+ code: [][]int64{
+ {0, Push, 2},
+ {0, Push, 3},
+ {0, EqualSet},
+ {0, Exit},
+ },
+ start: 0, end: 2, mem: "[2 false]",
+}, { // #07 -- equalSet.
+ code: [][]int64{
+ {0, Push, 3},
+ {0, Push, 3},
+ {0, EqualSet},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[true]",
+}, { // #08 not.
+ code: [][]int64{
+ {0, Push, 3},
+ {0, Push, 3},
+ {0, Equal},
+ {0, Not},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[false]",
+}, { // #09 pop.
+ code: [][]int64{
+ {0, Push, 3},
+ {0, Push, 2},
+ {0, Pop, 1},
+ {0, Exit},
+ },
+ start: 0, end: 1, mem: "[3]",
+}, { // #10 -- Assign a variable.
+ sym: []Value{{Type: TypeOf(0), Data: reflect.ValueOf(0)}},
+ code: [][]int64{
+ {0, Grow, 1},
+ {0, New, 2, 0},
+ {0, Push, 2},
+ {0, Assign, 1},
+ {0, Exit},
+ },
+ start: 1, end: 2, mem: "[2]",
+}, { // #11 -- Calling a function defined outside the VM.
sym: []Value{ValueOf(fmt.Println), ValueOf("Hello")},
code: [][]int64{
{0, Dup, 0},
@@ -69,7 +153,7 @@ var tests = []struct {
{0, Exit},
},
start: 1, end: 3, mem: "[6 <nil>]",
-}, { // #02 -- Defining and calling a function in VM.
+}, { // #12 -- Defining and calling a function in VM.
code: [][]int64{
{0, Jump, 3}, // 0
{0, Push, 3}, // 1
@@ -79,7 +163,7 @@ var tests = []struct {
{0, Exit}, // 5
},
start: 0, end: 1, mem: "[3]",
-}, { // #03 -- Defining and calling a function in VM.
+}, { // #13 -- Defining and calling a function in VM.
code: [][]int64{
{0, Jump, 3}, // 0
{0, Push, 3}, // 1
@@ -90,7 +174,7 @@ var tests = []struct {
{0, Exit}, // 6
},
start: 0, end: 1, mem: "[3]",
-}, { // #04 -- Defining and calling a function in VM.
+}, { // #14 -- Defining and calling a function in VM.
code: [][]int64{
{0, Jump, 5}, // 0
{0, Push, 3}, // 1
@@ -103,20 +187,20 @@ var tests = []struct {
{0, Exit}, // 8
},
start: 0, end: 1, mem: "[3]",
-}, { // #05 -- Fibonacci numbers, hand written. Showcase recursivity.
+}, { // #15 -- Fibonacci numbers, hand written. Showcase recursivity.
code: [][]int64{
{0, Jump, 19}, // 0
{0, Push, 2}, // 2 [2]
{0, Fdup, -2}, // 1 [2 i]
{0, Lower}, // 3 [true/false]
{0, JumpTrue, 13}, // 4 [], goto 17
- {0, Push, 2}, // 6 [i 2]
- {0, Fdup, -2}, // 5 [i]
+ {0, Push, 2}, // 5 [i 2]
+ {0, Fdup, -2}, // 6 [i]
{0, Sub}, // 7 [(i-2)]
{0, Push, 1}, // 8
{0, Call}, // 9 [fib(i-2)]
- {0, Push, 1}, // 11 [(i-2) i 1]
- {0, Fdup, -2}, // 10 [fib(i-2) i]
+ {0, Push, 1}, // 10 [(i-2) i 1]
+ {0, Fdup, -2}, // 11 [fib(i-2) i]
{0, Sub}, // 12 [(i-2) (i-1)]
{0, Push, 1}, // 13
{0, Call}, // 14 [fib(i-2) fib(i-1)]
@@ -130,7 +214,7 @@ var tests = []struct {
{0, Exit}, // 22
},
start: 0, end: 1, mem: "[8]",
-}, { // #06 -- Fibonacci with some immediate instructions.
+}, { // #16 -- Fibonacci with some immediate instructions.
code: [][]int64{
{0, Jump, 14}, // 0
{0, Fdup, -2}, // 1 [i]