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