separate number into float and integer

This commit is contained in:
Neemek 2026-07-07 23:24:44 +02:00
parent 10f55313b0
commit daab50d54c
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
22 changed files with 848 additions and 428 deletions

View file

@ -1,6 +1,7 @@
package core package core
import ( import (
"math/big"
"testing" "testing"
) )
@ -16,13 +17,13 @@ func GetAllTestCases() map[string]AllTestCase {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{1}, &IntegerValue{new(big.Int).SetInt64(1)},
0, 0,
}, },
}, },
}, },
"func": { "func": {
"func sum(a: number, b: number) number {\n\treturn a + b\n}\n_ = sum(1, 2)", "fn sum(a: int, b: int) -> int {\n\treturn a + b\n}\n_ = sum(1, 2)",
[]Value{ []Value{
&VariableValue{ &VariableValue{
"sum", "sum",
@ -31,11 +32,11 @@ func GetAllTestCases() map[string]AllTestCase {
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &IntegerSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &IntegerSignature{},
}, },
}, },
Chunk: &Chunk{ Chunk: &Chunk{
@ -43,7 +44,7 @@ func GetAllTestCases() map[string]AllTestCase {
InstructionDescend, InstructionDescend,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionAdd, InstructionAddInt,
InstructionReturn, InstructionReturn,
InstructionAscend, InstructionAscend,
}, },
@ -55,14 +56,14 @@ func GetAllTestCases() map[string]AllTestCase {
}, },
}, },
"list": { "list": {
"a := [1, 2]", "a := [1.0, 2.0]",
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
}, },
}, },
0, 0,
@ -76,9 +77,9 @@ func GetAllTestCases() map[string]AllTestCase {
"a", "a",
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &IntegerValue{big.NewInt(1)},
&NumberValue{2}, &IntegerValue{big.NewInt(2)},
&NumberValue{3}, &IntegerValue{big.NewInt(3)},
}, },
}, },
0, 0,
@ -86,14 +87,14 @@ func GetAllTestCases() map[string]AllTestCase {
}, },
}, },
"list_concat": { "list_concat": {
"a := [1, 2]\nb := a + [3]", "a := [1.0, 2.0]\nb := a + [3.0]",
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
}, },
}, },
0, 0,
@ -102,9 +103,9 @@ func GetAllTestCases() map[string]AllTestCase {
"b", "b",
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
&NumberValue{3}, &FloatValue{3},
}, },
}, },
0, 0,

View file

@ -2,6 +2,7 @@ package core
import ( import (
"fmt" "fmt"
"math/big"
"strings" "strings"
) )
@ -191,9 +192,13 @@ func (c *Compiler) compile(tree Node) error {
tree.(*StringNode).value, tree.(*StringNode).value,
}) })
case NumberNodeType: case FloatNodeType:
c.add(InstructionConstant) c.add(InstructionConstant)
c.addConstant(&NumberValue{tree.(*NumberNode).value}) c.addConstant(&FloatValue{tree.(*FloatNode).value})
case IntegerNodeType:
c.add(InstructionConstant)
c.addConstant(&IntegerValue{tree.(*IntegerNode).value})
case ListNodeType: case ListNodeType:
l := tree.(*ListNode) l := tree.(*ListNode)
@ -243,9 +248,19 @@ func (c *Compiler) compile(tree Node) error {
return err return err
} }
vt, err := c.deduceSignature(tree.(*UnaryNode).value)
if err != nil {
return err
}
switch tree.(*UnaryNode).UnaryOperation { switch tree.(*UnaryNode).UnaryOperation {
case UnaryNegate: case UnaryNegate:
c.add(InstructionNegate) if vt.Type() == TypeInteger {
c.add(InstructionNegateInt)
} else {
c.add(InstructionNegateFloat)
}
case UnaryNot: case UnaryNot:
c.add(InstructionNot) c.add(InstructionNot)
} }
@ -593,38 +608,72 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
return err return err
} }
switch binary.BinaryOperation {
case BinaryAddition:
res, err := c.deduceSignature(binary) res, err := c.deduceSignature(binary)
if err != nil { if err != nil {
return err return err
} }
switch binary.BinaryOperation {
case BinaryAddition:
if res.Type() == TypeString { if res.Type() == TypeString {
c.add(InstructionStringConcatenation) c.add(InstructionStringConcatenation)
} else if res.Type() == TypeList { } else if res.Type() == TypeList {
c.add(InstructionConcatLists) c.add(InstructionConcatLists)
} else if res.Type() == TypeFloat {
c.add(InstructionAddFloat)
} else if res.Type() == TypeInteger {
c.add(InstructionAddInt)
} else { } else {
c.add(InstructionAdd) return c.error("unimplemented binary compilation", binary)
} }
case BinarySubtraction: case BinarySubtraction:
c.add(InstructionSub) if res.Type() == TypeFloat {
c.add(InstructionSubFloat)
} else {
c.add(InstructionSubInt)
}
case BinaryMultiplication: case BinaryMultiplication:
c.add(InstructionMul) if res.Type() == TypeFloat {
c.add(InstructionMulFloat)
} else {
c.add(InstructionMulInt)
}
case BinaryDivision: case BinaryDivision:
c.add(InstructionDiv) if res.Type() == TypeFloat {
c.add(InstructionDivFloat)
} else {
c.add(InstructionDivInt)
}
case BinaryEquality: case BinaryEquality:
c.add(InstructionEquals) c.add(InstructionEquals)
case BinaryInequality: case BinaryInequality:
c.add(InstructionNotEqual) c.add(InstructionNotEqual)
case BinaryLess: case BinaryLess:
c.add(InstructionLess) if res.Type() == TypeFloat {
c.add(InstructionLessFloat)
} else {
c.add(InstructionLessInt)
}
case BinaryGreater: case BinaryGreater:
c.add(InstructionGreater) if res.Type() == TypeFloat {
c.add(InstructionGreaterFloat)
} else {
c.add(InstructionGreaterInt)
}
case BinaryLessEqual: case BinaryLessEqual:
c.add(InstructionLessOrEqual) if res.Type() == TypeFloat {
c.add(InstructionLessOrEqualFloat)
} else {
c.add(InstructionLessOrEqualInt)
}
case BinaryGreaterEqual: case BinaryGreaterEqual:
c.add(InstructionGreaterOrEqual) if res.Type() == TypeFloat {
c.add(InstructionGreaterOrEqualFloat)
} else {
c.add(InstructionGreaterOrEqualInt)
}
case BinaryAnd: case BinaryAnd:
c.add(InstructionAnd) c.add(InstructionAnd)
case BinaryOr: case BinaryOr:
@ -638,8 +687,10 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
switch tree.Type() { switch tree.Type() {
case StringNodeType: case StringNodeType:
return &StringSignature{}, nil return &StringSignature{}, nil
case NumberNodeType: case FloatNodeType:
return &NumberSignature{}, nil return &FloatSignature{}, nil
case IntegerNodeType:
return &IntegerSignature{}, nil
case ReferenceNodeType: case ReferenceNodeType:
n := tree.(*ReferenceNode) n := tree.(*ReferenceNode)
sig, err := c.getVarSignature(n.name, n) sig, err := c.getVarSignature(n.name, n)
@ -695,17 +746,23 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
switch n.BinaryOperation { switch n.BinaryOperation {
case BinarySubtraction, BinaryMultiplication, BinaryDivision: case BinarySubtraction, BinaryMultiplication, BinaryDivision:
if l.Type() != TypeNumber { if l.Type() == TypeInteger {
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n) return &IntegerSignature{}, nil
} }
return &NumberSignature{}, nil if l.Type() == TypeFloat {
return &FloatSignature{}, nil
}
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
case BinaryAddition: case BinaryAddition:
switch l.Type() { switch l.Type() {
case TypeString: case TypeString:
return &StringSignature{}, nil return &StringSignature{}, nil
case TypeNumber: case TypeInteger:
return &NumberSignature{}, nil return &IntegerSignature{}, nil
case TypeFloat:
return &FloatSignature{}, nil
case TypeList: case TypeList:
return &ListSignature{ return &ListSignature{
l.(*ListSignature).Contents, l.(*ListSignature).Contents,
@ -722,7 +779,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
case BinaryEquality, BinaryInequality: case BinaryEquality, BinaryInequality:
return &BooleanSignature{}, nil return &BooleanSignature{}, nil
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual: case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
if l.Type() != TypeNumber { if l.Type() != TypeInteger && l.Type() != TypeFloat {
return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on non-number type %s", n.BinaryOperation, l), n) return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on non-number type %s", n.BinaryOperation, l), n)
} }
@ -852,10 +909,13 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
switch n.UnaryOperation { switch n.UnaryOperation {
case UnaryNegate: case UnaryNegate:
if sig.Type() != TypeNumber { if sig.Type() == TypeFloat {
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n) return &FloatSignature{}, nil
} else if sig.Type() == TypeInteger {
return &IntegerSignature{}, nil
} }
return &NumberSignature{}, nil
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
case UnaryNot: case UnaryNot:
if sig.Type() != TypeBoolean { if sig.Type() != TypeBoolean {
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n) return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n)
@ -1040,7 +1100,7 @@ func (c *Compiler) isLocal(name string) bool {
// isTreeConstant check if a node tree is constant (predictable) // isTreeConstant check if a node tree is constant (predictable)
func (c *Compiler) isTreeConstant(tree Node) bool { func (c *Compiler) isTreeConstant(tree Node) bool {
switch tree.Type() { switch tree.Type() {
case StringNodeType, NumberNodeType, BooleanNodeType, NilNodeType: case StringNodeType, FloatNodeType, IntegerNodeType, BooleanNodeType, NilNodeType:
return true return true
case ListNodeType: case ListNodeType:
for _, item := range tree.(*ListNode).items { for _, item := range tree.(*ListNode).items {
@ -1076,8 +1136,13 @@ func (c *Compiler) compute(tree Node) (Value, error) {
n.value, n.value,
}, nil }, nil
case *NumberNode: case *FloatNode:
return &NumberValue{ return &FloatValue{
n.value,
}, nil
case *IntegerNode:
return &IntegerValue{
n.value, n.value,
}, nil }, nil
@ -1114,13 +1179,17 @@ func (c *Compiler) compute(tree Node) (Value, error) {
switch n.UnaryOperation { switch n.UnaryOperation {
case UnaryNegate: case UnaryNegate:
if v.Type() != NumberValueType { if v.Type() == FloatValueType {
return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n) return &FloatValue{
-v.(*FloatValue).Number,
}, nil
} else if v.Type() == IntegerValueType {
return &IntegerValue{
new(big.Int).Neg(v.(*IntegerValue).Number),
}, nil
} }
return &NumberValue{ return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
-v.(*NumberValue).Number,
}, nil
case UnaryNot: case UnaryNot:
if v.Type() != BoolValueType { if v.Type() != BoolValueType {
return nil, c.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n) return nil, c.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n)
@ -1180,7 +1249,7 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
// perform type check // perform type check
switch n.BinaryOperation { switch n.BinaryOperation {
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual: case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
if l.Type() != NumberValueType { if l.Type() != FloatValueType && l.Type() != IntegerValueType {
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n) return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n)
} }
case BinaryAnd, BinaryOr: case BinaryAnd, BinaryOr:
@ -1196,37 +1265,67 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
switch n.BinaryOperation { switch n.BinaryOperation {
case BinaryAddition: case BinaryAddition:
switch l.Type() { switch l.Type() {
case NumberValueType: case FloatValueType:
v = l.(*NumberValue).Number + r.(*NumberValue).Number v = l.(*FloatValue).Number + r.(*FloatValue).Number
case StringValueType: case StringValueType:
v = l.(*StringValue).Text + r.(*StringValue).Text v = l.(*StringValue).Text + r.(*StringValue).Text
case ListValueType: case ListValueType:
v = append(l.(*ListValue).Items, r.(*ListValue).Items...) v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
case IntegerValueType:
v = new(big.Int).Add(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
default: default:
return nil, c.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n) return nil, c.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
} }
case BinarySubtraction: case BinarySubtraction:
v = l.(*NumberValue).Number - r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number - r.(*FloatValue).Number
} else {
v = new(big.Int).Sub(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
}
case BinaryMultiplication: case BinaryMultiplication:
v = l.(*NumberValue).Number * r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number * r.(*FloatValue).Number
} else {
v = new(big.Int).Mul(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
}
case BinaryDivision: case BinaryDivision:
v = l.(*NumberValue).Number / r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number / r.(*FloatValue).Number
} else {
v = new(big.Int).Div(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
}
case BinaryAnd: case BinaryAnd:
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
case BinaryOr: case BinaryOr:
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean v = l.(*BoolValue).Boolean || r.(*BoolValue).Boolean
case BinaryEquality: case BinaryEquality:
v = l.Equals(r) v = l.Equals(r)
case BinaryInequality: case BinaryInequality:
v = !l.Equals(r) v = !l.Equals(r)
case BinaryLess: case BinaryLess:
v = l.(*NumberValue).Number < r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number < r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == -1
}
case BinaryGreater: case BinaryGreater:
v = l.(*NumberValue).Number > r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number > r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == 1
}
case BinaryLessEqual: case BinaryLessEqual:
v = l.(*NumberValue).Number <= r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number <= r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
}
case BinaryGreaterEqual: case BinaryGreaterEqual:
v = l.(*NumberValue).Number >= r.(*NumberValue).Number if l.Type() == FloatValueType {
v = l.(*FloatValue).Number >= r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
}
} }
return GoToValue(v), nil return GoToValue(v), nil

View file

@ -69,7 +69,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -85,7 +85,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -106,7 +106,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{0}, &FloatValue{0},
0, 0,
}, },
}, },
@ -118,7 +118,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -134,7 +134,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -155,7 +155,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{1}, &FloatValue{1},
0, 0,
}, },
}, },
@ -167,7 +167,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -183,7 +183,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -197,7 +197,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -217,7 +217,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{2}, &FloatValue{2},
0, 0,
}, },
}, },
@ -229,7 +229,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -245,7 +245,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -259,7 +259,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"a", "a",
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -279,7 +279,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{1}, &FloatValue{1},
0, 0,
}, },
}, },
@ -293,11 +293,11 @@ func GetCompileTestData() map[string]CompileTestData {
"a", "a",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -314,7 +314,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{3}, &FloatValue{3},
0, 0,
}, },
}, },
@ -331,14 +331,14 @@ func GetCompileTestData() map[string]CompileTestData {
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
&NumberSignature{}, &FloatSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -378,20 +378,20 @@ func GetCompileTestData() map[string]CompileTestData {
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
&NumberSignature{}, &FloatSignature{},
NewChunk( NewChunk(
[]Bytecode{ []Bytecode{
InstructionDescend, InstructionDescend,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionAdd, InstructionAddFloat,
InstructionReturn, InstructionReturn,
InstructionAscend, InstructionAscend,
}, },
@ -415,12 +415,12 @@ func GetCompileTestData() map[string]CompileTestData {
&FunctionNode{ &FunctionNode{
"a", "a",
[]FunctionParameter{}, []FunctionParameter{},
&NumberSignature{}, &FloatSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
"b", "b",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -462,7 +462,7 @@ func GetCompileTestData() map[string]CompileTestData {
&FunctionValue{ &FunctionValue{
"a", "a",
[]FunctionParameter{}, []FunctionParameter{},
&NumberSignature{}, &FloatSignature{},
NewChunk( NewChunk(
[]Bytecode{ []Bytecode{
InstructionDescend, InstructionDescend,
@ -473,7 +473,7 @@ func GetCompileTestData() map[string]CompileTestData {
InstructionAscend, InstructionAscend,
}, },
[]Value{ []Value{
&NumberValue{1}, &StringValue{"b"}, &FloatValue{1}, &StringValue{"b"},
}, },
), ),
nil, nil,
@ -491,8 +491,8 @@ func GetCompileTestData() map[string]CompileTestData {
name: "a", name: "a",
value: &ListNode{ value: &ListNode{
items: []Node{ items: []Node{
&NumberNode{value: 1}, &FloatNode{value: 1},
&NumberNode{value: 2}, &FloatNode{value: 2},
}, },
}, },
declare: true, declare: true,
@ -516,8 +516,8 @@ func GetCompileTestData() map[string]CompileTestData {
name: "a", name: "a",
value: &ListValue{ value: &ListValue{
Items: []Value{ Items: []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
}, },
}, },
scope: 0, scope: 0,
@ -543,7 +543,7 @@ func GetCompileTestData() map[string]CompileTestData {
name: "a", name: "a",
value: &UnaryNode{ value: &UnaryNode{
UnaryNegate, UnaryNegate,
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -559,7 +559,7 @@ func GetCompileTestData() map[string]CompileTestData {
expectedStack: []Value{ expectedStack: []Value{
&VariableValue{ &VariableValue{
name: "a", name: "a",
value: &NumberValue{-1}, value: &FloatValue{-1},
scope: 0, scope: 0,
}, },
}, },

View file

@ -28,7 +28,8 @@ const (
TokenBang TokenBang
TokenSemicolon TokenSemicolon
TokenNumber TokenInteger
TokenFloat
TokenHexadecimal TokenHexadecimal
TokenString TokenString
TokenName TokenName
@ -55,6 +56,7 @@ const (
TokenComma TokenComma
TokenDot TokenDot
TokenColon TokenColon
TokenArrow
TokenAssign TokenAssign
TokenDeclare TokenDeclare
@ -86,8 +88,10 @@ func (t TokenType) String() string {
return "slash" return "slash"
case TokenBang: case TokenBang:
return "bang" return "bang"
case TokenNumber: case TokenFloat:
return "number" return "float"
case TokenInteger:
return "integer"
case TokenString: case TokenString:
return "string" return "string"
case TokenTrue: case TokenTrue:
@ -135,7 +139,7 @@ func (t TokenType) String() string {
case TokenDeclare: case TokenDeclare:
return "declare" return "declare"
case TokenFunc: case TokenFunc:
return "func" return "fn"
case TokenReturn: case TokenReturn:
return "return" return "return"
case TokenWhile: case TokenWhile:
@ -162,6 +166,8 @@ func (t TokenType) String() string {
return "pipe" return "pipe"
case TokenHexadecimal: case TokenHexadecimal:
return "hexadecimal" return "hexadecimal"
case TokenArrow:
return "arrow"
} }
panic("UNDEFINED TOKENTYPE STRING CONVERSION") panic("UNDEFINED TOKENTYPE STRING CONVERSION")
@ -209,6 +215,9 @@ func (l *Lexer) NextToken() (Token, error) {
case '+': case '+':
return l.makeToken(TokenPlus), nil return l.makeToken(TokenPlus), nil
case '-': case '-':
if l.accept('>') {
return l.makeToken(TokenArrow), nil
}
return l.makeToken(TokenMinus), nil return l.makeToken(TokenMinus), nil
case '*': case '*':
return l.makeToken(TokenStar), nil return l.makeToken(TokenStar), nil
@ -339,7 +348,7 @@ func (l *Lexer) NextToken() (Token, error) {
return l.makeToken(TokenElse), nil return l.makeToken(TokenElse), nil
case "var": case "var":
return l.makeToken(TokenVar), nil return l.makeToken(TokenVar), nil
case "func": case "fn":
return l.makeToken(TokenFunc), nil return l.makeToken(TokenFunc), nil
case "while": case "while":
return l.makeToken(TokenWhile), nil return l.makeToken(TokenWhile), nil
@ -364,7 +373,7 @@ func (l *Lexer) NextToken() (Token, error) {
return l.makeToken(TokenHexadecimal), nil return l.makeToken(TokenHexadecimal), nil
} }
return l.makeToken(TokenNumber), nil return l.makeToken(TokenInteger), nil
} else if unicode.IsDigit(c) { } else if unicode.IsDigit(c) {
for unicode.IsDigit(l.peek()) { for unicode.IsDigit(l.peek()) {
l.advance() l.advance()
@ -375,9 +384,11 @@ func (l *Lexer) NextToken() (Token, error) {
for unicode.IsDigit(l.peek()) { for unicode.IsDigit(l.peek()) {
l.advance() l.advance()
} }
return l.makeToken(TokenFloat), nil
} }
return l.makeToken(TokenNumber), nil return l.makeToken(TokenInteger), nil
} }
return l.makeToken(TokenError), errors.New(fmt.Sprintf("invalid token %c", c)) return l.makeToken(TokenError), errors.New(fmt.Sprintf("invalid token %c", c))

View file

@ -21,23 +21,23 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"simple number(1)": { "simple number(1)": {
"1024", "1024",
[]TokenType{TokenNumber, TokenEOF}, []TokenType{TokenInteger, TokenEOF},
}, },
"simple_arithmetics(7)": { "simple_arithmetics(7)": {
"1 + 23 / 4 * 3", "1 + 23 / 4 * 3",
[]TokenType{ []TokenType{
TokenNumber, TokenPlus, TokenNumber, TokenSlash, TokenInteger, TokenPlus, TokenInteger, TokenSlash,
TokenNumber, TokenStar, TokenNumber, TokenEOF, TokenInteger, TokenStar, TokenInteger, TokenEOF,
}, },
}, },
"condition(3)": { "condition(3)": {
"a <= 200", "a <= 200",
[]TokenType{TokenName, TokenLessThanOrEqual, TokenNumber, TokenEOF}, []TokenType{TokenName, TokenLessThanOrEqual, TokenInteger, TokenEOF},
}, },
"if_statement(10)": { "if_statement(10)": {
"if a >= 200 {\n write(\"Hello world!\")\n}", "if a >= 200 {\n write(\"Hello world!\")\n}",
[]TokenType{ []TokenType{
TokenIf, TokenName, TokenGreaterThanOrEqual, TokenNumber, TokenOpenBrace, TokenIf, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenOpenBrace,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace,
TokenEOF, TokenEOF,
}, },
@ -45,7 +45,7 @@ func GetLexerTestData() map[string]LexerTestData {
"if_else_statement(20)": { "if_else_statement(20)": {
"if 23 * 2/3 > 32 {\n write(\"It is larger!\")\n} else {\n write(\"It is lower!\")\n}", "if 23 * 2/3 > 32 {\n write(\"It is larger!\")\n} else {\n write(\"It is lower!\")\n}",
[]TokenType{ []TokenType{
TokenIf, TokenNumber, TokenStar, TokenNumber, TokenSlash, TokenNumber, TokenGreaterThan, TokenNumber, TokenOpenBrace, TokenIf, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenGreaterThan, TokenInteger, TokenOpenBrace,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace,
TokenElse, TokenOpenBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace, TokenElse, TokenOpenBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace,
TokenEOF, TokenEOF,
@ -58,8 +58,8 @@ func GetLexerTestData() map[string]LexerTestData {
"full_arithmetic_equality": { "full_arithmetic_equality": {
"a + 2 == 10 * 2 / 3", "a + 2 == 10 * 2 / 3",
[]TokenType{ []TokenType{
TokenName, TokenPlus, TokenNumber, TokenEquals, TokenName, TokenPlus, TokenInteger, TokenEquals,
TokenNumber, TokenStar, TokenNumber, TokenSlash, TokenNumber, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger,
TokenEOF, TokenEOF,
}, },
}, },
@ -86,21 +86,21 @@ func GetLexerTestData() map[string]LexerTestData {
"complex_comparison": { "complex_comparison": {
"!(h__elo123 >= 1)", "!(h__elo123 >= 1)",
[]TokenType{ []TokenType{
TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenNumber, TokenCloseParenthesis, TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenCloseParenthesis,
TokenEOF, TokenEOF,
}, },
}, },
"3assignments_1condition": { "3assignments_1condition": {
"a = 8 * 32\nb = a > 256\nc = a <= 256\n!b == c", "a = 8 * 32\nb = a > 256\nc = a <= 256\n!b == c",
[]TokenType{ []TokenType{
TokenName, TokenAssign, TokenNumber, TokenStar, TokenNumber, TokenName, TokenAssign, TokenInteger, TokenStar, TokenInteger,
TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenNumber, TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenInteger,
TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenNumber, TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenInteger,
TokenBang, TokenName, TokenEquals, TokenName, TokenEOF, TokenBang, TokenName, TokenEquals, TokenName, TokenEOF,
}, },
}, },
"func": { "function": {
"func sum(a, b) {\n return a + b\n}", "fn sum(a, b) {\n return a + b\n}",
[]TokenType{ []TokenType{
TokenFunc, TokenName, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis, TokenFunc, TokenName, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis,
TokenOpenBrace, TokenReturn, TokenName, TokenPlus, TokenName, TokenCloseBrace, TokenOpenBrace, TokenReturn, TokenName, TokenPlus, TokenName, TokenCloseBrace,
@ -109,12 +109,12 @@ func GetLexerTestData() map[string]LexerTestData {
"while_loop": { "while_loop": {
"while a < 5 {\n a = a + 1\n}", "while a < 5 {\n a = a + 1\n}",
[]TokenType{ []TokenType{
TokenWhile, TokenName, TokenLessThan, TokenNumber, TokenOpenBrace, TokenWhile, TokenName, TokenLessThan, TokenInteger, TokenOpenBrace,
TokenName, TokenAssign, TokenName, TokenPlus, TokenNumber, TokenCloseBrace, TokenName, TokenAssign, TokenName, TokenPlus, TokenInteger, TokenCloseBrace,
}, },
}, },
"lambda": { "lambda": {
"sum := func(a, b) {\n" + "sum := fn(a, b) {\n" +
" return a + b\n" + " return a + b\n" +
"}", "}",
[]TokenType{ []TokenType{
@ -125,7 +125,7 @@ func GetLexerTestData() map[string]LexerTestData {
"list": { "list": {
"data := [3, 1, 4, 1]", "data := [3, 1, 4, 1]",
[]TokenType{ []TokenType{
TokenName, TokenDeclare, TokenOpenBracket, TokenNumber, TokenComma, TokenNumber, TokenComma, TokenNumber, TokenComma, TokenNumber, TokenCloseBracket, TokenName, TokenDeclare, TokenOpenBracket, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenCloseBracket,
}, },
}, },
} }

View file

@ -2,6 +2,7 @@ package core
import ( import (
"fmt" "fmt"
"math/big"
"strconv" "strconv"
"strings" "strings"
) )
@ -21,7 +22,8 @@ type Boundary interface {
const ( const (
StringNodeType NodeType = iota StringNodeType NodeType = iota
NumberNodeType FloatNodeType
IntegerNodeType
ReferenceNodeType ReferenceNodeType
BooleanNodeType BooleanNodeType
NilNodeType NilNodeType
@ -43,8 +45,10 @@ func (n NodeType) String() string {
switch n { switch n {
case StringNodeType: case StringNodeType:
return "String" return "String"
case NumberNodeType: case FloatNodeType:
return "Number" return "Float"
case IntegerNodeType:
return "Integer"
case ReferenceNodeType: case ReferenceNodeType:
return "Reference" return "Reference"
case BinaryNodeType: case BinaryNodeType:
@ -120,22 +124,41 @@ func (n StringNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
type NumberNode struct { type FloatNode struct {
value float64 value float64
start Pos start Pos
end Pos end Pos
} }
func (n NumberNode) Type() NodeType { func (n FloatNode) Type() NodeType {
return NumberNodeType return FloatNodeType
} }
func (n NumberNode) String() string { func (n FloatNode) String() string {
return strconv.FormatFloat(n.value, 'g', -1, NumberSize) return strconv.FormatFloat(n.value, 'g', -1, FloatSize)
} }
func (n NumberNode) Bounds() (Pos, Pos) { func (n FloatNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
type IntegerNode struct {
value *big.Int
start Pos
end Pos
}
func (n IntegerNode) Type() NodeType {
return IntegerNodeType
}
func (n IntegerNode) String() string {
return n.value.String()
}
func (n IntegerNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"math/big"
"strconv" "strconv"
"strings" "strings"
) )
@ -203,10 +204,11 @@ func (p *Parser) advance() {
if p.pos < Pos(len(p.tokens)) { if p.pos < Pos(len(p.tokens)) {
p.curr = &p.tokens[p.pos] p.curr = &p.tokens[p.pos]
p.pos++
} else { } else {
panic("no more tokens") p.curr = nil
} }
p.pos++
} }
func (p *Parser) error(error string, causer *Token) error { func (p *Parser) error(error string, causer *Token) error {
@ -229,15 +231,29 @@ func (p *Parser) factor() (Node, error) {
p.prev.Start + p.prev.Length, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenNumber: case TokenInteger:
p.advance() p.advance()
num, err := strconv.ParseFloat((*p.prev).Lexeme, NumberSize)
num, success := new(big.Int).SetString(p.prev.Lexeme, 10)
if !success {
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.prev.Lexeme), p.prev)
}
return &IntegerNode{
num,
p.prev.Start,
p.prev.Start + p.prev.Length,
}, nil
case TokenFloat:
p.advance()
num, err := strconv.ParseFloat((*p.prev).Lexeme, FloatSize)
if err != nil { if err != nil {
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev) return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev)
} }
return &NumberNode{ return &FloatNode{
num, num,
p.prev.Start, p.prev.Start,
p.prev.Start + p.prev.Length, p.prev.Start + p.prev.Length,
@ -246,13 +262,13 @@ func (p *Parser) factor() (Node, error) {
case TokenHexadecimal: case TokenHexadecimal:
p.advance() p.advance()
start := (*p.prev).Start start := (*p.prev).Start
num, err := strconv.ParseUint((*p.prev).Lexeme[2:], 16, NumberSize) num, ok := new(big.Int).SetString(p.prev.Lexeme[2:], 16)
if err != nil { if !ok {
return nil, err return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.prev.Lexeme), p.prev)
} }
return &NumberNode{ return &IntegerNode{
float64(num), num,
start, start,
p.prev.Start + p.prev.Length, p.prev.Start + p.prev.Length,
}, nil }, nil
@ -280,6 +296,8 @@ func (p *Parser) factor() (Node, error) {
p.advance() p.advance()
start := p.prev.Start start := p.prev.Start
// TODO: find better solution; current one is messy
// Maybe perform better analysis to determine the kind of the list...
if p.accept(TokenCloseBracket) { if p.accept(TokenCloseBracket) {
s, err := p.parseSignature() s, err := p.parseSignature()
if err != nil { if err != nil {
@ -390,7 +408,7 @@ func (p *Parser) factor() (Node, error) {
} }
var sig TypeSignature = &NilSignature{} var sig TypeSignature = &NilSignature{}
if p.curr.Type != TokenOpenBrace { if p.accept(TokenArrow) {
sig, err = p.parseSignature() sig, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
@ -744,7 +762,7 @@ func (p *Parser) statement() (Node, error) {
} }
var yield TypeSignature = &NilSignature{} var yield TypeSignature = &NilSignature{}
if p.curr.Type != TokenOpenBrace { if p.accept(TokenArrow) {
yield, err = p.parseSignature() yield, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
@ -983,8 +1001,10 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
switch name { switch name {
case "string": case "string":
s = &StringSignature{} s = &StringSignature{}
case "number": case "int":
s = &NumberSignature{} s = &IntegerSignature{}
case "float":
s = &FloatSignature{}
case "boolean": case "boolean":
s = &BooleanSignature{} s = &BooleanSignature{}
case "list": case "list":

View file

@ -55,9 +55,9 @@ func GetTokenTestData() map[string]TokenTestData {
[]Token{ []Token{
NewToken(TokenName, 0, 1, 0, "_"), NewToken(TokenName, 0, 1, 0, "_"),
NewToken(TokenAssign, 1, 1, 0, "="), NewToken(TokenAssign, 1, 1, 0, "="),
NewToken(TokenNumber, 3, 1, 0, "1"), NewToken(TokenFloat, 3, 1, 0, "1"),
NewToken(TokenPlus, 4, 1, 0, "+"), NewToken(TokenPlus, 4, 1, 0, "+"),
NewToken(TokenNumber, 5, 1, 0, "2"), NewToken(TokenFloat, 5, 1, 0, "2"),
NewToken(TokenEOF, 6, 0, 0, ""), NewToken(TokenEOF, 6, 0, 0, ""),
}, },
&BlockNode{ &BlockNode{
@ -66,11 +66,11 @@ func GetTokenTestData() map[string]TokenTestData {
"_", "_",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -110,7 +110,7 @@ func GetTokenTestData() map[string]TokenTestData {
[]Token{ []Token{
NewToken(TokenName, 0, 1, 0, "a"), NewToken(TokenName, 0, 1, 0, "a"),
NewToken(TokenDeclare, 1, 2, 0, ":="), NewToken(TokenDeclare, 1, 2, 0, ":="),
NewToken(TokenNumber, 3, 1, 0, "1"), NewToken(TokenFloat, 3, 1, 0, "1"),
NewToken(TokenPlus, 4, 1, 0, "+"), NewToken(TokenPlus, 4, 1, 0, "+"),
NewToken(TokenName, 5, 1, 0, "b"), NewToken(TokenName, 5, 1, 0, "b"),
NewToken(TokenEOF, 6, 0, 0, ""), NewToken(TokenEOF, 6, 0, 0, ""),
@ -121,7 +121,7 @@ func GetTokenTestData() map[string]TokenTestData {
"a", "a",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -145,28 +145,28 @@ func GetTokenTestData() map[string]TokenTestData {
NewToken(TokenAssign, 1, 2, 0, "="), NewToken(TokenAssign, 1, 2, 0, "="),
NewToken(TokenOpenParenthesis, 3, 1, 0, "("), NewToken(TokenOpenParenthesis, 3, 1, 0, "("),
NewToken(TokenNumber, 4, 1, 0, "2"), NewToken(TokenFloat, 4, 1, 0, "2"),
NewToken(TokenPlus, 5, 1, 0, "+"), NewToken(TokenPlus, 5, 1, 0, "+"),
NewToken(TokenNumber, 6, 1, 0, "1"), NewToken(TokenFloat, 6, 1, 0, "1"),
NewToken(TokenCloseParenthesis, 7, 1, 0, ")"), NewToken(TokenCloseParenthesis, 7, 1, 0, ")"),
NewToken(TokenStar, 8, 1, 0, "*"), NewToken(TokenStar, 8, 1, 0, "*"),
NewToken(TokenNumber, 9, 1, 0, "5"), NewToken(TokenFloat, 9, 1, 0, "5"),
NewToken(TokenPlus, 10, 1, 0, "+"), NewToken(TokenPlus, 10, 1, 0, "+"),
NewToken(TokenNumber, 11, 1, 0, "3"), NewToken(TokenFloat, 11, 1, 0, "3"),
NewToken(TokenSlash, 12, 1, 0, "/"), NewToken(TokenSlash, 12, 1, 0, "/"),
NewToken(TokenOpenParenthesis, 13, 1, 0, "("), NewToken(TokenOpenParenthesis, 13, 1, 0, "("),
NewToken(TokenNumber, 14, 1, 0, "6"), NewToken(TokenFloat, 14, 1, 0, "6"),
NewToken(TokenMinus, 15, 1, 0, "-"), NewToken(TokenMinus, 15, 1, 0, "-"),
NewToken(TokenNumber, 16, 1, 0, "2"), NewToken(TokenFloat, 16, 1, 0, "2"),
NewToken(TokenCloseParenthesis, 17, 1, 0, ")"), NewToken(TokenCloseParenthesis, 17, 1, 0, ")"),
NewToken(TokenMinus, 18, 1, 0, "-"), NewToken(TokenMinus, 18, 1, 0, "-"),
NewToken(TokenNumber, 19, 2, 0, "10"), NewToken(TokenFloat, 19, 2, 0, "10"),
NewToken(TokenSlash, 20, 1, 0, "/"), NewToken(TokenSlash, 20, 1, 0, "/"),
NewToken(TokenNumber, 21, 1, 0, "2"), NewToken(TokenFloat, 21, 1, 0, "2"),
NewToken(TokenEOF, 22, 0, 0, ""), NewToken(TokenEOF, 22, 0, 0, ""),
}, },
// (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2 // (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2
@ -182,17 +182,17 @@ func GetTokenTestData() map[string]TokenTestData {
BinaryMultiplication, BinaryMultiplication,
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
5, 5,
0, 0, 0, 0,
}, },
@ -200,17 +200,17 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&BinaryNode{ &BinaryNode{
BinaryDivision, BinaryDivision,
&NumberNode{ &FloatNode{
3, 3,
0, 0, 0, 0,
}, },
&BinaryNode{ &BinaryNode{
BinarySubtraction, BinarySubtraction,
&NumberNode{ &FloatNode{
6, 6,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -222,11 +222,11 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&BinaryNode{ &BinaryNode{
BinaryDivision, BinaryDivision,
&NumberNode{ &FloatNode{
10, 10,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, },
@ -245,9 +245,9 @@ func GetTokenTestData() map[string]TokenTestData {
[]Token{ []Token{
NewToken(TokenName, 0, 1, 0, "_"), NewToken(TokenName, 0, 1, 0, "_"),
NewToken(TokenAssign, 1, 1, 0, "="), NewToken(TokenAssign, 1, 1, 0, "="),
NewToken(TokenNumber, 2, 2, 0, "20"), NewToken(TokenFloat, 2, 2, 0, "20"),
NewToken(TokenEquals, 4, 2, 0, "=="), NewToken(TokenEquals, 4, 2, 0, "=="),
NewToken(TokenNumber, 6, 2, 0, "15"), NewToken(TokenFloat, 6, 2, 0, "15"),
NewToken(TokenEOF, 8, 0, 0, ""), NewToken(TokenEOF, 8, 0, 0, ""),
}, },
&BlockNode{ &BlockNode{
@ -256,11 +256,11 @@ func GetTokenTestData() map[string]TokenTestData {
"_", "_",
&BinaryNode{ &BinaryNode{
BinaryEquality, BinaryEquality,
&NumberNode{ &FloatNode{
20, 20,
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
15, 15,
0, 0, 0, 0,
}, },
@ -278,11 +278,11 @@ func GetTokenTestData() map[string]TokenTestData {
NewToken(TokenIf, 0, 2, 0, "if"), NewToken(TokenIf, 0, 2, 0, "if"),
NewToken(TokenName, 2, 1, 0, "a"), NewToken(TokenName, 2, 1, 0, "a"),
NewToken(TokenEquals, 3, 2, 0, "=="), NewToken(TokenEquals, 3, 2, 0, "=="),
NewToken(TokenNumber, 5, 1, 0, "0"), NewToken(TokenFloat, 5, 1, 0, "0"),
NewToken(TokenOpenBrace, 6, 1, 0, "{"), NewToken(TokenOpenBrace, 6, 1, 0, "{"),
NewToken(TokenName, 7, 1, 1, "b"), NewToken(TokenName, 7, 1, 1, "b"),
NewToken(TokenAssign, 8, 1, 1, "="), NewToken(TokenAssign, 8, 1, 1, "="),
NewToken(TokenNumber, 9, 1, 1, "1"), NewToken(TokenFloat, 9, 1, 1, "1"),
NewToken(TokenCloseBrace, 10, 1, 2, "}"), NewToken(TokenCloseBrace, 10, 1, 2, "}"),
NewToken(TokenEOF, 11, 0, 2, ""), NewToken(TokenEOF, 11, 0, 2, ""),
}, },
@ -295,7 +295,7 @@ func GetTokenTestData() map[string]TokenTestData {
"a", "a",
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -305,7 +305,7 @@ func GetTokenTestData() map[string]TokenTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"b", "b",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -325,17 +325,17 @@ func GetTokenTestData() map[string]TokenTestData {
NewToken(TokenIf, 0, 2, 0, "if"), NewToken(TokenIf, 0, 2, 0, "if"),
NewToken(TokenName, 2, 1, 0, "a"), NewToken(TokenName, 2, 1, 0, "a"),
NewToken(TokenEquals, 3, 2, 0, "=="), NewToken(TokenEquals, 3, 2, 0, "=="),
NewToken(TokenNumber, 5, 1, 0, "0"), NewToken(TokenFloat, 5, 1, 0, "0"),
NewToken(TokenOpenBrace, 6, 1, 0, "{"), NewToken(TokenOpenBrace, 6, 1, 0, "{"),
NewToken(TokenName, 7, 1, 1, "b"), NewToken(TokenName, 7, 1, 1, "b"),
NewToken(TokenAssign, 8, 1, 1, "="), NewToken(TokenAssign, 8, 1, 1, "="),
NewToken(TokenNumber, 9, 1, 1, "1"), NewToken(TokenFloat, 9, 1, 1, "1"),
NewToken(TokenCloseBrace, 10, 1, 2, "}"), NewToken(TokenCloseBrace, 10, 1, 2, "}"),
NewToken(TokenElse, 11, 4, 2, "else"), NewToken(TokenElse, 11, 4, 2, "else"),
NewToken(TokenOpenBrace, 15, 1, 2, "{"), NewToken(TokenOpenBrace, 15, 1, 2, "{"),
NewToken(TokenName, 16, 1, 2, "b"), NewToken(TokenName, 16, 1, 2, "b"),
NewToken(TokenAssign, 17, 1, 2, "="), NewToken(TokenAssign, 17, 1, 2, "="),
NewToken(TokenNumber, 18, 1, 2, "0"), NewToken(TokenFloat, 18, 1, 2, "0"),
NewToken(TokenCloseBrace, 19, 1, 2, "}"), NewToken(TokenCloseBrace, 19, 1, 2, "}"),
NewToken(TokenEOF, 20, 0, 2, ""), NewToken(TokenEOF, 20, 0, 2, ""),
}, },
@ -348,7 +348,7 @@ func GetTokenTestData() map[string]TokenTestData {
"a", "a",
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -358,7 +358,7 @@ func GetTokenTestData() map[string]TokenTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"b", "b",
&NumberNode{ &FloatNode{
1, 1,
0, 0, 0, 0,
}, },
@ -372,7 +372,7 @@ func GetTokenTestData() map[string]TokenTestData {
[]Node{ []Node{
&AssignNode{ &AssignNode{
"b", "b",
&NumberNode{ &FloatNode{
0, 0,
0, 0, 0, 0,
}, },
@ -403,21 +403,22 @@ func GetTokenTestData() map[string]TokenTestData {
0, 0, 0, 0,
}, },
}, },
"lambda": { // a := func(a, b) { return a + b } "lambda": { // a := fn(a: float, b: float) -> float { return a + b }
[]Token{ []Token{
NewToken(TokenName, 0, 1, 0, "a"), NewToken(TokenName, 0, 1, 0, "a"),
NewToken(TokenDeclare, 1, 2, 0, ":="), NewToken(TokenDeclare, 1, 2, 0, ":="),
NewToken(TokenFunc, 3, 4, 0, "func"), NewToken(TokenFunc, 3, 2, 0, "fn"),
NewToken(TokenOpenParenthesis, 7, 1, 0, "("), NewToken(TokenOpenParenthesis, 5, 1, 0, "("),
NewToken(TokenName, 8, 1, 0, "a"), NewToken(TokenName, 6, 1, 0, "a"),
NewToken(TokenColon, 9, 1, 0, ":"), NewToken(TokenColon, 6, 1, 0, ":"),
NewToken(TokenName, 10, 5, 0, "number"), NewToken(TokenName, 10, 5, 0, "float"),
NewToken(TokenComma, 9, 1, 0, ","), NewToken(TokenComma, 9, 1, 0, ","),
NewToken(TokenName, 10, 1, 0, "b"), NewToken(TokenName, 10, 1, 0, "b"),
NewToken(TokenColon, 9, 1, 0, ":"), NewToken(TokenColon, 9, 1, 0, ":"),
NewToken(TokenName, 10, 5, 0, "number"), NewToken(TokenName, 10, 5, 0, "float"),
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"), NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
NewToken(TokenName, 10, 5, 0, "number"), NewToken(TokenArrow, 12, 1, 0, "->"),
NewToken(TokenName, 10, 5, 0, "float"),
NewToken(TokenOpenBrace, 12, 1, 1, "{"), NewToken(TokenOpenBrace, 12, 1, 1, "{"),
NewToken(TokenReturn, 13, 6, 1, "return"), NewToken(TokenReturn, 13, 6, 1, "return"),
@ -437,14 +438,14 @@ func GetTokenTestData() map[string]TokenTestData {
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
&NumberSignature{}, &FloatSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -476,13 +477,19 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
"function_declaration": { "function_declaration": {
[]Token{ []Token{
NewToken(TokenFunc, 0, 4, 0, "func"), NewToken(TokenFunc, 0, 2, 0, "fn"),
NewToken(TokenName, 4, 3, 0, "a"), NewToken(TokenName, 4, 3, 0, "a"),
NewToken(TokenOpenParenthesis, 7, 1, 0, "("), NewToken(TokenOpenParenthesis, 7, 1, 0, "("),
NewToken(TokenName, 8, 1, 0, "a"), NewToken(TokenName, 8, 1, 0, "a"),
NewToken(TokenColon, 9, 1, 0, ":"),
NewToken(TokenName, 8, 1, 0, "float"),
NewToken(TokenComma, 9, 1, 0, ","), NewToken(TokenComma, 9, 1, 0, ","),
NewToken(TokenName, 10, 1, 0, "b"), NewToken(TokenName, 10, 1, 0, "b"),
NewToken(TokenColon, 9, 1, 0, ":"),
NewToken(TokenName, 8, 1, 0, "float"),
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"), NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
NewToken(TokenArrow, 9, 1, 0, "->"),
NewToken(TokenName, 8, 1, 0, "float"),
NewToken(TokenOpenBrace, 12, 1, 1, "{"), NewToken(TokenOpenBrace, 12, 1, 1, "{"),
NewToken(TokenReturn, 13, 6, 1, "return"), NewToken(TokenReturn, 13, 6, 1, "return"),
@ -502,14 +509,14 @@ func GetTokenTestData() map[string]TokenTestData {
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
&NumberSignature{}, &FloatSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -577,7 +584,7 @@ func GetTokenTestData() map[string]TokenTestData {
NewToken(TokenName, 8, 1, 0, "a"), NewToken(TokenName, 8, 1, 0, "a"),
NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenComma, 11, 1, 0, ","),
NewToken(TokenNumber, 8, 1, 0, "3.141"), NewToken(TokenFloat, 8, 1, 0, "3.141"),
NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenComma, 11, 1, 0, ","),
NewToken(TokenString, 6, 1, 0, "\"Hello world!\""), NewToken(TokenString, 6, 1, 0, "\"Hello world!\""),
@ -587,10 +594,10 @@ func GetTokenTestData() map[string]TokenTestData {
NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenComma, 11, 1, 0, ","),
NewToken(TokenOpenBracket, 6, 1, 0, "["), NewToken(TokenOpenBracket, 6, 1, 0, "["),
NewToken(TokenNumber, 6, 1, 0, "2"), NewToken(TokenFloat, 6, 1, 0, "2"),
NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenComma, 11, 1, 0, ","),
NewToken(TokenNumber, 6, 1, 0, "3"), NewToken(TokenFloat, 6, 1, 0, "3"),
NewToken(TokenCloseBracket, 6, 1, 0, "]"), NewToken(TokenCloseBracket, 6, 1, 0, "]"),
NewToken(TokenCloseBracket, 6, 1, 0, "]"), NewToken(TokenCloseBracket, 6, 1, 0, "]"),
@ -607,7 +614,7 @@ func GetTokenTestData() map[string]TokenTestData {
"a", "a",
0, 0, 0, 0,
}, },
&NumberNode{ &FloatNode{
3.141, 3.141,
0, 0, 0, 0,
}, },
@ -622,10 +629,10 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&ListNode{ &ListNode{
[]Node{ []Node{
&NumberNode{ &FloatNode{
2, 2,
0, 0, 0, 0,
}, &NumberNode{ }, &FloatNode{
3, 3,
0, 0, 0, 0,
}, },
@ -677,12 +684,20 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
t.Logf("String node quoted values match (%s)", n1.(*StringNode).value) t.Logf("String node quoted values match (%s)", n1.(*StringNode).value)
} }
case NumberNodeType: case FloatNodeType:
if n1.(*NumberNode).value != n2.(*NumberNode).value { if n1.(*FloatNode).value != n2.(*FloatNode).value {
t.Errorf("Number node values don't match (%f and %f)", n1.(*NumberNode).value, n2.(*NumberNode).value) t.Errorf("Float node values don't match (%f and %f)", n1.(*FloatNode).value, n2.(*FloatNode).value)
} else { } else {
t.Logf("Number node values match (%f)", n1.(*NumberNode).value) t.Logf("Float node values match (%f)", n1.(*FloatNode).value)
} }
case IntegerNodeType:
if n1.(*IntegerNode).value.Cmp(n2.(*IntegerNode).value) != 0 {
t.Errorf("Integer node values don't match (%d and %d)", n1.(*IntegerNode).value, n2.(*IntegerNode).value)
} else {
t.Logf("Integer node values match (%d)", n1.(*IntegerNode).value)
}
case ReferenceNodeType: case ReferenceNodeType:
if n1.(*ReferenceNode).name != n2.(*ReferenceNode).name { if n1.(*ReferenceNode).name != n2.(*ReferenceNode).name {
t.Errorf("Reference node values don't match (%s and %s)", n1.(*ReferenceNode).name, n2.(*ReferenceNode).name) t.Errorf("Reference node values don't match (%s and %s)", n1.(*ReferenceNode).name, n2.(*ReferenceNode).name)
@ -795,6 +810,37 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
case ReturnNodeType: case ReturnNodeType:
NodeEquality(t, n1.(*ReturnNode).value, n2.(*ReturnNode).value) NodeEquality(t, n1.(*ReturnNode).value, n2.(*ReturnNode).value)
case AccessNodeType:
a1 := n1.(*AccessNode)
a2 := n2.(*AccessNode)
if a1.property != a2.property {
t.Errorf("Access node property does not match: .%s != .%s", a1.property, a2.property)
} else {
t.Logf("Access node property matches: .%s", a1.property)
}
NodeEquality(t, a1.source, a2.source)
case ListNodeType:
l1 := n1.(*ListNode)
l2 := n2.(*ListNode)
if l1.content == nil && l2.content == nil {
// fine
t.Logf("Both content types are yet to be determined")
} else if l1.content != nil || l2.content != nil {
t.Errorf("one is nil, one is not")
} else if !l1.content.Matches(l2.content) {
t.Errorf("signature doesn't match")
}
for i, v1 := range l1.items {
t.Logf("Checking item %d", i)
NodeEquality(t, v1, l2.items[i])
}
default: default:
panic("unimplemented node equality") panic("unimplemented node equality")
} }
@ -818,7 +864,7 @@ func SerializeTokens(tokens []Token) string {
out.WriteString("!") out.WriteString("!")
case TokenSemicolon: case TokenSemicolon:
out.WriteString(";") out.WriteString(";")
case TokenNumber: case TokenFloat:
out.WriteString(token.Lexeme) out.WriteString(token.Lexeme)
case TokenString: case TokenString:
out.WriteString(fmt.Sprintf("\"%s\"", token.Lexeme)) out.WriteString(fmt.Sprintf("\"%s\"", token.Lexeme))
@ -845,7 +891,7 @@ func SerializeTokens(tokens []Token) string {
case TokenNil: case TokenNil:
out.WriteString("nil") out.WriteString("nil")
case TokenFunc: case TokenFunc:
out.WriteString("func") out.WriteString("fn")
case TokenReturn: case TokenReturn:
out.WriteString("return ") out.WriteString("return ")
case TokenWhile: case TokenWhile:
@ -904,10 +950,6 @@ func TestParser_Parse(t *testing.T) {
tokenData := GetTokenTestData() tokenData := GetTokenTestData()
for name, data := range tokenData { for name, data := range tokenData {
if name != "empty_block" && name != "lambda" {
continue
}
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Logf("Initializing parser") t.Logf("Initializing parser")
p := NewParser("", []string{}, data.tokens) p := NewParser("", []string{}, data.tokens)
@ -916,7 +958,7 @@ func TestParser_Parse(t *testing.T) {
tree, err := p.Parse("") tree, err := p.Parse("")
if err != nil { if err != nil {
t.Fatalf("Unexpected error(s): %s", err.(ParsingError).Format()) t.Fatalf("Unexpected error(s): %s", err.(ParsingError).Description)
} }
t.Logf("Checking parsed tree") t.Logf("Checking parsed tree")

View file

@ -7,10 +7,10 @@ type Stack[T any] struct {
items []T items []T
} }
func NewStack[T any](capacity Pos) *Stack[T] { func NewStack[T any](maxCapacity Pos) *Stack[T] {
return &Stack[T]{ return &Stack[T]{
items: make([]T, 16), items: make([]T, min(maxCapacity, 16)),
Capacity: capacity, Capacity: maxCapacity,
Current: 0, Current: 0,
} }
} }

View file

@ -9,7 +9,8 @@ type Type int
const ( const (
TypeString Type = iota TypeString Type = iota
TypeNumber TypeInteger
TypeFloat
TypeBoolean TypeBoolean
TypeNil TypeNil
TypeList TypeList
@ -24,8 +25,10 @@ func (t Type) String() string {
switch t { switch t {
case TypeString: case TypeString:
return "string" return "string"
case TypeNumber: case TypeInteger:
return "number" return "integer"
case TypeFloat:
return "float"
case TypeBoolean: case TypeBoolean:
return "boolean" return "boolean"
case TypeNil: case TypeNil:
@ -51,8 +54,10 @@ func SignatureOf(v Value) TypeSignature {
switch t := v.(type) { switch t := v.(type) {
case *StringValue: case *StringValue:
return &StringSignature{} return &StringSignature{}
case *NumberValue: case *FloatValue:
return &NumberSignature{} return &FloatSignature{}
case *IntegerValue:
return &IntegerSignature{}
case *BoolValue: case *BoolValue:
return &BooleanSignature{} return &BooleanSignature{}
case *ListValue: case *ListValue:
@ -138,22 +143,40 @@ func (*StringSignature) String() string {
return "string" return "string"
} }
type NumberSignature struct{} type FloatSignature struct{}
func (*NumberSignature) Type() Type { func (*FloatSignature) Type() Type {
return TypeNumber return TypeFloat
} }
func (s *NumberSignature) Matches(other TypeSignature) bool { func (s *FloatSignature) Matches(other TypeSignature) bool {
if other.Type() == TypeComposite { if other.Type() == TypeComposite {
return other.Matches(s) return other.Matches(s)
} }
return other.Type() == TypeAny || other.Type() == TypeNumber return other.Type() == TypeAny || other.Type() == TypeFloat
} }
func (*NumberSignature) String() string { func (*FloatSignature) String() string {
return "number" return "float"
}
type IntegerSignature struct{}
func (*IntegerSignature) Type() Type {
return TypeInteger
}
func (s *IntegerSignature) Matches(other TypeSignature) bool {
if other.Type() == TypeComposite {
return other.Matches(s)
}
return other.Type() == TypeAny || other.Type() == TypeInteger
}
func (*IntegerSignature) String() string {
return "int"
} }
type BooleanSignature struct{} type BooleanSignature struct{}

View file

@ -3,7 +3,7 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"math" "math/big"
"reflect" "reflect"
"strconv" "strconv"
) )
@ -13,7 +13,8 @@ type ValueType int
const ( const (
NilValueType ValueType = iota NilValueType ValueType = iota
BoolValueType BoolValueType
NumberValueType FloatValueType
IntegerValueType
StringValueType StringValueType
ListValueType ListValueType
ObjectValueType ObjectValueType
@ -30,8 +31,10 @@ func (v ValueType) String() string {
return "bool" return "bool"
case ObjectValueType: case ObjectValueType:
return "object" return "object"
case NumberValueType: case FloatValueType:
return "number" return "float"
case IntegerValueType:
return "int"
case StringValueType: case StringValueType:
return "string" return "string"
case ListValueType: case ListValueType:
@ -57,11 +60,15 @@ func GoToValue(gov interface{}) Value {
v, v,
} }
case int: case int:
return &NumberValue{ return &IntegerValue{
float64(v), new(big.Int).SetInt64(int64(v)),
}
case *big.Int:
return &IntegerValue{
v,
} }
case float64: case float64:
return &NumberValue{ return &FloatValue{
v, v,
} }
case string: case string:
@ -259,40 +266,71 @@ func (v *ObjectValue) Clone() Value {
} }
} }
// NumberValue Integer or floating-point values // FloatValue floating-point values
type NumberValue struct { type FloatValue struct {
Number float64 Number float64
} }
const NumberSize int = 64 const FloatSize int = 64
func (v *NumberValue) Type() ValueType { func (v *FloatValue) Type() ValueType {
return NumberValueType return FloatValueType
} }
func (v *NumberValue) String() string { func (v *FloatValue) String() string {
return strconv.FormatFloat(v.Number, 'g', -1, NumberSize) return strconv.FormatFloat(v.Number, 'g', -1, FloatSize)
} }
func (v *NumberValue) DebugString() string { func (v *FloatValue) DebugString() string {
return v.String() return v.String()
} }
func (v *NumberValue) Equals(other Value) bool { func (v *FloatValue) Equals(other Value) bool {
return other.Type() == NumberValueType && other.(*NumberValue).Number == v.Number return other.Type() == FloatValueType && other.(*FloatValue).Number == v.Number
} }
func (v *NumberValue) Get(_ string) (Value, error) { func (v *FloatValue) Get(_ string) (Value, error) {
// TODO maybe add standard functions for number values? // TODO maybe add standard functions for number values?
return nil, errors.New("numbers have no properties") return nil, errors.New("numbers have no properties")
} }
func (v *NumberValue) Clone() Value { func (v *FloatValue) Clone() Value {
return &NumberValue{ return &FloatValue{
v.Number, v.Number,
} }
} }
// IntegerValue whole number/integer values
type IntegerValue struct {
Number *big.Int
}
func (v *IntegerValue) Type() ValueType {
return IntegerValueType
}
func (v *IntegerValue) String() string {
return v.Number.String()
}
func (v *IntegerValue) DebugString() string {
return v.String()
}
func (v *IntegerValue) Equals(other Value) bool {
return other.Type() == IntegerValueType && other.(*IntegerValue).Number.Cmp(v.Number) == 0
}
func (v *IntegerValue) Get(_ string) (Value, error) {
return nil, errors.New("numbers have no properties")
}
func (v *IntegerValue) Clone() Value {
return &IntegerValue{
new(big.Int).Set(v.Number),
}
}
type StringValue struct { type StringValue struct {
Text string Text string
} }
@ -348,7 +386,7 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
Name: "length", Name: "length",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
[]TypeSignature{}, []TypeSignature{},
&NumberSignature{}, &IntegerSignature{},
}, },
F: func(vm *VM, this Value, _ []Value) (Value, error) { F: func(vm *VM, this Value, _ []Value) (Value, error) {
return GoToValue(len(this.(*StringValue).Text)), nil return GoToValue(len(this.(*StringValue).Text)), nil
@ -357,11 +395,11 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
"at": { "at": {
Name: "at", Name: "at",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
[]TypeSignature{&NumberSignature{}}, []TypeSignature{&IntegerSignature{}},
&StringSignature{}, &StringSignature{},
}, },
F: func(vm *VM, this Value, args []Value) (Value, error) { F: func(vm *VM, this Value, args []Value) (Value, error) {
i := int(math.Floor(args[0].(*NumberValue).Number)) i := int(args[0].(*IntegerValue).Number.Int64())
if i < 0 || i >= len(this.(*StringValue).Text) { if i < 0 || i >= len(this.(*StringValue).Text) {
return nil, errors.New("index is out of range") return nil, errors.New("index is out of range")
@ -450,13 +488,13 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"at", "at",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{ []TypeSignature{
&NumberSignature{}, &IntegerSignature{},
}, },
&InnerSignature{}, &InnerSignature{},
}, },
func(_ *VM, this Value, p []Value) (Value, error) { func(_ *VM, this Value, p []Value) (Value, error) {
items := this.(*ListValue).Items items := this.(*ListValue).Items
index := int(p[0].(*NumberValue).Number) index := int(p[0].(*IntegerValue).Number.Int64())
if index >= len(items) { if index >= len(items) {
return nil, errors.New(fmt.Sprintf("list index %x out of range", index)) return nil, errors.New(fmt.Sprintf("list index %x out of range", index))
@ -471,13 +509,13 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"put", "put",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{ []TypeSignature{
&NumberSignature{}, &InnerSignature{}, &FloatSignature{}, &InnerSignature{},
}, },
&NilSignature{}, &NilSignature{},
}, },
func(_ *VM, this Value, args []Value) (Value, error) { func(_ *VM, this Value, args []Value) (Value, error) {
l := this.(*ListValue) l := this.(*ListValue)
i := int(args[0].(*NumberValue).Number) i := int(args[0].(*FloatValue).Number)
v := args[1] v := args[1]
// bounds check // bounds check
@ -496,7 +534,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"length", "length",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{}, []TypeSignature{},
&NumberSignature{}, &IntegerSignature{},
}, },
func(_ *VM, this Value, _ []Value) (Value, error) { func(_ *VM, this Value, _ []Value) (Value, error) {
return GoToValue(len(this.(*ListValue).Items)), nil return GoToValue(len(this.(*ListValue).Items)), nil

View file

@ -21,15 +21,21 @@ func CompareValues(t *testing.T, got Value, want Value) {
} else { } else {
t.Logf("Both are same boolean (%s)", want.(*BoolValue).String()) t.Logf("Both are same boolean (%s)", want.(*BoolValue).String())
} }
case NumberValueType: case FloatValueType:
if got.(*NumberValue).Number != want.(*NumberValue).Number { if got.(*FloatValue).Number != want.(*FloatValue).Number {
t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue)) t.Errorf("number value mismatch: got %v, want %v", got.(*FloatValue), want.(*FloatValue))
} else { } else {
t.Logf("Both are same number (%s)", got.(*NumberValue).String()) t.Logf("Both are same number (%s)", got.(*FloatValue).String())
}
case IntegerValueType:
if got.(*IntegerValue).Number.String() != want.(*IntegerValue).Number.String() {
t.Errorf("number value mismatch: got %v, want %v", got.(*IntegerValue), want.(*IntegerValue))
} else {
t.Logf("Both are same number (%s)", got.(*IntegerValue).String())
} }
case StringValueType: case StringValueType:
if got.(*StringValue).Text != want.(*StringValue).Text { if got.(*StringValue).Text != want.(*StringValue).Text {
t.Errorf("string value mismatch: got %v, want %v", got.(*StringValue), want.(*StringValue)) t.Errorf("string value mismatch: got %s, want %s", got.(*StringValue), want.(*StringValue))
} else { } else {
t.Logf("Both are same string (%s)", got.(*StringValue).String()) t.Logf("Both are same string (%s)", got.(*StringValue).String())
} }

View file

@ -7,7 +7,9 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"math/big"
"os" "os"
"strconv"
"strings" "strings"
) )
@ -20,30 +22,52 @@ const (
// InstructionPop pop and delete the first item on the stack // InstructionPop pop and delete the first item on the stack
InstructionPop InstructionPop
// InstructionAdd pop two and add them // InstructionAddFloat pop two floats and add them
InstructionAdd InstructionAddFloat
// InstructionSub pop two and subtract the second from the first // InstructionSubFloat pop two floats and subtract the second from the first
InstructionSub InstructionSubFloat
// InstructionMul pop two and multiply them // InstructionMulFloat pop two floats and multiply them
InstructionMul InstructionMulFloat
// InstructionDiv pop two and divide the second by the first // InstructionDivFloat pop two floats and divide the second by the first
InstructionDiv InstructionDivFloat
// InstructionNegate negate the value; if it was positive, make it negative, and vice versa. // InstructionNegateFloat negate the float; if it was positive, make it negative, and vice versa.
InstructionNegate InstructionNegateFloat
// InstructionAddInt pop two ints and add them
InstructionAddInt
// InstructionSubInt pop two ints and subtract the second from the first
InstructionSubInt
// InstructionMulInt pop two ints and multiply them
InstructionMulInt
// InstructionDivInt pop two ints and divide the second by the first
InstructionDivInt
// InstructionNegateInt negate the int; if it was positive, make it negative, and vice versa.
InstructionNegateInt
// InstructionEquals whether the two top values on the stack are equal // InstructionEquals whether the two top values on the stack are equal
InstructionEquals InstructionEquals
// InstructionNotEqual whether the two top values on the stack are not equal // InstructionNotEqual whether the two top values on the stack are not equal
InstructionNotEqual InstructionNotEqual
// InstructionNot inverts boolean (true => false, false => true) // InstructionNot inverts boolean (true => false, false => true)
InstructionNot InstructionNot
// InstructionLess pops two from stack, pushes whether the lowest is less than the highest
InstructionLess // InstructionLessFloat pops two from stack, pushes whether the lowest is less than the highest
// InstructionLessOrEqual pops two from stack, pushes whether the lowest is less or equal than the highest InstructionLessFloat
InstructionLessOrEqual // InstructionLessOrEqualFloat pops two from stack, pushes whether the lowest is less or equal than the highest
// InstructionGreater pops two from stack, pushes whether the lowest is greater than the highest InstructionLessOrEqualFloat
InstructionGreater // InstructionGreaterFloat pops two from stack, pushes whether the lowest is greater than the highest
// InstructionGreaterOrEqual pops two from stack, pushes whether the lowest is greater or equal than the highest InstructionGreaterFloat
InstructionGreaterOrEqual // InstructionGreaterOrEqualFloat pops two from stack, pushes whether the lowest is greater or equal than the highest
InstructionGreaterOrEqualFloat
// InstructionLessInt pops two from stack, pushes whether the lowest is less than the highest
InstructionLessInt
// InstructionLessOrEqualInt pops two from stack, pushes whether the lowest is less or equal than the highest
InstructionLessOrEqualInt
// InstructionGreaterInt pops two from stack, pushes whether the lowest is greater than the highest
InstructionGreaterInt
// InstructionGreaterOrEqualInt pops two from stack, pushes whether the lowest is greater or equal than the highest
InstructionGreaterOrEqualInt
// InstructionAccessProperty gets a property from a value, and pops it onto the stack // InstructionAccessProperty gets a property from a value, and pops it onto the stack
InstructionAccessProperty InstructionAccessProperty
@ -116,30 +140,48 @@ func (b Bytecode) String() string {
return "RETURN" return "RETURN"
case InstructionPop: case InstructionPop:
return "POP" return "POP"
case InstructionAdd: case InstructionAddFloat:
return "ADD" return "ADD_FLOAT"
case InstructionSub: case InstructionSubFloat:
return "SUB" return "SUB_FLOAT"
case InstructionMul: case InstructionMulFloat:
return "MUL" return "MUL_FLOAT"
case InstructionDiv: case InstructionDivFloat:
return "DIV" return "DIV_FLOAT"
case InstructionNegate: case InstructionNegateFloat:
return "NEGATE" return "NEGATE_FLOAT"
case InstructionAddInt:
return "ADD_INT"
case InstructionSubInt:
return "SUB_INT"
case InstructionMulInt:
return "MUL_INT"
case InstructionDivInt:
return "DIV_INT"
case InstructionNegateInt:
return "NEGATE_INT"
case InstructionEquals: case InstructionEquals:
return "EQUALS" return "EQUALS"
case InstructionNotEqual: case InstructionNotEqual:
return "NOT_EQUALS" return "NOT_EQUALS"
case InstructionNot: case InstructionNot:
return "NOT" return "NOT"
case InstructionLess: case InstructionLessFloat:
return "LESS" return "LESS_FLOAT"
case InstructionLessOrEqual: case InstructionLessOrEqualFloat:
return "LESS_OR_EQUAL" return "LESS_OR_EQUAL_FLOAT"
case InstructionGreater: case InstructionGreaterFloat:
return "GREATER_OR_EQUAL" return "GREATER_FLOAT"
case InstructionGreaterOrEqual: case InstructionGreaterOrEqualFloat:
return "GREATER_OR_EQUAL" return "GREATER_OR_EQUAL_FLOAT"
case InstructionLessInt:
return "LESS_INT"
case InstructionLessOrEqualInt:
return "LESS_OR_EQUAL_INT"
case InstructionGreaterInt:
return "GREATER_INT"
case InstructionGreaterOrEqualInt:
return "GREATER_OR_EQUAL_INT"
case InstructionJump: case InstructionJump:
return "JUMP" return "JUMP"
case InstructionJumpFalse: case InstructionJumpFalse:
@ -232,7 +274,7 @@ func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
func RegisterGOBTypes() { func RegisterGOBTypes() {
gob.Register(&StringValue{""}) gob.Register(&StringValue{""})
gob.Register(&BoolValue{false}) gob.Register(&BoolValue{false})
gob.Register(&NumberValue{0}) gob.Register(&FloatValue{0})
gob.Register(&FunctionValue{ gob.Register(&FunctionValue{
Name: "", Name: "",
Params: nil, Params: nil,
@ -241,7 +283,7 @@ func RegisterGOBTypes() {
// Signatures // Signatures
gob.Register(&NilSignature{}) gob.Register(&NilSignature{})
gob.Register(&NumberSignature{}) gob.Register(&FloatSignature{})
gob.Register(&StringSignature{}) gob.Register(&StringSignature{})
gob.Register(&FunctionSignature{}) gob.Register(&FunctionSignature{})
gob.Register(&ListSignature{}) gob.Register(&ListSignature{})
@ -368,12 +410,12 @@ var DefaultGlobals = map[string]Value{
"char": &BuiltinFunctionValue{ "char": &BuiltinFunctionValue{
"char", "char",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&NumberSignature{}}, []TypeSignature{&IntegerSignature{}},
&StringSignature{}, &StringSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
n := args[0].(*NumberValue).Number n := args[0].(*IntegerValue).Number
b := byte(n) b := n.Bytes()[0]
return &StringValue{ return &StringValue{
string([]byte{b}), string([]byte{b}),
@ -383,16 +425,16 @@ var DefaultGlobals = map[string]Value{
true, true,
}, },
"byte": &BuiltinFunctionValue{ "byte": &BuiltinFunctionValue{
"char", "byte",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&StringSignature{}}, []TypeSignature{&StringSignature{}},
&NumberSignature{}, &IntegerSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
s := args[0].(*StringValue).Text s := args[0].(*StringValue).Text
b := []byte(s)[0] n := new(big.Int).SetBytes([]byte(s))
return &NumberValue{float64(b)}, nil return &IntegerValue{n}, nil
}, },
nil, nil,
true, true,
@ -453,6 +495,66 @@ var DefaultGlobals = map[string]Value{
nil, nil,
true, true,
}, },
"int": &BuiltinFunctionValue{
"int",
&FunctionSignature{
[]TypeSignature{&AnySignature{}},
&CompositeSignature{
&IntegerSignature{},
&NilSignature{},
},
},
func(vm *VM, _ Value, args []Value) (Value, error) {
switch v := args[0].(type) {
case *IntegerValue:
return &IntegerValue{v.Number}, nil // this might need to clone the value instead
case *FloatValue:
n := new(big.Int).SetInt64(int64(v.Number))
return &IntegerValue{n}, nil
case *StringValue:
n, success := new(big.Int).SetString(v.Text, 0) // determine base
if !success {
return &NilValue{}, nil
}
return &IntegerValue{n}, nil
default:
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
}
},
nil,
true,
},
"float": &BuiltinFunctionValue{
"float",
&FunctionSignature{
[]TypeSignature{&AnySignature{}},
&CompositeSignature{
&FloatSignature{},
&NilSignature{},
},
},
func(vm *VM, _ Value, args []Value) (Value, error) {
switch v := args[0].(type) {
case *IntegerValue:
n, _ := v.Number.Float64()
return &FloatValue{n}, nil // this might need to clone the value instead
case *FloatValue:
return &FloatValue{v.Number}, nil
case *StringValue:
num, err := strconv.ParseFloat(v.Text, FloatSize)
if err != nil {
return &NilValue{}, nil
}
return &FloatValue{num}, nil
default:
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
}
},
nil,
true,
},
"type": &BuiltinFunctionValue{ "type": &BuiltinFunctionValue{
Name: "type", Name: "type",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
@ -470,11 +572,11 @@ var DefaultGlobals = map[string]Value{
"exit": &BuiltinFunctionValue{ "exit": &BuiltinFunctionValue{
"exit", "exit",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&NumberSignature{}}, []TypeSignature{&FloatSignature{}},
&NilSignature{}, &NilSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
os.Exit(int(args[0].(*NumberValue).Number)) os.Exit(int(args[0].(*FloatValue).Number))
return &NilValue{}, nil return &NilValue{}, nil
}, },
nil, nil,
@ -483,11 +585,11 @@ var DefaultGlobals = map[string]Value{
"floor": &BuiltinFunctionValue{ "floor": &BuiltinFunctionValue{
"floor", "floor",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&NumberSignature{}}, []TypeSignature{&FloatSignature{}},
&NumberSignature{}, &FloatSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil return &FloatValue{math.Floor(args[0].(*FloatValue).Number)}, nil
}, },
nil, nil,
true, true,
@ -495,11 +597,11 @@ var DefaultGlobals = map[string]Value{
"ceil": &BuiltinFunctionValue{ "ceil": &BuiltinFunctionValue{
"ceil", "ceil",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&NumberSignature{}}, []TypeSignature{&FloatSignature{}},
&NumberSignature{}, &FloatSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil return &FloatValue{math.Ceil(args[0].(*FloatValue).Number)}, nil
}, },
nil, nil,
true, true,
@ -507,14 +609,14 @@ var DefaultGlobals = map[string]Value{
"roundd": &BuiltinFunctionValue{ "roundd": &BuiltinFunctionValue{
"roundd", "roundd",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&NumberSignature{}, &NumberSignature{}}, []TypeSignature{&FloatSignature{}, &FloatSignature{}},
&NumberSignature{}, &FloatSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
x := args[0].(*NumberValue).Number x := args[0].(*FloatValue).Number
decimals := args[1].(*NumberValue).Number decimals := args[1].(*FloatValue).Number
multiplier := math.Pow(10, decimals) multiplier := math.Pow(10, decimals)
return &NumberValue{math.Round(x*multiplier) / multiplier}, nil return &FloatValue{math.Round(x*multiplier) / multiplier}, nil
}, },
nil, nil,
true, true,
@ -568,34 +670,63 @@ func (vm *VM) Next() bool {
case InstructionConstant: case InstructionConstant:
vm.stack.Push(vm.ReadConstant()) vm.stack.Push(vm.ReadConstant())
case InstructionAdd: case InstructionAddFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l + r}) vm.stack.Push(&FloatValue{l + r})
case InstructionSub: case InstructionSubFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l - r}) vm.stack.Push(&FloatValue{l - r})
case InstructionMul: case InstructionMulFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l * r}) vm.stack.Push(&FloatValue{l * r})
case InstructionDiv: case InstructionDivFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{l / r}) vm.stack.Push(&FloatValue{l / r})
case InstructionNegate: case InstructionNegateFloat:
v := vm.stack.Pop().(*NumberValue).Number v := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&NumberValue{-v}) vm.stack.Push(&FloatValue{-v})
case InstructionAddInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Add(l, r)})
case InstructionSubInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Sub(l, r)})
case InstructionMulInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Mul(l, r)})
case InstructionDivInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Div(l, r)})
case InstructionNegateInt:
v := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&IntegerValue{new(big.Int).Neg(v)})
case InstructionEquals: case InstructionEquals:
vm.stack.Push( vm.stack.Push(
@ -621,30 +752,54 @@ func (vm *VM) Next() bool {
l := vm.stack.Pop().(*BoolValue).Boolean l := vm.stack.Pop().(*BoolValue).Boolean
vm.stack.Push(&BoolValue{l || r}) vm.stack.Push(&BoolValue{l || r})
case InstructionLess: case InstructionLessFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l < r}) vm.stack.Push(&BoolValue{l < r})
case InstructionLessOrEqual: case InstructionLessOrEqualFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l <= r}) vm.stack.Push(&BoolValue{l <= r})
case InstructionGreater: case InstructionGreaterFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l > r}) vm.stack.Push(&BoolValue{l > r})
case InstructionGreaterOrEqual: case InstructionGreaterOrEqualFloat:
r := vm.stack.Pop().(*NumberValue).Number r := vm.stack.Pop().(*FloatValue).Number
l := vm.stack.Pop().(*NumberValue).Number l := vm.stack.Pop().(*FloatValue).Number
vm.stack.Push(&BoolValue{l >= r}) vm.stack.Push(&BoolValue{l >= r})
case InstructionLessInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == -1})
case InstructionLessOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != 1})
case InstructionGreaterInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) == 1})
case InstructionGreaterOrEqualInt:
r := vm.stack.Pop().(*IntegerValue).Number
l := vm.stack.Pop().(*IntegerValue).Number
vm.stack.Push(&BoolValue{l.Cmp(r) != -1})
case InstructionCall: case InstructionCall:
v := vm.stack.Pop() v := vm.stack.Pop()
switch f := v.(type) { switch f := v.(type) {

View file

@ -49,7 +49,7 @@ func TestNewVM(t *testing.T) {
chunk := NewChunk([]Bytecode{ chunk := NewChunk([]Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
}, []Value{ }, []Value{
&NumberValue{0}, &FloatValue{0},
}) })
stackSize := Pos(256) stackSize := Pos(256)
callstackSize := Pos(256) callstackSize := Pos(256)
@ -104,13 +104,13 @@ func GetExecutionTestData() map[string]struct {
NewChunk([]Bytecode{ NewChunk([]Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionConstant, 1, InstructionConstant, 1,
InstructionAdd, InstructionAddFloat,
}, },
[]Value{ []Value{
&NumberValue{1}, &NumberValue{2}, &FloatValue{1}, &FloatValue{2},
}), }),
[]Value{ []Value{
&NumberValue{3}, &FloatValue{3},
}, },
}, },
"push_constant": { "push_constant": {
@ -119,11 +119,11 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 0, InstructionConstant, 0,
}, },
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
}, },
"push_true": { "push_true": {
@ -172,20 +172,20 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionConstant, 1, InstructionConstant, 1,
InstructionAdd, InstructionAddFloat,
InstructionConstant, 2, InstructionConstant, 2,
InstructionMul, InstructionMulFloat,
InstructionConstant, 3, InstructionConstant, 3,
InstructionConstant, 0, InstructionConstant, 0,
InstructionSub, InstructionSubFloat,
InstructionDiv, InstructionDivFloat,
}, },
[]Value{ []Value{
&NumberValue{2}, &NumberValue{1}, &NumberValue{5}, &NumberValue{6}, &FloatValue{2}, &FloatValue{1}, &FloatValue{5}, &FloatValue{6},
}, },
), ),
[]Value{ []Value{
&NumberValue{3.75}, &FloatValue{3.75},
}, },
}, },
"equality_true": { "equality_true": {
@ -196,7 +196,7 @@ func GetExecutionTestData() map[string]struct {
InstructionEquals, InstructionEquals,
}, },
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
@ -211,7 +211,7 @@ func GetExecutionTestData() map[string]struct {
InstructionEquals, InstructionEquals,
}, },
[]Value{ []Value{
&NumberValue{1}, &NumberValue{2}, &FloatValue{1}, &FloatValue{2},
}, },
), ),
[]Value{ []Value{
@ -226,7 +226,7 @@ func GetExecutionTestData() map[string]struct {
InstructionNotEqual, InstructionNotEqual,
}, },
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
@ -241,7 +241,7 @@ func GetExecutionTestData() map[string]struct {
InstructionNotEqual, InstructionNotEqual,
}, },
[]Value{ []Value{
&NumberValue{1}, &NumberValue{2}, &FloatValue{1}, &FloatValue{2},
}, },
), ),
[]Value{ []Value{
@ -280,11 +280,11 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &FloatValue{0}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
}, },
"jump_false/false": { "jump_false/false": {
@ -296,11 +296,11 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &FloatValue{0}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
}, },
}, },
"jump_false/true": { "jump_false/true": {
@ -312,11 +312,11 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &FloatValue{0}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &FloatValue{0}, &FloatValue{1},
}, },
}, },
"declare_local": { "declare_local": {
@ -326,13 +326,13 @@ func GetExecutionTestData() map[string]struct {
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
}, },
[]Value{ []Value{
&NumberValue{0}, &StringValue{"a"}, &FloatValue{0}, &StringValue{"a"},
}, },
), ),
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{0}, &FloatValue{0},
0, 0,
}, },
}, },
@ -346,13 +346,13 @@ func GetExecutionTestData() map[string]struct {
InstructionSetLocal, 1, // reassign InstructionSetLocal, 1, // reassign
}, },
[]Value{ []Value{
&NumberValue{0}, &StringValue{"a"}, &NumberValue{1}, &FloatValue{0}, &StringValue{"a"}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{1}, &FloatValue{1},
0, 0,
}, },
}, },
@ -365,16 +365,16 @@ func GetExecutionTestData() map[string]struct {
InstructionGetLocal, 1, // reassign InstructionGetLocal, 1, // reassign
}, },
[]Value{ []Value{
&NumberValue{0}, &StringValue{"a"}, &FloatValue{0}, &StringValue{"a"},
}, },
), ),
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{0}, &FloatValue{0},
0, 0,
}, },
&NumberValue{0}, &FloatValue{0},
}, },
}, },
"get_reassigned_local": { "get_reassigned_local": {
@ -388,17 +388,17 @@ func GetExecutionTestData() map[string]struct {
InstructionGetLocal, 1, InstructionGetLocal, 1,
}, },
[]Value{ []Value{
&NumberValue{0}, &StringValue{"a"}, &NumberValue{1}, &FloatValue{0}, &StringValue{"a"}, &FloatValue{1},
}, },
), ),
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{1}, &FloatValue{1},
0, 0,
}, },
&NumberValue{0}, &FloatValue{0},
&NumberValue{1}, &FloatValue{1},
}, },
}, },
"variable_scope": { "variable_scope": {
@ -416,15 +416,15 @@ func GetExecutionTestData() map[string]struct {
InstructionAscend, InstructionAscend,
}, },
[]Value{ []Value{
&NumberValue{0}, &StringValue{"a"}, &FloatValue{0}, &StringValue{"a"},
&NumberValue{1}, &StringValue{"b"}, &FloatValue{1}, &StringValue{"b"},
&NumberValue{2}, &StringValue{"c"}, &FloatValue{2}, &StringValue{"c"},
}, },
), ),
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&NumberValue{0}, &FloatValue{0},
0, 0,
}, },
}, },
@ -438,25 +438,25 @@ func GetExecutionTestData() map[string]struct {
InstructionCall, InstructionCall,
}, },
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
&FunctionValue{ &FunctionValue{
Name: "sum", Name: "sum",
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionAdd, InstructionAddFloat,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -467,7 +467,7 @@ func GetExecutionTestData() map[string]struct {
}, },
), ),
[]Value{ []Value{
&NumberValue{3}, &FloatValue{3},
}, },
}, },
"function_calling_function": { "function_calling_function": {
@ -481,18 +481,18 @@ func GetExecutionTestData() map[string]struct {
InstructionCall, InstructionCall,
}, },
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
&FunctionValue{ &FunctionValue{
Name: "sum", Name: "sum",
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"a", "a",
&NumberSignature{}, &FloatSignature{},
}, },
{ {
"b", "b",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
@ -501,7 +501,7 @@ func GetExecutionTestData() map[string]struct {
InstructionGetLocal, 2, InstructionCall, // square the number InstructionGetLocal, 2, InstructionCall, // square the number
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionGetLocal, 2, InstructionCall, // square the number InstructionGetLocal, 2, InstructionCall, // square the number
InstructionAdd, InstructionAddFloat,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -514,14 +514,14 @@ func GetExecutionTestData() map[string]struct {
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"n", "n",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionMul, InstructionMulFloat,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -540,14 +540,14 @@ func GetExecutionTestData() map[string]struct {
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"n", "n",
&NumberSignature{}, &FloatSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionMul, InstructionMulFloat,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -557,7 +557,7 @@ func GetExecutionTestData() map[string]struct {
}, },
0, 0,
}, },
&NumberValue{5}, &FloatValue{5},
}, },
}, },
"list_concat": { "list_concat": {
@ -570,13 +570,13 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
}, },
}, },
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{3}, &FloatValue{3},
}, },
}, },
}, },
@ -584,9 +584,9 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&ListValue{ &ListValue{
[]Value{ []Value{
&NumberValue{1}, &FloatValue{1},
&NumberValue{2}, &FloatValue{2},
&NumberValue{3}, &FloatValue{3},
}, },
}, },
}, },
@ -630,7 +630,7 @@ func TestVM_NextByte(t *testing.T) {
InstructionConstant, 0, InstructionConstant, 0,
}, },
[]Value{ []Value{
&NumberValue{0}, &FloatValue{0},
}, },
), ),
16, 16,
@ -741,7 +741,7 @@ func TestVM_Jump(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &NumberValue{2}, &FloatValue{0}, &FloatValue{1}, &FloatValue{2},
}, },
), ),
16, 16,
@ -766,7 +766,7 @@ func TestVM_JumpFalse(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &NumberValue{2}, &FloatValue{0}, &FloatValue{1}, &FloatValue{2},
}, },
), ),
16, 16,
@ -792,7 +792,7 @@ func TestVM_DontJumpFalse(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&NumberValue{0}, &NumberValue{1}, &NumberValue{2}, &FloatValue{0}, &FloatValue{1}, &FloatValue{2},
}, },
), ),
16, 16,

6
era3.ang Normal file
View file

@ -0,0 +1,6 @@
fn double(a: int) -> int {
2*a
}
println(double(2) == 4)

View file

@ -1,5 +1,5 @@
assertEq(1+1, 2) assertEq(1+1, 2)
assertEq(3*2, 6) assertEq(3*2, 6)
assertEq(3/4, 0.75) assertEq(3.0/4.0, 0.75)
assertEq(2 - 5, -3) assertEq(2 - 5, -3)

View file

@ -3,7 +3,7 @@ assertEq(1, 1)
assertEq(0, 0) assertEq(0, 0)
assertEq("", "") assertEq("", "")
assertEq([]number, []number) assertEq([]int, []int)
assertEq([3, 1, 4, 1], [3, 1, 4, 1]) assertEq([3, 1, 4, 1], [3, 1, 4, 1])
# Inequality # Inequality

View file

@ -1,20 +1,20 @@
list := []number list := []int
x := 1 x := 1
while x <= 1000 { while x <= 1000 {
list.append(x) list.append(x)
assertEq(list.reduce(func(tot: number, a: number) number { assertEq(list.reduce(fn(tot: int, a: int) -> int {
return tot + a return tot + a
}, 0), x*(x + 1)/2) }, 0), x*(x + 1)/2)
x = x + 1 x = x + 1
} }
func sum(a: number, b: number) number { fn sum(a: int, b: int) -> int {
return a + b return a + b
} }
list = []number list = []int
x = 1 x = 1
while x <= 100 { while x <= 100 {
list.append(2*x - 1) list.append(2*x - 1)

View file

@ -3,7 +3,7 @@ fibonacci_numbers := [
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377
] ]
func fib(n: number) number { fn fib(n: int) -> int {
if n < 2 { if n < 2 {
return n return n
} }

View file

@ -1,5 +1,5 @@
list := []number list := []int
list.append(1) list.append(1)
list.append(2) list.append(2)

View file

@ -1,12 +1,8 @@
func sum(a: number, b: number) number { fn sum(a: int, b: int) -> int {
return a + b return a + b
} }
breakpoint
assertEq(sum(1, 2), 3) assertEq(sum(1, 2), 3)
breakpoint
assertEq(sum(3, 3), 6) assertEq(sum(3, 3), 6)
breakpoint

View file

@ -1,8 +1,8 @@
assertEq(type(1), "number") assertEq(type(1), "int")
assertEq(type("Hello"), "string") assertEq(type("Hello"), "string")
assertEq(type(true), "boolean") assertEq(type(true), "boolean")
# lists # lists
assertEq(type(["Hello", "world"]), "list[string]") assertEq(type(["Hello", "world"]), "list[string]")
assertEq(type([0, 1]), "list[number]") assertEq(type([0, 1]), "list[int]")