var type checking, fix importing, fix creating empty lists, repl command, dynamic stacks
This commit is contained in:
parent
f633a02c32
commit
5bcab07681
11 changed files with 407 additions and 284 deletions
49
cli/main.go
49
cli/main.go
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"log"
|
"log"
|
||||||
|
|
@ -226,11 +227,59 @@ func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReplCmd struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *ReplCmd) Run(ctx *Context) error {
|
||||||
|
c := core.NewCompiler([]rune(""))
|
||||||
|
vm := core.NewVM(core.NewChunk([]core.Bytecode{}, []core.Value{}), 256, 256)
|
||||||
|
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
for {
|
||||||
|
print("> ")
|
||||||
|
src, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := core.NewLexer(src)
|
||||||
|
tokens, err := l.Tokenize()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p := core.NewParser(src, tokens)
|
||||||
|
prog, err := p.Parse("REPL")
|
||||||
|
if err != nil {
|
||||||
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
log.Print(e.Format())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetSource(src)
|
||||||
|
if err = c.Compile(prog); err != nil {
|
||||||
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
log.Print(e.Format())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.SetChunk(c.Chunk)
|
||||||
|
for vm.Next() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Debug bool `short:"D" name:"debug" help:"Enable debug mode."`
|
Debug bool `short:"D" name:"debug" help:"Enable debug mode."`
|
||||||
|
|
||||||
Run RunCmd `cmd:"" name:"run" help:"Run program."`
|
Run RunCmd `cmd:"" name:"run" help:"Run program."`
|
||||||
Compile CompileCmd `cmd:"" name:"compile" help:"Compile program to bytecode."`
|
Compile CompileCmd `cmd:"" name:"compile" help:"Compile program to bytecode."`
|
||||||
|
Repl ReplCmd `cmd:"" name:"repl" help:"Start a REPL loop."`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,6 @@ func (c *Compiler) addConstant(value Value) {
|
||||||
|
|
||||||
func (c *Compiler) Compile(p *Program) error {
|
func (c *Compiler) Compile(p *Program) error {
|
||||||
c.fileStack.Push(p.Path)
|
c.fileStack.Push(p.Path)
|
||||||
defer c.fileStack.Pop()
|
|
||||||
|
|
||||||
for _, i := range p.Imports {
|
for _, i := range p.Imports {
|
||||||
if err := c.resolveImport(i); err != nil {
|
if err := c.resolveImport(i); err != nil {
|
||||||
|
|
@ -171,6 +170,8 @@ func (c *Compiler) Compile(p *Program) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.fileStack.Pop()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,7 +452,8 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
c.registerVar(p.Name, p.Signature)
|
c.registerVar(p.Name, p.Signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.affirmReturnSignature(n.logic, n.yield); err != nil {
|
err = c.affirmReturnSignature(n.logic, n.yield)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -585,7 +587,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
case ListNodeType:
|
case ListNodeType:
|
||||||
n := tree.(*ListNode)
|
n := tree.(*ListNode)
|
||||||
|
|
||||||
var contents TypeSignature
|
contents := n.content
|
||||||
// check for contents type
|
// check for contents type
|
||||||
for _, v := range n.items {
|
for _, v := range n.items {
|
||||||
sig, err := c.deduceSignature(v)
|
sig, err := c.deduceSignature(v)
|
||||||
|
|
@ -596,8 +598,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
if contents == nil {
|
if contents == nil {
|
||||||
contents = sig
|
contents = sig
|
||||||
} else if !contents.Matches(sig) {
|
} else if !contents.Matches(sig) {
|
||||||
contents = &AnySignature{}
|
return nil, c.error(fmt.Sprintf("non-conforming item type"), v)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -804,7 +805,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sig.Matches(v) {
|
if !sig.Matches(v) {
|
||||||
return c.error(fmt.Sprintf("function cannot return a value with type %s. defined to be %s", v, sig), n.value)
|
return c.error(fmt.Sprintf("function cannot return a value with type %s. defined to be %s", v, sig), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ConditionalNodeType:
|
case ConditionalNodeType:
|
||||||
|
|
@ -842,7 +843,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sig.Matches(prev) {
|
if !sig.Matches(prev) {
|
||||||
return c.error(fmt.Sprintf("cannot assign value of type %s to variable of type %s", prev, sig), n.value)
|
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, n.name, prev), n.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -891,14 +892,24 @@ func (c *Compiler) addSetVar(name string, value Node, declare bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t, err := c.deduceSignature(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if declare {
|
if declare {
|
||||||
c.add(InstructionDeclareLocal)
|
c.add(InstructionDeclareLocal)
|
||||||
t, err := c.deduceSignature(value)
|
c.registerVar(name, t)
|
||||||
|
} else {
|
||||||
|
vt, err := c.getVarSignature(name, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.registerVar(name, t)
|
|
||||||
} else {
|
if !vt.Matches(t) {
|
||||||
|
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", t, name, vt), value)
|
||||||
|
}
|
||||||
|
|
||||||
c.add(InstructionSetLocal)
|
c.add(InstructionSetLocal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1090,10 +1101,7 @@ func (c *Compiler) ascend() {
|
||||||
|
|
||||||
func (c *Compiler) addAscend() {
|
func (c *Compiler) addAscend() {
|
||||||
c.ascend()
|
c.ascend()
|
||||||
|
c.add(InstructionAscend)
|
||||||
if c.scope != 0 {
|
|
||||||
c.add(InstructionAscend)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) descend() {
|
func (c *Compiler) descend() {
|
||||||
|
|
@ -1102,9 +1110,7 @@ func (c *Compiler) descend() {
|
||||||
|
|
||||||
func (c *Compiler) addDescend() {
|
func (c *Compiler) addDescend() {
|
||||||
c.descend()
|
c.descend()
|
||||||
if c.scope != 1 {
|
c.add(InstructionDescend)
|
||||||
c.add(InstructionDescend)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) error(msg string, causer Node) CompilerError {
|
func (c *Compiler) error(msg string, causer Node) CompilerError {
|
||||||
|
|
@ -1169,6 +1175,10 @@ func (c *Compiler) SetImportsResolver(resolver ImportsResolver) {
|
||||||
c.resolver = resolver
|
c.resolver = resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) SetSource(src string) {
|
||||||
|
c.source = []rune(src)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) advance(amount Pos) {
|
func (c *Compiler) advance(amount Pos) {
|
||||||
c.ip += amount
|
c.ip += amount
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,58 +28,80 @@ func BenchmarkNewCompiler(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompileTestData struct {
|
type CompileTestData struct {
|
||||||
tree Node
|
program *Program
|
||||||
expectedStack []Value
|
expectedStack []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCompileTestData() map[string]CompileTestData {
|
func GetCompileTestData() map[string]CompileTestData {
|
||||||
return map[string]CompileTestData{
|
return map[string]CompileTestData{
|
||||||
"constant_string": {
|
"constant_string": {
|
||||||
&StringNode{
|
&Program{
|
||||||
"Hello world!",
|
[]string{},
|
||||||
"\"Hello world!\"",
|
&BlockNode{
|
||||||
0, 0,
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&StringNode{
|
||||||
|
"Hello world!",
|
||||||
|
"\"Hello world!\"",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&StringValue{"Hello world!"},
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&StringValue{"Hello world!"},
|
||||||
|
0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_false": {
|
"conditional_false": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"a",
|
[]Node{
|
||||||
&NumberNode{
|
&AssignNode{
|
||||||
0,
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
0,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
&ConditionalNode{
|
||||||
0, 0,
|
&BooleanNode{
|
||||||
},
|
false,
|
||||||
&ConditionalNode{
|
0, 0,
|
||||||
&BooleanNode{
|
},
|
||||||
false,
|
&BlockNode{
|
||||||
0, 0,
|
[]Node{
|
||||||
},
|
&AssignNode{
|
||||||
&BlockNode{
|
"a",
|
||||||
[]Node{
|
&NumberNode{
|
||||||
&AssignNode{
|
1,
|
||||||
"a",
|
0, 0,
|
||||||
&NumberNode{
|
},
|
||||||
1,
|
false,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -90,41 +112,45 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_true": {
|
"conditional_true": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"a",
|
[]Node{
|
||||||
&NumberNode{
|
&AssignNode{
|
||||||
0,
|
"a",
|
||||||
0, 0,
|
&NumberNode{
|
||||||
},
|
0,
|
||||||
true,
|
0, 0,
|
||||||
0, 0,
|
},
|
||||||
},
|
|
||||||
&ConditionalNode{
|
|
||||||
&BooleanNode{
|
|
||||||
true,
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&ConditionalNode{
|
||||||
[]Node{
|
&BooleanNode{
|
||||||
&AssignNode{
|
true,
|
||||||
"a",
|
0, 0,
|
||||||
&NumberNode{
|
},
|
||||||
1,
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -135,54 +161,58 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_else_false": {
|
"conditional_else_false": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"a",
|
[]Node{
|
||||||
&NumberNode{
|
&AssignNode{
|
||||||
0,
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
0,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
&ConditionalNode{
|
||||||
0, 0,
|
&BooleanNode{
|
||||||
},
|
false,
|
||||||
&ConditionalNode{
|
0, 0,
|
||||||
&BooleanNode{
|
},
|
||||||
false,
|
&BlockNode{
|
||||||
0, 0,
|
[]Node{
|
||||||
},
|
&AssignNode{
|
||||||
&BlockNode{
|
"a",
|
||||||
[]Node{
|
&NumberNode{
|
||||||
&AssignNode{
|
1,
|
||||||
"a",
|
0, 0,
|
||||||
&NumberNode{
|
},
|
||||||
1,
|
false,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
|
||||||
[]Node{
|
|
||||||
&AssignNode{
|
|
||||||
"a",
|
|
||||||
&NumberNode{
|
|
||||||
2,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -193,54 +223,58 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_else_true": {
|
"conditional_else_true": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"a",
|
[]Node{
|
||||||
&NumberNode{
|
&AssignNode{
|
||||||
0,
|
"a",
|
||||||
0, 0,
|
&NumberNode{
|
||||||
},
|
0,
|
||||||
true,
|
0, 0,
|
||||||
0, 0,
|
},
|
||||||
},
|
|
||||||
&ConditionalNode{
|
|
||||||
&BooleanNode{
|
|
||||||
true,
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&ConditionalNode{
|
||||||
[]Node{
|
&BooleanNode{
|
||||||
&AssignNode{
|
true,
|
||||||
"a",
|
0, 0,
|
||||||
&NumberNode{
|
},
|
||||||
1,
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
|
||||||
[]Node{
|
|
||||||
&AssignNode{
|
|
||||||
"a",
|
|
||||||
&NumberNode{
|
|
||||||
2,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -251,67 +285,89 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"addition": {
|
"addition": {
|
||||||
&BinaryNode{
|
&Program{
|
||||||
BinaryAddition,
|
[]string{},
|
||||||
&NumberNode{
|
&BlockNode{
|
||||||
1,
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
|
&BinaryNode{
|
||||||
|
BinaryAddition,
|
||||||
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
"",
|
||||||
2,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&NumberValue{3},
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&NumberValue{3},
|
||||||
|
0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sum_function": {
|
"sum_function": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"sum",
|
[]Node{
|
||||||
&FunctionNode{
|
&AssignNode{
|
||||||
"sum",
|
"sum",
|
||||||
[]FunctionParameter{
|
&FunctionNode{
|
||||||
{
|
"sum",
|
||||||
"a",
|
[]FunctionParameter{
|
||||||
&NumberSignature{},
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
&NumberSignature{},
|
||||||
"b",
|
&BlockNode{
|
||||||
&NumberSignature{},
|
[]Node{
|
||||||
},
|
&ReturnNode{
|
||||||
},
|
&BinaryNode{
|
||||||
&NumberSignature{},
|
BinaryAddition,
|
||||||
&BlockNode{
|
&ReferenceNode{
|
||||||
[]Node{
|
"a",
|
||||||
&ReturnNode{
|
0, 0,
|
||||||
&BinaryNode{
|
},
|
||||||
BinaryAddition,
|
&ReferenceNode{
|
||||||
&ReferenceNode{
|
"b",
|
||||||
"a",
|
0, 0,
|
||||||
0, 0,
|
},
|
||||||
},
|
|
||||||
&ReferenceNode{
|
|
||||||
"b",
|
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -350,51 +406,55 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"remove_func_vars": {
|
"remove_func_vars": {
|
||||||
&BlockNode{
|
&Program{
|
||||||
[]Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"a",
|
[]Node{
|
||||||
&FunctionNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
[]FunctionParameter{},
|
&FunctionNode{
|
||||||
&NumberSignature{},
|
"a",
|
||||||
&BlockNode{
|
[]FunctionParameter{},
|
||||||
[]Node{
|
&NumberSignature{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
"b",
|
[]Node{
|
||||||
&NumberNode{
|
&AssignNode{
|
||||||
1,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
0, 0,
|
|
||||||
},
|
|
||||||
&ReturnNode{
|
|
||||||
&ReferenceNode{
|
|
||||||
"b",
|
"b",
|
||||||
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
&ReturnNode{
|
||||||
|
&ReferenceNode{
|
||||||
|
"b",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
&CallNode{
|
||||||
0, 0,
|
&ReferenceNode{
|
||||||
},
|
"a",
|
||||||
&CallNode{
|
0, 0,
|
||||||
&ReferenceNode{
|
},
|
||||||
"a",
|
[]Node{},
|
||||||
|
false,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
[]Node{},
|
|
||||||
false,
|
|
||||||
0, 0,
|
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
0, 0,
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -423,29 +483,33 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"two_lists": {
|
"two_lists": {
|
||||||
tree: &BlockNode{
|
program: &Program{
|
||||||
statements: []Node{
|
[]string{},
|
||||||
&AssignNode{
|
&BlockNode{
|
||||||
name: "a",
|
statements: []Node{
|
||||||
value: &ListNode{
|
&AssignNode{
|
||||||
items: []Node{
|
name: "a",
|
||||||
&NumberNode{value: 1},
|
value: &ListNode{
|
||||||
&NumberNode{value: 2},
|
items: []Node{
|
||||||
|
&NumberNode{value: 1},
|
||||||
|
&NumberNode{value: 2},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
declare: true,
|
||||||
},
|
},
|
||||||
declare: true,
|
&AssignNode{
|
||||||
},
|
name: "b",
|
||||||
&AssignNode{
|
value: &ListNode{
|
||||||
name: "b",
|
items: []Node{
|
||||||
value: &ListNode{
|
&StringNode{value: "Hello"},
|
||||||
items: []Node{
|
&StringNode{value: "world"},
|
||||||
&StringNode{value: "Hello"},
|
},
|
||||||
&StringNode{value: "world"},
|
|
||||||
},
|
},
|
||||||
|
declare: true,
|
||||||
},
|
},
|
||||||
declare: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
expectedStack: []Value{
|
expectedStack: []Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -499,10 +563,10 @@ func TestCompile(t *testing.T) {
|
||||||
for name, testCase := range data {
|
for name, testCase := range data {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Log("Initializing compiler")
|
t.Log("Initializing compiler")
|
||||||
c := NewCompiler([]rune(testCase.tree.String()))
|
c := NewCompiler([]rune(testCase.program.String()))
|
||||||
|
|
||||||
t.Log("Compiling node tree")
|
t.Log("Compiling node tree")
|
||||||
err := c.compile(testCase.tree)
|
err := c.Compile(testCase.program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -529,7 +593,7 @@ func BenchmarkCompile(b *testing.B) {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
c := NewCompiler([]rune{})
|
c := NewCompiler([]rune{})
|
||||||
_ = c.compile(testCase.tree)
|
_ = c.Compile(testCase.program)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -556,24 +620,9 @@ func TestCompiler_CleanStack(t *testing.T) {
|
||||||
cases := GetCompileTestData()
|
cases := GetCompileTestData()
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
switch tc.tree.Type() {
|
|
||||||
// skip all expected unclean nodes
|
|
||||||
case StringNodeType, NumberNodeType, ReferenceNodeType, BooleanNodeType, NilNodeType, BinaryNodeType, ReturnNodeType:
|
|
||||||
continue
|
|
||||||
|
|
||||||
case CallNodeType:
|
|
||||||
if tc.tree.(*CallNode).keep {
|
|
||||||
// if we know it should be unclean, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean statements
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
c := NewCompiler([]rune(tc.tree.String()))
|
c := NewCompiler([]rune(tc.program.String()))
|
||||||
err := c.compile(tc.tree)
|
err := c.Compile(tc.program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,8 @@ func (n NumberNode) Bounds() (Pos, Pos) {
|
||||||
|
|
||||||
// ListNode a list or sequence of values (items)
|
// ListNode a list or sequence of values (items)
|
||||||
type ListNode struct {
|
type ListNode struct {
|
||||||
items []Node
|
items []Node
|
||||||
|
content TypeSignature
|
||||||
|
|
||||||
start Pos
|
start Pos
|
||||||
end Pos
|
end Pos
|
||||||
|
|
|
||||||
|
|
@ -253,9 +253,21 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
case TokenOpenBracket:
|
case TokenOpenBracket:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
start := p.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
|
if p.accept(TokenCloseBracket) {
|
||||||
|
s, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ListNode{
|
||||||
|
[]Node{},
|
||||||
|
s,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
var values []Node
|
var values []Node
|
||||||
for !p.accept(TokenCloseBracket) {
|
for !p.accept(TokenCloseBracket) {
|
||||||
if len(values) > 0 {
|
if len(values) > 0 {
|
||||||
|
|
@ -275,6 +287,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
return &ListNode{
|
return &ListNode{
|
||||||
values,
|
values,
|
||||||
|
nil,
|
||||||
start,
|
start,
|
||||||
p.prev.Start + p.prev.Length,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -386,9 +399,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err := p.error("invalid factor", p.curr)
|
return nil, p.error("invalid factor", p.curr)
|
||||||
p.advance()
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -688,10 +699,10 @@ func (p *Parser) statement() (Node, error) {
|
||||||
start,
|
start,
|
||||||
p.prev.Start + p.prev.Length,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
|
||||||
return p.condition()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, p.error("invalid statement", p.curr)
|
||||||
|
|
||||||
case TokenFunc:
|
case TokenFunc:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -630,9 +630,11 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
0, 0,
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,28 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
type Stack[T any] struct {
|
type Stack[T any] struct {
|
||||||
Current Pos
|
Current Pos
|
||||||
Size Pos
|
Capacity Pos
|
||||||
|
|
||||||
items []T
|
items []T
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStack[T any](size Pos) *Stack[T] {
|
func NewStack[T any](capacity Pos) *Stack[T] {
|
||||||
return &Stack[T]{
|
return &Stack[T]{
|
||||||
items: make([]T, size),
|
items: make([]T, 16),
|
||||||
Size: size,
|
Capacity: capacity,
|
||||||
Current: 0,
|
Current: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack[T]) Push(items ...T) {
|
func (s *Stack[T]) Push(items ...T) {
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if s.Current >= s.Size {
|
if s.Current >= s.Capacity {
|
||||||
panic("stack overflow")
|
panic("stack overflow")
|
||||||
}
|
}
|
||||||
|
if int(s.Current) == len(s.items) {
|
||||||
|
s.items = append(s.items, item)
|
||||||
|
}
|
||||||
|
|
||||||
s.items[s.Current] = item
|
s.items[s.Current] = item
|
||||||
s.Current++
|
s.Current++
|
||||||
|
|
@ -45,7 +48,7 @@ func (s *Stack[T]) Peek() T {
|
||||||
|
|
||||||
// check whether the stack is invalid (stack over-/underflow)
|
// check whether the stack is invalid (stack over-/underflow)
|
||||||
func (s *Stack[T]) check() {
|
func (s *Stack[T]) check() {
|
||||||
if s.Current >= s.Size {
|
if s.Current >= s.Capacity {
|
||||||
panic("stack underflow")
|
panic("stack underflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,10 @@ func TestNewStack(t *testing.T) {
|
||||||
|
|
||||||
s := NewStack[any](Pos(size))
|
s := NewStack[any](Pos(size))
|
||||||
|
|
||||||
if s.Size != Pos(size) {
|
if s.Capacity != Pos(size) {
|
||||||
t.Errorf("Stack size (%d) does not match expected size (%d)", s.Size, size)
|
t.Errorf("Stack size (%d) does not match expected size (%d)", s.Capacity, size)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Stack size is expected size (%d)", s.Size)
|
t.Logf("Stack size is expected size (%d)", s.Capacity)
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.items) != size {
|
|
||||||
t.Errorf("internal items slice size (%d) does not match expected size (%d)", len(s.items), size)
|
|
||||||
} else {
|
|
||||||
t.Logf("internal items slice size is as expected (%d)", len(s.items))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Current != 0 {
|
if s.Current != 0 {
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ func (v *ObjectValue) String() string {
|
||||||
out += ", "
|
out += ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
out += fmt.Sprintf("%q=%s", key, value.String())
|
out += fmt.Sprintf("%q=%s", key, value.DebugString())
|
||||||
}
|
}
|
||||||
out += "}"
|
out += "}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -826,6 +826,10 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
return nil, errors.New(fmt.Sprintf("value is not a function (%s)", v.DebugString()))
|
return nil, errors.New(fmt.Sprintf("value is not a function (%s)", v.DebugString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *VM) SetChunk(c *Chunk) {
|
||||||
|
vm.chunk = c
|
||||||
|
}
|
||||||
|
|
||||||
func (vm *VM) TryNextByte() (Bytecode, error) {
|
func (vm *VM) TryNextByte() (Bytecode, error) {
|
||||||
if !vm.HasNext() {
|
if !vm.HasNext() {
|
||||||
return 0, errors.New("there are no more instructions")
|
return 0, errors.New("there are no more instructions")
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,13 @@ func TestNewVM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// should have given stack size
|
// should have given stack size
|
||||||
if vm.stack.Size != stackSize {
|
if vm.stack.Capacity != stackSize {
|
||||||
t.Errorf("vm.stack.Size = %d, want %d", vm.stack.Size, stackSize)
|
t.Errorf("vm.stack.Capacity = %d, want %d", vm.stack.Capacity, stackSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// should have given call stack size
|
// should have given call stack size
|
||||||
if vm.call.Size != callstackSize {
|
if vm.call.Capacity != callstackSize {
|
||||||
t.Errorf("vm.call.Size = %d, want %d", vm.call.Size, callstackSize)
|
t.Errorf("vm.call.Capacity = %d, want %d", vm.call.Capacity, callstackSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue