Fix values

This commit is contained in:
Neemek 2025-03-16 20:09:36 +01:00
parent ae7d6c5359
commit 5edad332cd
Signed by: neemek
GPG key ID: 28360A8951CD0E9B
19 changed files with 611 additions and 331 deletions

View file

@ -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

View file

@ -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() {
}
}
})

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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)

View file

@ -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

View file

@ -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,
},

View file

@ -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
}
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 {
for key, value := range v.members {
if !object.members[key].Equals(value) {
return false
}
}
return true
}
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
}
var ListPrototype = map[string]BuiltinFunctionValue{
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{
"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")
}

View file

@ -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)

View file

@ -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 {

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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
}

View file

@ -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("")

View file

@ -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)

View file

@ -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

View file

@ -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")