Fix values
This commit is contained in:
parent
ae7d6c5359
commit
5edad332cd
19 changed files with 611 additions and 331 deletions
|
|
@ -147,7 +147,7 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
log.Println("=v= output =v=")
|
log.Println("=v= output =v=")
|
||||||
}
|
}
|
||||||
// execute order 66
|
// execute order 66
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -26,7 +26,7 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"sum",
|
"sum",
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []string{"a", "b"},
|
||||||
Chunk: &Chunk{
|
Chunk: &Chunk{
|
||||||
|
|
@ -38,7 +38,7 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
InstructionReturn,
|
InstructionReturn,
|
||||||
InstructionAscend,
|
InstructionAscend,
|
||||||
},
|
},
|
||||||
Constants: []Value{StringValue("a"), StringValue("b")},
|
Constants: []Value{&StringValue{"a"}, &StringValue{"b"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
|
|
@ -86,7 +86,7 @@ func TestAll(t *testing.T) {
|
||||||
vm := NewVM(c.Chunk, 256, 256)
|
vm := NewVM(c.Chunk, 256, 256)
|
||||||
|
|
||||||
t.Log("Running bytecode")
|
t.Log("Running bytecode")
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Comparing stacks")
|
t.Log("Comparing stacks")
|
||||||
|
|
@ -112,7 +112,7 @@ func BenchmarkAll(b *testing.B) {
|
||||||
|
|
||||||
vm := NewVM(c.Chunk, 256, 256)
|
vm := NewVM(c.Chunk, 256, 256)
|
||||||
|
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
217
core/compiler.go
217
core/compiler.go
|
|
@ -1,5 +1,9 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
Chunk *Chunk
|
Chunk *Chunk
|
||||||
ip Pos
|
ip Pos
|
||||||
|
|
@ -45,7 +49,7 @@ func (c *Compiler) add(instruction Bytecode) {
|
||||||
func (c *Compiler) addConstant(value Value) {
|
func (c *Compiler) addConstant(value Value) {
|
||||||
chunk := c.Chunk
|
chunk := c.Chunk
|
||||||
for i := 0; i < len(chunk.Constants); i++ {
|
for i := 0; i < len(chunk.Constants); i++ {
|
||||||
if chunk.Constants[i] == value {
|
if chunk.Constants[i].Equals(value) {
|
||||||
c.add(Bytecode(i))
|
c.add(Bytecode(i))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -57,26 +61,41 @@ func (c *Compiler) addConstant(value Value) {
|
||||||
c.add(Bytecode(len(chunk.Constants) - 1))
|
c.add(Bytecode(len(chunk.Constants) - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) Compile(tree Node) {
|
func (c *Compiler) Compile(tree Node) error {
|
||||||
if tree == nil {
|
if tree == nil {
|
||||||
panic("nil value parse tree node")
|
panic("compile called with nil value")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch tree.Type() {
|
switch tree.Type() {
|
||||||
case StringNodeType:
|
case StringNodeType:
|
||||||
c.add(InstructionConstant)
|
c.add(InstructionConstant)
|
||||||
c.addConstant(StringValue(tree.(*StringNode).value))
|
c.addConstant(&StringValue{
|
||||||
|
tree.(*StringNode).value,
|
||||||
|
})
|
||||||
|
|
||||||
case NumberNodeType:
|
case NumberNodeType:
|
||||||
c.add(InstructionConstant)
|
c.add(InstructionConstant)
|
||||||
c.addConstant(tree.(*NumberNode).value)
|
c.addConstant(&NumberValue{tree.(*NumberNode).value})
|
||||||
|
|
||||||
case ListNodeType:
|
case ListNodeType:
|
||||||
v := tree.(*ListNode).items
|
l := tree.(*ListNode)
|
||||||
|
|
||||||
|
if len(l.items) == 0 {
|
||||||
c.add(InstructionNewList)
|
c.add(InstructionNewList)
|
||||||
for _, n := range v {
|
} else if c.isTreeConstant(l) {
|
||||||
|
v, err := c.compute(l)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // this shouldn't happen
|
||||||
|
}
|
||||||
|
|
||||||
|
c.add(InstructionConstant)
|
||||||
|
c.addConstant(v)
|
||||||
|
} else {
|
||||||
|
for _, n := range l.items {
|
||||||
c.Compile(n)
|
c.Compile(n)
|
||||||
c.add(InstructionAppend)
|
}
|
||||||
|
c.add(InstructionFormList)
|
||||||
|
c.addU16(uint16(len(l.items)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case ReferenceNodeType:
|
case ReferenceNodeType:
|
||||||
|
|
@ -201,12 +220,17 @@ func (c *Compiler) Compile(tree Node) {
|
||||||
for _, p := range n.params {
|
for _, p := range n.params {
|
||||||
c.registerVar(p)
|
c.registerVar(p)
|
||||||
}
|
}
|
||||||
c.Compile(n.logic)
|
|
||||||
|
err := c.Compile(n.logic)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if n.logic.Type() != BlockNodeType {
|
if n.logic.Type() != BlockNodeType {
|
||||||
c.stack.Pop()
|
c.stack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
mc.Constants[fi] = FunctionValue{
|
mc.Constants[fi] = &FunctionValue{
|
||||||
n.name,
|
n.name,
|
||||||
n.params,
|
n.params,
|
||||||
c.Chunk,
|
c.Chunk,
|
||||||
|
|
@ -219,9 +243,14 @@ func (c *Compiler) Compile(tree Node) {
|
||||||
|
|
||||||
case AccessNodeType:
|
case AccessNodeType:
|
||||||
n := tree.(*AccessNode)
|
n := tree.(*AccessNode)
|
||||||
c.Compile(n.source)
|
err := c.Compile(n.source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.add(InstructionAccessProperty)
|
c.add(InstructionAccessProperty)
|
||||||
c.addConstant(StringValue(n.property))
|
c.addConstant(&StringValue{
|
||||||
|
n.property,
|
||||||
|
})
|
||||||
|
|
||||||
case ImportNodeType:
|
case ImportNodeType:
|
||||||
n := tree.(*ImportNode)
|
n := tree.(*ImportNode)
|
||||||
|
|
@ -229,21 +258,46 @@ func (c *Compiler) Compile(tree Node) {
|
||||||
t := c.resolveImport(n.path).(*BlockNode)
|
t := c.resolveImport(n.path).(*BlockNode)
|
||||||
|
|
||||||
for _, statement := range t.statements {
|
for _, statement := range t.statements {
|
||||||
c.Compile(statement)
|
err := c.Compile(statement)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case ReturnNodeType:
|
case ReturnNodeType:
|
||||||
c.Compile(tree.(*ReturnNode).value)
|
err := c.Compile(tree.(*ReturnNode).value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.add(InstructionReturn)
|
c.add(InstructionReturn)
|
||||||
|
|
||||||
case BreakpointNodeType:
|
case BreakpointNodeType:
|
||||||
c.add(InstructionBreakpoint)
|
c.add(InstructionBreakpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) compileBinary(binary *BinaryNode) {
|
func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||||
c.Compile(binary.Left)
|
if c.isTreeConstant(binary) {
|
||||||
c.Compile(binary.Right)
|
v, err := c.compute(binary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.add(InstructionConstant)
|
||||||
|
c.addConstant(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.Compile(binary.Left)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.Compile(binary.Right)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch binary.BinaryOperation {
|
switch binary.BinaryOperation {
|
||||||
case BinaryAddition:
|
case BinaryAddition:
|
||||||
|
|
@ -271,20 +325,29 @@ func (c *Compiler) compileBinary(binary *BinaryNode) {
|
||||||
case BinaryOr:
|
case BinaryOr:
|
||||||
c.add(InstructionOr)
|
c.add(InstructionOr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) getVar(name string) {
|
func (c *Compiler) getVar(name string) {
|
||||||
if c.isGlobal(name) {
|
if c.isGlobal(name) {
|
||||||
c.add(InstructionGetGlobal)
|
c.add(InstructionGetGlobal)
|
||||||
c.addConstant(StringValue(name))
|
c.addConstant(&StringValue{
|
||||||
|
name,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
c.add(InstructionGetLocal)
|
c.add(InstructionGetLocal)
|
||||||
c.addConstant(StringValue(name))
|
c.addConstant(&StringValue{
|
||||||
|
name,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) setVar(name string, value Node, declare bool) {
|
func (c *Compiler) setVar(name string, value Node, declare bool) error {
|
||||||
c.Compile(value)
|
err := c.Compile(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if declare {
|
if declare {
|
||||||
c.add(InstructionDeclareLocal)
|
c.add(InstructionDeclareLocal)
|
||||||
|
|
@ -293,7 +356,11 @@ func (c *Compiler) setVar(name string, value Node, declare bool) {
|
||||||
c.add(InstructionSetLocal)
|
c.add(InstructionSetLocal)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addConstant(StringValue(name))
|
c.addConstant(&StringValue{
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep track that a variable is declared but doesn't necessarily have a deducible type
|
// keep track that a variable is declared but doesn't necessarily have a deducible type
|
||||||
|
|
@ -314,6 +381,112 @@ func (c *Compiler) isLocal(name string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTreeConstant check if a node tree is constant (predictable)
|
||||||
|
func (c *Compiler) isTreeConstant(tree Node) bool {
|
||||||
|
switch tree.Type() {
|
||||||
|
case StringNodeType, NumberNodeType, BooleanNodeType, NilNodeType:
|
||||||
|
return true
|
||||||
|
case ListNodeType:
|
||||||
|
for _, item := range tree.(*ListNode).items {
|
||||||
|
if !c.isTreeConstant(item) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
case BinaryNodeType:
|
||||||
|
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
||||||
|
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, CallNodeType, FunctionNodeType,
|
||||||
|
ReturnNodeType, AccessNodeType, BreakpointNodeType, ImportNodeType, ReferenceNodeType:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s", tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compute(tree Node) (Value, error) {
|
||||||
|
switch n := tree.(type) {
|
||||||
|
case *StringNode:
|
||||||
|
return &StringValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *NumberNode:
|
||||||
|
return &NumberValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BooleanNode:
|
||||||
|
return &BoolValue{
|
||||||
|
n.value,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *NilNode:
|
||||||
|
return &NilValue{}, nil
|
||||||
|
|
||||||
|
case *ListNode:
|
||||||
|
items := make([]Value, len(n.items))
|
||||||
|
var err error
|
||||||
|
for i, item := range n.items {
|
||||||
|
items[i], err = c.compute(item)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ListValue{
|
||||||
|
items,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case *BinaryNode:
|
||||||
|
return c.computeBinary(n)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected node %s, %T", tree.String(), tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
|
l, err := c.compute(n.Left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r, err := c.compute(n.Right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var v interface{}
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinaryAddition:
|
||||||
|
v = l.(*NumberValue).float64 + r.(*NumberValue).float64
|
||||||
|
case BinarySubtraction:
|
||||||
|
v = l.(*NumberValue).float64 - r.(*NumberValue).float64
|
||||||
|
case BinaryMultiplication:
|
||||||
|
v = l.(*NumberValue).float64 * r.(*NumberValue).float64
|
||||||
|
case BinaryDivision:
|
||||||
|
v = l.(*NumberValue).float64 / r.(*NumberValue).float64
|
||||||
|
case BinaryAnd:
|
||||||
|
v = l.(*BoolValue).bool && r.(*BoolValue).bool
|
||||||
|
case BinaryOr:
|
||||||
|
v = l.(*BoolValue).bool && r.(*BoolValue).bool
|
||||||
|
case BinaryEquality:
|
||||||
|
v = l.Equals(r)
|
||||||
|
case BinaryInequality:
|
||||||
|
v = !l.Equals(r.(*BoolValue))
|
||||||
|
case BinaryLess:
|
||||||
|
v = l.(*NumberValue).float64 < r.(*NumberValue).float64
|
||||||
|
case BinaryGreater:
|
||||||
|
v = l.(*NumberValue).float64 > r.(*NumberValue).float64
|
||||||
|
case BinaryLessEqual:
|
||||||
|
v = l.(*NumberValue).float64 <= r.(*NumberValue).float64
|
||||||
|
case BinaryGreaterEqual:
|
||||||
|
v = l.(*NumberValue).float64 >= r.(*NumberValue).float64
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoToValue(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
// isGlobal whether a variable is defined in the standard global environment
|
// isGlobal whether a variable is defined in the standard global environment
|
||||||
func (c *Compiler) isGlobal(name string) bool {
|
func (c *Compiler) isGlobal(name string) bool {
|
||||||
return DefaultGlobals[name] != nil
|
return DefaultGlobals[name] != nil
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("Hello world!"),
|
&StringValue{"Hello world!"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_false": {
|
"conditional_false": {
|
||||||
|
|
@ -75,7 +75,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -112,7 +112,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -159,7 +159,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(2),
|
&NumberValue{2},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -206,7 +206,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -222,7 +222,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(3),
|
&NumberValue{3},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sum_function": {&BlockNode{
|
"sum_function": {&BlockNode{
|
||||||
|
|
@ -251,7 +251,8 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"sum",
|
"sum",
|
||||||
FunctionValue{
|
|
||||||
|
&FunctionValue{
|
||||||
"sum",
|
"sum",
|
||||||
[]string{"a", "b"},
|
[]string{"a", "b"},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
|
|
@ -264,7 +265,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
InstructionAscend,
|
InstructionAscend,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("a"), StringValue("b"),
|
&StringValue{"a"}, &StringValue{"b"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
nil,
|
nil,
|
||||||
|
|
@ -308,7 +309,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
"a",
|
"a",
|
||||||
[]string{},
|
[]string{},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
|
|
@ -321,7 +322,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
InstructionAscend,
|
InstructionAscend,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1), StringValue("b"),
|
&NumberValue{1}, &StringValue{"b"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
nil,
|
nil,
|
||||||
|
|
@ -344,7 +345,7 @@ func printChunk(t *testing.T, name string, chunk *Chunk) {
|
||||||
for i, ct := range chunk.Constants {
|
for i, ct := range chunk.Constants {
|
||||||
t.Logf("c=%d \t%s", i, ct)
|
t.Logf("c=%d \t%s", i, ct)
|
||||||
|
|
||||||
f, ok := ct.(FunctionValue)
|
f, ok := ct.(*FunctionValue)
|
||||||
if ok {
|
if ok {
|
||||||
printChunk(t, f.Name, f.Chunk)
|
printChunk(t, f.Name, f.Chunk)
|
||||||
}
|
}
|
||||||
|
|
@ -371,7 +372,7 @@ func TestCompile(t *testing.T) {
|
||||||
printChunk(t, name, c.Chunk)
|
printChunk(t, name, c.Chunk)
|
||||||
|
|
||||||
t.Log("Executing bytecode")
|
t.Log("Executing bytecode")
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
t.Log("Executed bytecode")
|
t.Log("Executed bytecode")
|
||||||
|
|
||||||
|
|
@ -437,7 +438,7 @@ func TestCompiler_CleanStack(t *testing.T) {
|
||||||
c.Compile(tc.tree)
|
c.Compile(tc.tree)
|
||||||
|
|
||||||
vm := NewVM(c.Chunk, 256, 256)
|
vm := NewVM(c.Chunk, 256, 256)
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure stack has only assigned values
|
// make sure stack has only assigned values
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,8 @@ func (t TokenType) String() string {
|
||||||
return "open bracket"
|
return "open bracket"
|
||||||
case TokenCloseBracket:
|
case TokenCloseBracket:
|
||||||
return "close bracket"
|
return "close bracket"
|
||||||
|
case TokenImport:
|
||||||
|
return "import"
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ func (n StringNode) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type NumberNode struct {
|
type NumberNode struct {
|
||||||
value NumberValue
|
value float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NumberNode) Type() NodeType {
|
func (n NumberNode) Type() NodeType {
|
||||||
|
|
@ -109,7 +109,7 @@ func (n NumberNode) Type() NodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NumberNode) String() string {
|
func (n NumberNode) String() string {
|
||||||
return strconv.FormatFloat(float64(n.value), 'g', -1, NumberSize)
|
return strconv.FormatFloat(n.value, 'g', -1, NumberSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListNode a list or sequence of values (items)
|
// ListNode a list or sequence of values (items)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &NumberNode{
|
return &NumberNode{
|
||||||
NumberValue(num),
|
num,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenTrue:
|
case TokenTrue:
|
||||||
|
|
@ -209,7 +209,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
}
|
}
|
||||||
return &BinaryNode{
|
return &BinaryNode{
|
||||||
BinarySubtraction,
|
BinarySubtraction,
|
||||||
&NumberNode{NumberValue(0)},
|
&NumberNode{0},
|
||||||
f,
|
f,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,10 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
value: NumberValue(1),
|
1,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
value: NumberValue(2),
|
2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
|
@ -170,22 +170,22 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&NumberNode{2},
|
&NumberNode{2},
|
||||||
&NumberNode{1},
|
&NumberNode{1},
|
||||||
},
|
},
|
||||||
&NumberNode{NumberValue(5)},
|
&NumberNode{5},
|
||||||
},
|
},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryDivision,
|
BinaryDivision,
|
||||||
&NumberNode{NumberValue(3)},
|
&NumberNode{3},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinarySubtraction,
|
BinarySubtraction,
|
||||||
&NumberNode{NumberValue(6)},
|
&NumberNode{6},
|
||||||
&NumberNode{NumberValue(2)},
|
&NumberNode{2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryDivision,
|
BinaryDivision,
|
||||||
&NumberNode{NumberValue(10)},
|
&NumberNode{10},
|
||||||
&NumberNode{NumberValue(2)},
|
&NumberNode{2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
|
@ -242,7 +242,7 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"a",
|
"a",
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
NumberValue(0),
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
do: &BlockNode{
|
do: &BlockNode{
|
||||||
|
|
@ -250,7 +250,7 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
NumberValue(1),
|
1,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
@ -288,7 +288,7 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"a",
|
"a",
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
NumberValue(0),
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
do: &BlockNode{
|
do: &BlockNode{
|
||||||
|
|
@ -296,7 +296,7 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
NumberValue(1),
|
1,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
@ -307,7 +307,7 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
NumberValue(0),
|
0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
234
core/values.go
234
core/values.go
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -48,24 +47,34 @@ func (v ValueType) String() string {
|
||||||
return "undefined"
|
return "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoToValue convert go values to anglais VM-values. Works for some values (nil, bool, float64, string, slices, maps)
|
// GoToValue convert go values to anglais VM-values. Works for some values (nil, bool, float64, int, string, slices, maps)
|
||||||
func GoToValue(gov interface{}) Value {
|
func GoToValue(gov interface{}) Value {
|
||||||
switch v := gov.(type) {
|
switch v := gov.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return NilValue{}
|
return &NilValue{}
|
||||||
case bool:
|
case bool:
|
||||||
return BoolValue(v)
|
return &BoolValue{
|
||||||
|
v,
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
return &NumberValue{
|
||||||
|
float64(v),
|
||||||
|
}
|
||||||
case float64:
|
case float64:
|
||||||
return NumberValue(v)
|
return &NumberValue{
|
||||||
|
v,
|
||||||
|
}
|
||||||
case string:
|
case string:
|
||||||
return StringValue(v)
|
return &StringValue{
|
||||||
|
v,
|
||||||
|
}
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
values := make([]Value, len(v))
|
values := make([]Value, len(v))
|
||||||
for i, value := range v {
|
for i, value := range v {
|
||||||
values[i] = GoToValue(value)
|
values[i] = GoToValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListValue{
|
return &ListValue{
|
||||||
values,
|
values,
|
||||||
}
|
}
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
|
|
@ -74,7 +83,7 @@ func GoToValue(gov interface{}) Value {
|
||||||
values[key] = GoToValue(value)
|
values[key] = GoToValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ObjectValue{
|
return &ObjectValue{
|
||||||
values,
|
values,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,49 +110,51 @@ type Value interface {
|
||||||
|
|
||||||
type NilValue struct{}
|
type NilValue struct{}
|
||||||
|
|
||||||
func (v NilValue) Type() ValueType {
|
func (v *NilValue) Type() ValueType {
|
||||||
return NilValueType
|
return NilValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NilValue) String() string {
|
func (v *NilValue) String() string {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NilValue) DebugString() string {
|
func (v *NilValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NilValue) Equals(other Value) bool {
|
func (v *NilValue) Equals(other Value) bool {
|
||||||
return other.Type() == NilValueType
|
return other.Type() == NilValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NilValue) Get(key string) (Value, error) {
|
func (v *NilValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("nil has no properties")
|
return nil, errors.New("nil has no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoolValue bool
|
type BoolValue struct {
|
||||||
|
bool
|
||||||
|
}
|
||||||
|
|
||||||
func (v BoolValue) Type() ValueType {
|
func (v *BoolValue) Type() ValueType {
|
||||||
return BoolValueType
|
return BoolValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BoolValue) String() string {
|
func (v *BoolValue) String() string {
|
||||||
if v {
|
if v.bool {
|
||||||
return "true"
|
return "true"
|
||||||
} else {
|
} else {
|
||||||
return "false"
|
return "false"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BoolValue) DebugString() string {
|
func (v *BoolValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BoolValue) Equals(other Value) bool {
|
func (v *BoolValue) Equals(other Value) bool {
|
||||||
return other.Type() == BoolValueType && bool(other.(BoolValue)) == bool(v)
|
return other.Type() == BoolValueType && other.(*BoolValue).bool == v.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BoolValue) Get(key string) (Value, error) {
|
func (v *BoolValue) Get(key string) (Value, error) {
|
||||||
return nil, errors.New("booleans have no properties")
|
return nil, errors.New("booleans have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,11 +163,11 @@ type ObjectValue struct {
|
||||||
members map[string]Value
|
members map[string]Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ObjectValue) Type() ValueType {
|
func (v *ObjectValue) Type() ValueType {
|
||||||
return ObjectValueType
|
return ObjectValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ObjectValue) String() string {
|
func (v *ObjectValue) String() string {
|
||||||
out := "{"
|
out := "{"
|
||||||
for key, value := range v.members {
|
for key, value := range v.members {
|
||||||
if out != "{" {
|
if out != "{" {
|
||||||
|
|
@ -170,74 +181,111 @@ func (v ObjectValue) String() string {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ObjectValue) DebugString() string {
|
func (v *ObjectValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ObjectValue) Equals(other Value) bool {
|
func (v *ObjectValue) Equals(other Value) bool {
|
||||||
// TODO implement object equality check
|
object, ok := other.(*ObjectValue)
|
||||||
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range v.members {
|
||||||
|
if !object.members[key].Equals(value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ObjectValue) Get(key string) (Value, error) {
|
var ObjectPrototype = map[string]Value{
|
||||||
if member := v.members[key]; member == nil {
|
"set": &BuiltinFunctionValue{
|
||||||
return nil, errors.New("no property found with name \"" + key + "\"")
|
"set",
|
||||||
} else {
|
[]string{"property", "value"},
|
||||||
|
func(vm *VM, _this Value, params map[string]Value) (Value, error) {
|
||||||
|
this := _this.(*ObjectValue)
|
||||||
|
|
||||||
|
p := params["property"]
|
||||||
|
v, ok := params["value"].(*StringValue)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("property is not a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.members[v.string] = p
|
||||||
|
|
||||||
|
return &NilValue{}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *ObjectValue) Get(key string) (Value, error) {
|
||||||
|
if member, ok := v.members[key]; ok {
|
||||||
return member, nil
|
return member, nil
|
||||||
|
} else if p, ok := ObjectPrototype[key]; ok {
|
||||||
|
return p, nil
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("no property found with name \"" + key + "\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumberValue Integer or floating-point values
|
// NumberValue Integer or floating-point values
|
||||||
type NumberValue float64
|
type NumberValue struct {
|
||||||
|
float64
|
||||||
|
}
|
||||||
|
|
||||||
const NumberSize int = 64
|
const NumberSize int = 64
|
||||||
|
|
||||||
func (v NumberValue) Type() ValueType {
|
func (v *NumberValue) Type() ValueType {
|
||||||
return NumberValueType
|
return NumberValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NumberValue) String() string {
|
func (v *NumberValue) String() string {
|
||||||
return strconv.FormatFloat(float64(v), 'g', -1, NumberSize)
|
return strconv.FormatFloat(v.float64, 'g', -1, NumberSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NumberValue) DebugString() string {
|
func (v *NumberValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NumberValue) Equals(other Value) bool {
|
func (v *NumberValue) Equals(other Value) bool {
|
||||||
return other.Type() == NumberValueType && float64(other.(NumberValue)) == float64(v)
|
return other.Type() == NumberValueType && other.(*NumberValue).float64 == v.float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v NumberValue) Get(key string) (Value, error) {
|
func (v *NumberValue) Get(key string) (Value, error) {
|
||||||
// TODO maybe add standard functions for number values?
|
// TODO maybe add standard functions for number values?
|
||||||
return nil, errors.New("numbers have no properties")
|
return nil, errors.New("numbers have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
type StringValue string
|
type StringValue struct {
|
||||||
|
string
|
||||||
|
}
|
||||||
|
|
||||||
func (v StringValue) Type() ValueType {
|
func (v *StringValue) Type() ValueType {
|
||||||
return StringValueType
|
return StringValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v StringValue) String() string {
|
func (v *StringValue) String() string {
|
||||||
return string(v)
|
return v.string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v StringValue) DebugString() string {
|
func (v *StringValue) DebugString() string {
|
||||||
return "\"" + v.String() + "\""
|
return "\"" + v.String() + "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v StringValue) Equals(other Value) bool {
|
func (v *StringValue) Equals(other Value) bool {
|
||||||
return other.Type() == StringValueType && string(other.(StringValue)) == string(v)
|
return other.Type() == StringValueType && other.(*StringValue).string == v.string
|
||||||
}
|
}
|
||||||
|
|
||||||
var StringPrototype = map[string]BuiltinFunctionValue{
|
var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||||
"split": {
|
"split": {
|
||||||
"split",
|
"split",
|
||||||
[]string{"seperator"},
|
[]string{"seperator"},
|
||||||
func(vm *VM, this Value, m map[string]Value) (Value, error) {
|
func(vm *VM, this Value, m map[string]Value) (Value, error) {
|
||||||
str := this.(StringValue).String()
|
str := this.(*StringValue).String()
|
||||||
sep := m["seperator"].(StringValue).String()
|
sep := m["seperator"].(*StringValue).String()
|
||||||
|
|
||||||
var out []string
|
var out []string
|
||||||
tmp := strings.Builder{}
|
tmp := strings.Builder{}
|
||||||
|
|
@ -256,7 +304,7 @@ var StringPrototype = map[string]BuiltinFunctionValue{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v StringValue) Get(key string) (Value, error) {
|
func (v *StringValue) Get(key string) (Value, error) {
|
||||||
if prop, ok := StringPrototype[key]; ok {
|
if prop, ok := StringPrototype[key]; ok {
|
||||||
return prop, nil
|
return prop, nil
|
||||||
}
|
}
|
||||||
|
|
@ -269,11 +317,11 @@ type ListValue struct {
|
||||||
items []Value
|
items []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ListValue) Type() ValueType {
|
func (v *ListValue) Type() ValueType {
|
||||||
return ListValueType
|
return ListValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ListValue) String() string {
|
func (v *ListValue) String() string {
|
||||||
out := "["
|
out := "["
|
||||||
for i, item := range v.items {
|
for i, item := range v.items {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
|
@ -286,23 +334,37 @@ func (v ListValue) String() string {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ListValue) DebugString() string {
|
func (v *ListValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ListValue) Equals(other Value) bool {
|
func (v *ListValue) Equals(other Value) bool {
|
||||||
return other.Type() == ListValueType &&
|
if other.Type() != ListValueType {
|
||||||
slices.Equal(v.items, other.(ListValue).items)
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
l := other.(*ListValue)
|
||||||
|
|
||||||
|
if len(v.items) != len(l.items) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range l.items {
|
||||||
|
if !item.Equals(l.items[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var ListPrototype = map[string]BuiltinFunctionValue{
|
var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
"append": {
|
"append": {
|
||||||
"append",
|
"append",
|
||||||
[]string{"item"},
|
[]string{"item"},
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||||
return ListValue{
|
this.(*ListValue).items = append(this.(*ListValue).items, p["item"])
|
||||||
append(this.(ListValue).items, p["item"]),
|
return &NilValue{}, nil
|
||||||
}, nil
|
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
@ -310,8 +372,8 @@ var ListPrototype = map[string]BuiltinFunctionValue{
|
||||||
"at",
|
"at",
|
||||||
[]string{"index"},
|
[]string{"index"},
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||||
index := int(p["index"].(NumberValue))
|
items := this.(*ListValue).items
|
||||||
items := this.(ListValue).items
|
index := int(p["index"].(*NumberValue).float64)
|
||||||
|
|
||||||
if index >= len(items) {
|
if index >= len(items) {
|
||||||
return nil, errors.New(fmt.Sprintf("list index %x out of range", index))
|
return nil, errors.New(fmt.Sprintf("list index %x out of range", index))
|
||||||
|
|
@ -325,7 +387,7 @@ var ListPrototype = map[string]BuiltinFunctionValue{
|
||||||
"length",
|
"length",
|
||||||
[]string{},
|
[]string{},
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
||||||
return NumberValue(len(this.(ListValue).items)), nil
|
return GoToValue(len(this.(*ListValue).items)), nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
@ -333,13 +395,13 @@ var ListPrototype = map[string]BuiltinFunctionValue{
|
||||||
"map",
|
"map",
|
||||||
[]string{"f"},
|
[]string{"f"},
|
||||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||||
list := value.(ListValue)
|
list := value.(*ListValue)
|
||||||
|
|
||||||
v := m["f"]
|
v := m["f"]
|
||||||
var f Value
|
var f Value
|
||||||
f, ok := v.(FunctionValue)
|
f, ok := v.(*FunctionValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
f, ok = v.(BuiltinFunctionValue)
|
f, ok = v.(*BuiltinFunctionValue)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New(fmt.Sprintf("not a function to apply: %s", v))
|
return nil, errors.New(fmt.Sprintf("not a function to apply: %s", v))
|
||||||
|
|
@ -365,7 +427,7 @@ var ListPrototype = map[string]BuiltinFunctionValue{
|
||||||
"reduce",
|
"reduce",
|
||||||
[]string{"f", "start"},
|
[]string{"f", "start"},
|
||||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||||
list := value.(ListValue)
|
list := value.(*ListValue)
|
||||||
f := m["f"]
|
f := m["f"]
|
||||||
sum := m["start"]
|
sum := m["start"]
|
||||||
|
|
||||||
|
|
@ -383,7 +445,7 @@ var ListPrototype = map[string]BuiltinFunctionValue{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v ListValue) Get(key string) (Value, error) {
|
func (v *ListValue) Get(key string) (Value, error) {
|
||||||
if prop, ok := ListPrototype[key]; ok {
|
if prop, ok := ListPrototype[key]; ok {
|
||||||
return prop, nil
|
return prop, nil
|
||||||
}
|
}
|
||||||
|
|
@ -398,25 +460,25 @@ type FunctionValue struct {
|
||||||
Parent Value
|
Parent Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v FunctionValue) Type() ValueType {
|
func (v *FunctionValue) Type() ValueType {
|
||||||
return FunctionValueType
|
return FunctionValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v FunctionValue) String() string {
|
func (v *FunctionValue) String() string {
|
||||||
return fmt.Sprintf("<function name=%s>", v.Name)
|
return fmt.Sprintf("<function name=%s>", v.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v FunctionValue) DebugString() string {
|
func (v *FunctionValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v FunctionValue) Equals(other Value) bool {
|
func (v *FunctionValue) Equals(other Value) bool {
|
||||||
return other.Type() == FunctionValueType &&
|
return other.Type() == FunctionValueType &&
|
||||||
v.Name == other.(FunctionValue).Name &&
|
v.Name == other.(*FunctionValue).Name &&
|
||||||
v.Chunk == other.(FunctionValue).Chunk
|
v.Chunk == other.(*FunctionValue).Chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v FunctionValue) Get(_ string) (Value, error) {
|
func (v *FunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,20 +493,20 @@ func (v BuiltinFunctionValue) Type() ValueType {
|
||||||
return BuiltinFunctionValueType
|
return BuiltinFunctionValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BuiltinFunctionValue) String() string {
|
func (v *BuiltinFunctionValue) String() string {
|
||||||
return fmt.Sprintf("<function name=%s builtin>", v.Name)
|
return fmt.Sprintf("<function name=%s builtin>", v.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BuiltinFunctionValue) DebugString() string {
|
func (v *BuiltinFunctionValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BuiltinFunctionValue) Equals(other Value) bool {
|
func (v *BuiltinFunctionValue) Equals(other Value) bool {
|
||||||
return other.Type() == BuiltinFunctionValueType &&
|
return other.Type() == BuiltinFunctionValueType &&
|
||||||
v.Name == other.(BuiltinFunctionValue).Name
|
v.Name == other.(*BuiltinFunctionValue).Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v BuiltinFunctionValue) Get(_ string) (Value, error) {
|
func (v *BuiltinFunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,27 +517,27 @@ type VariableValue struct {
|
||||||
scope Pos
|
scope Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VariableValue) Type() ValueType {
|
func (v *VariableValue) Type() ValueType {
|
||||||
return VariableValueType
|
return VariableValueType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VariableValue) String() string {
|
func (v *VariableValue) String() string {
|
||||||
return fmt.Sprintf("<variable name=%s value=%s scope=%d>", v.name, v.value, v.scope)
|
return fmt.Sprintf("<variable name=%s value=%s scope=%d>", v.name, v.value, v.scope)
|
||||||
|
|
||||||
// variables should not be accessed on the stack; normal values should be pushed and popped predictably
|
// variables should not be accessed on the stack; normal values should be pushed and popped predictably
|
||||||
//panic("tried getting string value of a unreachable value")
|
//panic("tried getting string value of a unreachable value")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VariableValue) DebugString() string {
|
func (v *VariableValue) DebugString() string {
|
||||||
return v.String()
|
return v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VariableValue) Equals(other Value) bool {
|
func (v *VariableValue) Equals(other Value) bool {
|
||||||
return other.Type() == VariableValueType &&
|
return other.Type() == VariableValueType &&
|
||||||
v.name == other.(VariableValue).name &&
|
v.name == other.(*VariableValue).name &&
|
||||||
v.value.Equals(other.(VariableValue).value)
|
v.value.Equals(other.(*VariableValue).value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v VariableValue) Get(_ string) (Value, error) {
|
func (v *VariableValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("variables have no properties")
|
return nil, errors.New("variables have no properties")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,26 +16,26 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
||||||
t.Logf("Both are nil")
|
t.Logf("Both are nil")
|
||||||
return
|
return
|
||||||
case BoolValueType:
|
case BoolValueType:
|
||||||
if got.(BoolValue) != want.(BoolValue) {
|
if got.(*BoolValue).bool != want.(*BoolValue).bool {
|
||||||
t.Errorf("bool value mismatch: got %v, want %v", got.(BoolValue), want.(BoolValue))
|
t.Errorf("bool value mismatch: got %v, want %v", got.(*BoolValue), want.(*BoolValue))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Both are same boolean (%s)", want.(BoolValue).String())
|
t.Logf("Both are same boolean (%s)", want.(*BoolValue).String())
|
||||||
}
|
}
|
||||||
case NumberValueType:
|
case NumberValueType:
|
||||||
if got.(NumberValue) != want.(NumberValue) {
|
if got.(*NumberValue).float64 != want.(*NumberValue).float64 {
|
||||||
t.Errorf("number value mismatch: got %v, want %v", got.(NumberValue), want.(NumberValue))
|
t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Both are same number (%s)", got.(NumberValue).String())
|
t.Logf("Both are same number (%s)", got.(*NumberValue).String())
|
||||||
}
|
}
|
||||||
case StringValueType:
|
case StringValueType:
|
||||||
if got.(StringValue) != want.(StringValue) {
|
if got.(*StringValue).string != want.(*StringValue).string {
|
||||||
t.Errorf("string value mismatch: got %v, want %v", got.(StringValue), want.(StringValue))
|
t.Errorf("string value mismatch: got %v, want %v", got.(*StringValue), want.(*StringValue))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Both are same string (%s)", got.(StringValue).String())
|
t.Logf("Both are same string (%s)", got.(*StringValue).String())
|
||||||
}
|
}
|
||||||
case FunctionValueType:
|
case FunctionValueType:
|
||||||
n := got.(FunctionValue)
|
n := got.(*FunctionValue)
|
||||||
m := want.(FunctionValue)
|
m := want.(*FunctionValue)
|
||||||
|
|
||||||
if n.Name != m.Name {
|
if n.Name != m.Name {
|
||||||
t.Errorf("function name mismatch: got %v, want %v", n.Name, m.Name)
|
t.Errorf("function name mismatch: got %v, want %v", n.Name, m.Name)
|
||||||
|
|
@ -53,8 +53,8 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
||||||
|
|
||||||
CompareChunks(t, n.Chunk, m.Chunk)
|
CompareChunks(t, n.Chunk, m.Chunk)
|
||||||
case BuiltinFunctionValueType:
|
case BuiltinFunctionValueType:
|
||||||
n := got.(BuiltinFunctionValue)
|
n := got.(*BuiltinFunctionValue)
|
||||||
m := want.(BuiltinFunctionValue)
|
m := want.(*BuiltinFunctionValue)
|
||||||
|
|
||||||
if n.Name != m.Name {
|
if n.Name != m.Name {
|
||||||
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
|
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
|
||||||
|
|
|
||||||
190
core/vm.go
190
core/vm.go
|
|
@ -96,7 +96,8 @@ const (
|
||||||
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
|
// InstructionAppend Append to a list. stack: (... > list > item) => (... > list)
|
||||||
InstructionAppend
|
InstructionAppend
|
||||||
// InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of
|
// InstructionFormList Form items on the stack into a list. The 2 bytes after the instructions are the amount of
|
||||||
// items to include minus one. (value of 0 => 1 item, value of 1 => 2 items, etc.)
|
// items to include minus one. (value of 0 => 1 item, value of 1 => 2 items, etc.) The order is reversed compared
|
||||||
|
// to on the stack; the top value on the stack is the last in the list.
|
||||||
InstructionFormList
|
InstructionFormList
|
||||||
|
|
||||||
// InstructionBreakpoint for debugging purposes
|
// InstructionBreakpoint for debugging purposes
|
||||||
|
|
@ -203,7 +204,7 @@ func (c Chunk) String() string {
|
||||||
for i, ct := range c.Constants {
|
for i, ct := range c.Constants {
|
||||||
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct))
|
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct))
|
||||||
|
|
||||||
f, ok := ct.(FunctionValue)
|
f, ok := ct.(*FunctionValue)
|
||||||
if ok {
|
if ok {
|
||||||
b.WriteString(f.Chunk.String())
|
b.WriteString(f.Chunk.String())
|
||||||
}
|
}
|
||||||
|
|
@ -219,9 +220,10 @@ func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterGOBTypes() {
|
func RegisterGOBTypes() {
|
||||||
gob.Register(StringValue(""))
|
gob.Register(&StringValue{""})
|
||||||
gob.Register(NumberValue(0))
|
gob.Register(&BoolValue{false})
|
||||||
gob.Register(FunctionValue{
|
gob.Register(&NumberValue{0})
|
||||||
|
gob.Register(&FunctionValue{
|
||||||
Name: "",
|
Name: "",
|
||||||
Params: nil,
|
Params: nil,
|
||||||
Chunk: nil,
|
Chunk: nil,
|
||||||
|
|
@ -283,7 +285,7 @@ type Call struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultGlobals = map[string]Value{
|
var DefaultGlobals = map[string]Value{
|
||||||
"write": BuiltinFunctionValue{
|
"write": &BuiltinFunctionValue{
|
||||||
"write", // always remember where you come from...
|
"write", // always remember where you come from...
|
||||||
[]string{"value"},
|
[]string{"value"},
|
||||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
||||||
|
|
@ -292,7 +294,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
"print": BuiltinFunctionValue{
|
"print": &BuiltinFunctionValue{
|
||||||
"print",
|
"print",
|
||||||
[]string{"value"},
|
[]string{"value"},
|
||||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
||||||
|
|
@ -301,15 +303,43 @@ var DefaultGlobals = map[string]Value{
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
"assert": BuiltinFunctionValue{
|
"format": &BuiltinFunctionValue{
|
||||||
"assert",
|
"format",
|
||||||
[]string{"condition"},
|
[]string{"format_string", "values"},
|
||||||
|
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
||||||
|
valuies := m["values"].(*ListValue).items
|
||||||
|
|
||||||
|
return GoToValue(fmt.Sprintf(m["format_string"].String(), valuies)), nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
"assertEq": &BuiltinFunctionValue{
|
||||||
|
"assertEq",
|
||||||
|
[]string{"a", "b"},
|
||||||
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
||||||
if !params["condition"].(BoolValue) {
|
a := params["a"]
|
||||||
return nil, errors.New("assertion failed")
|
b := params["b"]
|
||||||
|
|
||||||
|
if !a.Equals(b) {
|
||||||
|
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
return NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
"assertNotEq": &BuiltinFunctionValue{
|
||||||
|
"assertNotEq",
|
||||||
|
[]string{"a", "b"},
|
||||||
|
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
||||||
|
a := params["a"]
|
||||||
|
b := params["b"]
|
||||||
|
|
||||||
|
if a.Equals(b) {
|
||||||
|
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
@ -330,6 +360,10 @@ func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
|
||||||
// Next execute instruction
|
// Next execute instruction
|
||||||
// returns true if more instructions should be executed
|
// returns true if more instructions should be executed
|
||||||
func (vm *VM) Next() bool {
|
func (vm *VM) Next() bool {
|
||||||
|
if !vm.HasNext() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
switch vm.NextByte() {
|
switch vm.NextByte() {
|
||||||
case InstructionReturn:
|
case InstructionReturn:
|
||||||
if vm.call.Current == 0 {
|
if vm.call.Current == 0 {
|
||||||
|
|
@ -359,81 +393,81 @@ func (vm *VM) Next() bool {
|
||||||
vm.stack.Push(vm.ReadConstant())
|
vm.stack.Push(vm.ReadConstant())
|
||||||
|
|
||||||
case InstructionAdd:
|
case InstructionAdd:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(l + r)
|
vm.stack.Push(&NumberValue{l + r})
|
||||||
|
|
||||||
case InstructionSub:
|
case InstructionSub:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(l - r)
|
vm.stack.Push(&NumberValue{l - r})
|
||||||
|
|
||||||
case InstructionMul:
|
case InstructionMul:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(l * r)
|
vm.stack.Push(&NumberValue{l * r})
|
||||||
|
|
||||||
case InstructionDiv:
|
case InstructionDiv:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(l / r)
|
vm.stack.Push(&NumberValue{l / r})
|
||||||
|
|
||||||
case InstructionEquals:
|
case InstructionEquals:
|
||||||
vm.stack.Push(
|
vm.stack.Push(
|
||||||
BoolValue(vm.stack.Pop().Equals(vm.stack.Pop())),
|
&BoolValue{vm.stack.Pop().Equals(vm.stack.Pop())},
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionNotEqual:
|
case InstructionNotEqual:
|
||||||
vm.stack.Push(
|
vm.stack.Push(
|
||||||
BoolValue(!vm.stack.Pop().Equals(vm.stack.Pop())),
|
&BoolValue{!vm.stack.Pop().Equals(vm.stack.Pop())},
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionNot:
|
case InstructionNot:
|
||||||
b := vm.stack.Pop().(BoolValue)
|
b := vm.stack.Pop().(*BoolValue).bool
|
||||||
vm.stack.Push(!b)
|
vm.stack.Push(&BoolValue{!b})
|
||||||
|
|
||||||
case InstructionAnd:
|
case InstructionAnd:
|
||||||
r := vm.stack.Pop().(BoolValue)
|
r := vm.stack.Pop().(*BoolValue).bool
|
||||||
l := vm.stack.Pop().(BoolValue)
|
l := vm.stack.Pop().(*BoolValue).bool
|
||||||
vm.stack.Push(l && r)
|
vm.stack.Push(&BoolValue{l && r})
|
||||||
|
|
||||||
case InstructionOr:
|
case InstructionOr:
|
||||||
r := vm.stack.Pop().(BoolValue)
|
r := vm.stack.Pop().(*BoolValue).bool
|
||||||
l := vm.stack.Pop().(BoolValue)
|
l := vm.stack.Pop().(*BoolValue).bool
|
||||||
vm.stack.Push(r || l)
|
vm.stack.Push(&BoolValue{l || r})
|
||||||
|
|
||||||
case InstructionLess:
|
case InstructionLess:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(BoolValue(l < r))
|
vm.stack.Push(&BoolValue{l < r})
|
||||||
|
|
||||||
case InstructionLessOrEqual:
|
case InstructionLessOrEqual:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(BoolValue(l <= r))
|
vm.stack.Push(&BoolValue{l <= r})
|
||||||
|
|
||||||
case InstructionGreater:
|
case InstructionGreater:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(BoolValue(l > r))
|
vm.stack.Push(&BoolValue{l > r})
|
||||||
|
|
||||||
case InstructionGreaterOrEqual:
|
case InstructionGreaterOrEqual:
|
||||||
r := vm.stack.Pop().(NumberValue)
|
r := vm.stack.Pop().(*NumberValue).float64
|
||||||
l := vm.stack.Pop().(NumberValue)
|
l := vm.stack.Pop().(*NumberValue).float64
|
||||||
|
|
||||||
vm.stack.Push(BoolValue(l >= r))
|
vm.stack.Push(&BoolValue{l >= r})
|
||||||
|
|
||||||
case InstructionCall:
|
case InstructionCall:
|
||||||
v := vm.stack.Pop()
|
v := vm.stack.Pop()
|
||||||
switch f := v.(type) {
|
switch f := v.(type) {
|
||||||
case FunctionValue:
|
case *FunctionValue:
|
||||||
vm.call.Push(Call{
|
vm.call.Push(Call{
|
||||||
chunk: vm.chunk,
|
chunk: vm.chunk,
|
||||||
ip: vm.ip,
|
ip: vm.ip,
|
||||||
|
|
@ -459,7 +493,7 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
vm.chunk = f.Chunk
|
vm.chunk = f.Chunk
|
||||||
vm.ip = 0
|
vm.ip = 0
|
||||||
case BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
args := map[string]Value{}
|
args := map[string]Value{}
|
||||||
|
|
||||||
for i := len(f.Parameters) - 1; i >= 0; i-- {
|
for i := len(f.Parameters) - 1; i >= 0; i-- {
|
||||||
|
|
@ -473,7 +507,7 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
vm.stack.Push(v)
|
vm.stack.Push(v)
|
||||||
default:
|
default:
|
||||||
vm.error(fmt.Sprintf("value called is not a function (%s)", v.DebugString()))
|
vm.error(fmt.Sprintf("value called is not a function (%s, type %T)", v.DebugString(), v))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,13 +519,13 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionJumpFalse:
|
case InstructionJumpFalse:
|
||||||
n := vm.NextU16()
|
n := vm.NextU16()
|
||||||
if !vm.stack.Pop().(BoolValue) {
|
if !vm.stack.Pop().(*BoolValue).bool {
|
||||||
vm.ip += Pos(n)
|
vm.ip += Pos(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
case InstructionGetLocal:
|
case InstructionGetLocal:
|
||||||
name := vm.GetConstant(vm.NextByte()).(StringValue)
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
||||||
v := vm.getVar(string(name))
|
v := vm.getVar(name)
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
vm.error(fmt.Sprintf("cannot get local: undefined variable %s", name))
|
vm.error(fmt.Sprintf("cannot get local: undefined variable %s", name))
|
||||||
|
|
@ -502,9 +536,9 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionSetLocal:
|
case InstructionSetLocal:
|
||||||
value := vm.stack.Pop().(Value)
|
value := vm.stack.Pop().(Value)
|
||||||
name := vm.GetConstant(vm.NextByte()).(StringValue)
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
||||||
|
|
||||||
v := vm.getVar(string(name))
|
v := vm.getVar(name)
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
||||||
|
|
@ -514,33 +548,39 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionDeclareLocal:
|
case InstructionDeclareLocal:
|
||||||
vm.addVar(
|
vm.addVar(
|
||||||
string(vm.GetConstant(vm.NextByte()).(StringValue)),
|
vm.GetConstant(vm.NextByte()).(*StringValue).string,
|
||||||
vm.stack.Pop().(Value),
|
vm.stack.Pop().(Value),
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionGetGlobal:
|
case InstructionGetGlobal:
|
||||||
vm.stack.Push(vm.globals[string(vm.GetConstant(vm.NextByte()).(StringValue))])
|
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string])
|
||||||
|
|
||||||
case InstructionSetGlobal:
|
case InstructionSetGlobal:
|
||||||
vm.globals[string(vm.GetConstant(vm.NextByte()).(StringValue))] = vm.stack.Pop()
|
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string] = vm.stack.Pop()
|
||||||
|
|
||||||
case InstructionTrue:
|
case InstructionTrue:
|
||||||
vm.stack.Push(BoolValue(true))
|
vm.stack.Push(&BoolValue{true})
|
||||||
|
|
||||||
case InstructionFalse:
|
case InstructionFalse:
|
||||||
vm.stack.Push(BoolValue(false))
|
vm.stack.Push(&BoolValue{false})
|
||||||
|
|
||||||
case InstructionNil:
|
case InstructionNil:
|
||||||
vm.stack.Push(NilValue{})
|
vm.stack.Push(&NilValue{})
|
||||||
|
|
||||||
case InstructionFormList:
|
case InstructionFormList:
|
||||||
|
n := int(vm.NextU16())
|
||||||
|
|
||||||
|
items := make([]Value, n+1)
|
||||||
|
for i := 0; i <= n; i++ {
|
||||||
|
items[n-i] = vm.stack.Pop()
|
||||||
|
}
|
||||||
|
|
||||||
case InstructionNewList:
|
case InstructionNewList:
|
||||||
vm.stack.Push(ListValue{[]Value{}})
|
vm.stack.Push(&ListValue{[]Value{}})
|
||||||
|
|
||||||
case InstructionAppend:
|
case InstructionAppend:
|
||||||
value := vm.stack.Pop()
|
value := vm.stack.Pop()
|
||||||
list := vm.stack.Pop().(ListValue)
|
list := vm.stack.Pop().(*ListValue)
|
||||||
list.items = append(list.items, value)
|
list.items = append(list.items, value)
|
||||||
vm.stack.Push(list)
|
vm.stack.Push(list)
|
||||||
|
|
||||||
|
|
@ -552,13 +592,13 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionStringConversion:
|
case InstructionStringConversion:
|
||||||
v := vm.stack.Pop()
|
v := vm.stack.Pop()
|
||||||
vm.stack.Push(StringValue(v.String()))
|
vm.stack.Push(&StringValue{v.String()})
|
||||||
|
|
||||||
case InstructionStringConcatenation:
|
case InstructionStringConcatenation:
|
||||||
r := vm.stack.Pop().(StringValue)
|
r := vm.stack.Pop().(*StringValue).string
|
||||||
l := vm.stack.Pop().(StringValue)
|
l := vm.stack.Pop().(*StringValue).string
|
||||||
|
|
||||||
vm.stack.Push(l + r)
|
vm.stack.Push(&StringValue{l + r})
|
||||||
|
|
||||||
case InstructionSwap:
|
case InstructionSwap:
|
||||||
r := vm.stack.Pop()
|
r := vm.stack.Pop()
|
||||||
|
|
@ -570,20 +610,16 @@ func (vm *VM) Next() bool {
|
||||||
source := vm.stack.Pop()
|
source := vm.stack.Pop()
|
||||||
property := vm.ReadConstant()
|
property := vm.ReadConstant()
|
||||||
|
|
||||||
member, err := source.Get(property.(StringValue).String())
|
member, err := source.Get(property.(*StringValue).String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
vm.error(err.Error())
|
vm.error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// add parent if function with a little switcheroo
|
// add parent if function
|
||||||
if member.Type() == FunctionValueType {
|
if member.Type() == FunctionValueType {
|
||||||
f := member.(FunctionValue)
|
member.(*FunctionValue).Parent = source
|
||||||
f.Parent = source
|
|
||||||
member = f
|
|
||||||
} else if member.Type() == BuiltinFunctionValueType {
|
} else if member.Type() == BuiltinFunctionValueType {
|
||||||
f := member.(BuiltinFunctionValue)
|
member.(*BuiltinFunctionValue).Parent = source
|
||||||
f.Parent = source
|
|
||||||
member = f
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.stack.Push(member)
|
vm.stack.Push(member)
|
||||||
|
|
@ -599,7 +635,7 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
switch f := v.(type) {
|
switch f := v.(type) {
|
||||||
case FunctionValue:
|
case *FunctionValue:
|
||||||
vm.call.Push(Call{
|
vm.call.Push(Call{
|
||||||
chunk: vm.chunk,
|
chunk: vm.chunk,
|
||||||
ip: vm.ip,
|
ip: vm.ip,
|
||||||
|
|
@ -630,7 +666,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
|
|
||||||
return vm.stack.Pop(), nil
|
return vm.stack.Pop(), nil
|
||||||
|
|
||||||
case BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
argies := map[string]Value{}
|
argies := map[string]Value{}
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
|
|
|
||||||
126
core/vm_test.go
126
core/vm_test.go
|
|
@ -11,7 +11,7 @@ func CompareChunks(t *testing.T, got *Chunk, want *Chunk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, v := range got.Constants {
|
for i, v := range got.Constants {
|
||||||
if v != want.Constants[i] {
|
if i < len(want.Constants) && !v.Equals(want.Constants[i]) {
|
||||||
t.Errorf("constant %d does not match (%s and %s)", i, v.String(), want.Constants[i].String())
|
t.Errorf("constant %d does not match (%s and %s)", i, v.String(), want.Constants[i].String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ func TestNewVM(t *testing.T) {
|
||||||
chunk := NewChunk([]Bytecode{
|
chunk := NewChunk([]Bytecode{
|
||||||
InstructionConstant, 0,
|
InstructionConstant, 0,
|
||||||
}, []Value{
|
}, []Value{
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
})
|
})
|
||||||
stackSize := Pos(256)
|
stackSize := Pos(256)
|
||||||
callstackSize := Pos(256)
|
callstackSize := Pos(256)
|
||||||
|
|
@ -107,10 +107,10 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionAdd,
|
InstructionAdd,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1), NumberValue(2),
|
&NumberValue{1}, &NumberValue{2},
|
||||||
}),
|
}),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(3),
|
&NumberValue{3},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"push_constant": {
|
"push_constant": {
|
||||||
|
|
@ -119,11 +119,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionConstant, 0,
|
InstructionConstant, 0,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"push_true": {
|
"push_true": {
|
||||||
|
|
@ -134,7 +134,7 @@ func GetExecutionTestData() map[string]struct {
|
||||||
[]Value{},
|
[]Value{},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(true),
|
&BoolValue{true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"push_false": {
|
"push_false": {
|
||||||
|
|
@ -145,7 +145,7 @@ func GetExecutionTestData() map[string]struct {
|
||||||
[]Value{},
|
[]Value{},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(false),
|
&BoolValue{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"push_nil": {
|
"push_nil": {
|
||||||
|
|
@ -156,7 +156,7 @@ func GetExecutionTestData() map[string]struct {
|
||||||
[]Value{},
|
[]Value{},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NilValue{},
|
&NilValue{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"empty": {
|
"empty": {
|
||||||
|
|
@ -181,11 +181,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionDiv,
|
InstructionDiv,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(2), NumberValue(1), NumberValue(5), NumberValue(6),
|
&NumberValue{2}, &NumberValue{1}, &NumberValue{5}, &NumberValue{6},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue((2.0 + 1.0) * 5.0 / (6.0 - 2.0)),
|
&NumberValue{3.75},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"equality_true": {
|
"equality_true": {
|
||||||
|
|
@ -196,11 +196,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionEquals,
|
InstructionEquals,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(true),
|
&BoolValue{true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"equality_false": {
|
"equality_false": {
|
||||||
|
|
@ -211,11 +211,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionEquals,
|
InstructionEquals,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1), NumberValue(2),
|
&NumberValue{1}, &NumberValue{2},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(false),
|
&BoolValue{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"inequality_false": {
|
"inequality_false": {
|
||||||
|
|
@ -226,11 +226,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionNotEqual,
|
InstructionNotEqual,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(false),
|
&BoolValue{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"inequality_true": {
|
"inequality_true": {
|
||||||
|
|
@ -241,11 +241,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionNotEqual,
|
InstructionNotEqual,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1), NumberValue(2),
|
&NumberValue{1}, &NumberValue{2},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(true),
|
&BoolValue{true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"not_true": {
|
"not_true": {
|
||||||
|
|
@ -257,7 +257,7 @@ func GetExecutionTestData() map[string]struct {
|
||||||
[]Value{},
|
[]Value{},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(false),
|
&BoolValue{false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"not_false": {
|
"not_false": {
|
||||||
|
|
@ -269,7 +269,7 @@ func GetExecutionTestData() map[string]struct {
|
||||||
[]Value{},
|
[]Value{},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
BoolValue(true),
|
&BoolValue{true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"jump": {
|
"jump": {
|
||||||
|
|
@ -280,11 +280,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionConstant, 1, // should execute
|
InstructionConstant, 1, // should execute
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1),
|
&NumberValue{0}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"jump_false/false": {
|
"jump_false/false": {
|
||||||
|
|
@ -296,11 +296,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionConstant, 1, // should execute
|
InstructionConstant, 1, // should execute
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1),
|
&NumberValue{0}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"jump_false/true": {
|
"jump_false/true": {
|
||||||
|
|
@ -312,11 +312,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionConstant, 1, // should execute
|
InstructionConstant, 1, // should execute
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1),
|
&NumberValue{0}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1),
|
&NumberValue{0}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"declare_local": {
|
"declare_local": {
|
||||||
|
|
@ -326,13 +326,13 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionDeclareLocal, 1,
|
InstructionDeclareLocal, 1,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), StringValue("a"),
|
&NumberValue{0}, &StringValue{"a"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -346,13 +346,13 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionSetLocal, 1, // reassign
|
InstructionSetLocal, 1, // reassign
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), StringValue("a"), NumberValue(1),
|
&NumberValue{0}, &StringValue{"a"}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -365,16 +365,16 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionGetLocal, 1, // reassign
|
InstructionGetLocal, 1, // reassign
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), StringValue("a"),
|
&NumberValue{0}, &StringValue{"a"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"get_reassigned_local": {
|
"get_reassigned_local": {
|
||||||
|
|
@ -388,17 +388,17 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionGetLocal, 1,
|
InstructionGetLocal, 1,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), StringValue("a"), NumberValue(1),
|
&NumberValue{0}, &StringValue{"a"}, &NumberValue{1},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"variable_scope": {
|
"variable_scope": {
|
||||||
|
|
@ -416,15 +416,15 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionAscend,
|
InstructionAscend,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), StringValue("a"),
|
&NumberValue{0}, &StringValue{"a"},
|
||||||
NumberValue(1), StringValue("b"),
|
&NumberValue{1}, &StringValue{"b"},
|
||||||
NumberValue(2), StringValue("c"),
|
&NumberValue{2}, &StringValue{"c"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -438,9 +438,9 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionCall,
|
InstructionCall,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
NumberValue(2),
|
&NumberValue{2},
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []string{"a", "b"},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
|
|
@ -451,14 +451,14 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionReturn,
|
InstructionReturn,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("a"), StringValue("b"),
|
&StringValue{"a"}, &StringValue{"b"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(3),
|
&NumberValue{3},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"function_calling_function": {
|
"function_calling_function": {
|
||||||
|
|
@ -472,9 +472,9 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionCall,
|
InstructionCall,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(1),
|
&NumberValue{1},
|
||||||
NumberValue(2),
|
&NumberValue{2},
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []string{"a", "b"},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
|
|
@ -487,11 +487,11 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionReturn,
|
InstructionReturn,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("a"), StringValue("b"), StringValue("square"),
|
&StringValue{"a"}, &StringValue{"b"}, &StringValue{"square"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
Name: "square",
|
Name: "square",
|
||||||
Params: []string{"n"},
|
Params: []string{"n"},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
|
|
@ -502,17 +502,17 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionReturn,
|
InstructionReturn,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("n"),
|
&StringValue{"n"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
StringValue("square"),
|
&StringValue{"square"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"square",
|
"square",
|
||||||
FunctionValue{
|
&FunctionValue{
|
||||||
Name: "square",
|
Name: "square",
|
||||||
Params: []string{"n"},
|
Params: []string{"n"},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
|
|
@ -523,13 +523,13 @@ func GetExecutionTestData() map[string]struct {
|
||||||
InstructionReturn,
|
InstructionReturn,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
StringValue("n"),
|
&StringValue{"n"},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
NumberValue(5),
|
&NumberValue{5},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +542,7 @@ func TestVM_Execution(t *testing.T) {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
vm := NewVM(test.chunk, 256, 256)
|
vm := NewVM(test.chunk, 256, 256)
|
||||||
|
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CompareStacks(t, test.resultingStack, vm.stack)
|
CompareStacks(t, test.resultingStack, vm.stack)
|
||||||
|
|
@ -557,7 +557,7 @@ func BenchmarkVM_Execution(b *testing.B) {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
vm := NewVM(test.chunk, 256, 256)
|
vm := NewVM(test.chunk, 256, 256)
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -571,7 +571,7 @@ func TestVM_NextByte(t *testing.T) {
|
||||||
InstructionConstant, 0,
|
InstructionConstant, 0,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0),
|
&NumberValue{0},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
16,
|
16,
|
||||||
|
|
@ -682,7 +682,7 @@ func TestVM_Jump(t *testing.T) {
|
||||||
InstructionConstant, 2,
|
InstructionConstant, 2,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1), NumberValue(2),
|
&NumberValue{0}, &NumberValue{1}, &NumberValue{2},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
16,
|
16,
|
||||||
|
|
@ -707,7 +707,7 @@ func TestVM_JumpFalse(t *testing.T) {
|
||||||
InstructionConstant, 2,
|
InstructionConstant, 2,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1), NumberValue(2),
|
&NumberValue{0}, &NumberValue{1}, &NumberValue{2},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
16,
|
16,
|
||||||
|
|
@ -733,7 +733,7 @@ func TestVM_DontJumpFalse(t *testing.T) {
|
||||||
InstructionConstant, 2,
|
InstructionConstant, 2,
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
NumberValue(0), NumberValue(1), NumberValue(2),
|
&NumberValue{0}, &NumberValue{1}, &NumberValue{2},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
16,
|
16,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
assert(1+1 == 2)
|
assertEq(1+1, 2)
|
||||||
assert(3*2 == 6)
|
assertEq(3*2, 6)
|
||||||
assert(3/4 == 0.75)
|
assertEq(3/4, 0.75)
|
||||||
assert(2 - 5 == -3)
|
assertEq(2 - 5, -3)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
# Basic equality
|
# Basic equality
|
||||||
assert(1 == 1)
|
assertEq(1, 1)
|
||||||
assert(0 == 0)
|
assertEq(0, 0)
|
||||||
assert("" == "")
|
assertEq("", "")
|
||||||
|
|
||||||
assert([] == [])
|
assertEq([], [])
|
||||||
assert([3, 1, 4, 1] == [3, 1, 4, 1])
|
assertEq([3, 1, 4, 1], [3, 1, 4, 1])
|
||||||
assert([true, 1024, nil, "Hello world!"] == [true, 1024, nil, "Hello world!"])
|
assertEq([true, 1024, nil, "Hello world!"], [true, 1024, nil, "Hello world!"])
|
||||||
|
|
||||||
# Inequality
|
# Inequality
|
||||||
assert(2 != 3)
|
assertNotEq(2, 3)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ list := []
|
||||||
|
|
||||||
x := 1
|
x := 1
|
||||||
while x <= 1000 {
|
while x <= 1000 {
|
||||||
list = list.append(x)
|
list.append(x)
|
||||||
assert(list.reduce(func(tot, a){
|
assertEq(list.reduce(func(tot, a){
|
||||||
return tot + a
|
return tot + a
|
||||||
}, 0) == x*(x + 1)/2)
|
}, 0), x*(x + 1)/2)
|
||||||
|
|
||||||
x = x + 1
|
x = x + 1
|
||||||
}
|
}
|
||||||
|
|
@ -17,8 +17,8 @@ func sum(a, b) {
|
||||||
list = []
|
list = []
|
||||||
x = 1
|
x = 1
|
||||||
while x <= 100 {
|
while x <= 100 {
|
||||||
list = list.append(2*x - 1)
|
list.append(2*x - 1)
|
||||||
assert(list.reduce(sum, 0) == x*x)
|
assertEq(list.reduce(sum, 0), x*x)
|
||||||
|
|
||||||
x = x + 1
|
x = x + 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ fibonacci_numbers := [
|
||||||
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,
|
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,
|
||||||
610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657,
|
610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657,
|
||||||
46368, 75025, 121393, 196418, 317811, 514229, 832040,
|
46368, 75025, 121393, 196418, 317811, 514229, 832040,
|
||||||
1346269, 2178309, 3524578, 5702887, 9227465, 14930352,
|
1346269, 2178309, 3524578, 5702887, 9227465, 14930352
|
||||||
24157817, 39088169, 63245986, 102334155
|
|
||||||
]
|
]
|
||||||
|
|
||||||
func fib(n) {
|
func fib(n) {
|
||||||
|
|
@ -15,14 +14,21 @@ func fib(n) {
|
||||||
return fib(n-1) + fib(n-2)
|
return fib(n-1) + fib(n-2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n := 0
|
||||||
|
while n < fibonacci_numbers.length() {
|
||||||
|
print("_")
|
||||||
|
n = n + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
write("")
|
||||||
|
|
||||||
x := 0
|
x := 0
|
||||||
while x < fibonacci_numbers.length() {
|
while x < fibonacci_numbers.length() {
|
||||||
n := fib(x)
|
n := fib(x)
|
||||||
|
|
||||||
print("assert ")
|
assertEq(n, fibonacci_numbers.at(x))
|
||||||
print(n)
|
print("*")
|
||||||
print(" == ")
|
|
||||||
write(fibonacci_numbers.at(x))
|
|
||||||
assert(n == fibonacci_numbers.at(x))
|
|
||||||
x = x + 1
|
x = x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write("")
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ a := 2
|
||||||
|
|
||||||
{
|
{
|
||||||
a := 3
|
a := 3
|
||||||
assert(a == 3)
|
assertEq(a, 3)
|
||||||
|
|
||||||
a = 4
|
a = 4
|
||||||
assert(a == 4)
|
assertEq(a, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(a == 2)
|
assertEq(a, 2)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ func sum(a, b) {
|
||||||
|
|
||||||
breakpoint
|
breakpoint
|
||||||
|
|
||||||
assert(sum(1, 2) == 3)
|
assertEq(sum(1, 2), 3)
|
||||||
breakpoint
|
breakpoint
|
||||||
|
|
||||||
assert(sum(3, 3) == 6)
|
assertEq(sum(3, 3), 6)
|
||||||
breakpoint
|
breakpoint
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func run(this js.Value, args []js.Value) interface{} {
|
||||||
vm := core.NewVM(compiler.Chunk, 256, 256)
|
vm := core.NewVM(compiler.Chunk, 256, 256)
|
||||||
|
|
||||||
// overwrite output
|
// overwrite output
|
||||||
vm.SetGlobal("write", core.BuiltinFunctionValue{
|
vm.SetGlobal("write", &core.BuiltinFunctionValue{
|
||||||
Name: "write",
|
Name: "write",
|
||||||
Parameters: []string{"value"},
|
Parameters: []string{"value"},
|
||||||
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
||||||
|
|
@ -105,7 +105,7 @@ func run(this js.Value, args []js.Value) interface{} {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
vm.SetGlobal("print", core.BuiltinFunctionValue{
|
vm.SetGlobal("print", &core.BuiltinFunctionValue{
|
||||||
Name: "print",
|
Name: "print",
|
||||||
Parameters: []string{"value"},
|
Parameters: []string{"value"},
|
||||||
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
||||||
|
|
@ -115,7 +115,7 @@ func run(this js.Value, args []js.Value) interface{} {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
for vm.HasNext() && vm.Next() {
|
for vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Finished executing")
|
log.Println("Finished executing")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue