Compare commits

..

No commits in common. "2c354c4a967e64309a5719b4a3785eba2daa8b6a" and "10f55313b097c924de6c31e9ee2c3b80e6597c50" have entirely different histories.

30 changed files with 967 additions and 1956 deletions

View file

@ -2,4 +2,4 @@ module neemek.com/anglais/cli
go 1.25 go 1.25
require github.com/alecthomas/kong v1.15.0 require github.com/alecthomas/kong v1.12.1

View file

@ -4,10 +4,7 @@ github.com/alecthomas/kong v1.5.1 h1:9quB93P2aNGXf5C1kWNei85vjBgITNJQA4dSwJQGCOY
github.com/alecthomas/kong v1.5.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/kong v1.5.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0= github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0=
github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/kong v1.15.0 h1:BVJstKbpO73zKpmIu+m/aLRrNmWwxXPIGTNin9VmLVI=
github.com/alecthomas/kong v1.15.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=

View file

@ -3,14 +3,13 @@ package main
import ( import (
"bufio" "bufio"
"errors" "errors"
"github.com/alecthomas/kong"
"log" "log"
"neemek.com/anglais/core"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/alecthomas/kong"
"neemek.com/anglais/core"
) )
type Context struct { type Context struct {
@ -307,8 +306,6 @@ func (cmd *ReplCmd) Run(ctx *Context) error {
vm.SetChunk(c.Chunk) vm.SetChunk(c.Chunk)
for vm.Next() { for vm.Next() {
} }
println(vm.Stack.Pop().DebugString())
} }
} }

View file

@ -1,73 +1,113 @@
package core package core
import ( import (
"math/big"
"testing" "testing"
) )
type AllTestCase struct { type AllTestCase struct {
src string src string
expectedStack []Value expectedStack []Value
expectedScope []map[string]Value
} }
func GetAllTestCases() map[string]AllTestCase { func GetAllTestCases() map[string]AllTestCase {
return map[string]AllTestCase{ return map[string]AllTestCase{
"constant_number": { "constant_number": {
"a := 1", "a := 1",
[]Value{&IntegerValue{new(big.Int).SetInt64(1)}}, []Value{
[]map[string]Value{ &VariableValue{
{"a": &IntegerValue{new(big.Int).SetInt64(1)}}, "a",
&NumberValue{1},
0,
},
}, },
}, },
"func": { "func": {
"fn sum(a: int, b: int) -> int {\n\treturn a + b\n}\nres := sum(1, 2)", "func sum(a: number, b: number) number {\n\treturn a + b\n}\n_ = sum(1, 2)",
[]Value{&IntegerValue{new(big.Int).SetInt64(3)}}, []Value{
[]map[string]Value{}, &VariableValue{
"sum",
&FunctionValue{
Name: "sum",
Params: []FunctionParameter{
{
"a",
&NumberSignature{},
},
{
"b",
&NumberSignature{},
},
},
Chunk: &Chunk{
Bytecode: []Bytecode{
InstructionDescend,
InstructionGetLocal, 0,
InstructionGetLocal, 1,
InstructionAdd,
InstructionReturn,
InstructionAscend,
},
Constants: []Value{&StringValue{"a"}, &StringValue{"b"}},
},
},
0,
},
},
}, },
"list": { "list": {
"a := [1.0, 2.0]\n{}", "a := [1, 2]",
[]Value{&NilValue{}},
[]map[string]Value{
{"a": &ListValue{
[]Value{ []Value{
&FloatValue{1}, &VariableValue{
&FloatValue{2}, "a",
&ListValue{
[]Value{
&NumberValue{1},
&NumberValue{2},
},
},
0,
}, },
}},
}, },
}, },
"constant_list_concat": { "constant_list_concat": {
"a := [1, 2] + [3]\n{}", "a := [1, 2] + [3]",
[]Value{&NilValue{}},
[]map[string]Value{
{"a": &ListValue{
[]Value{ []Value{
&IntegerValue{big.NewInt(1)}, &VariableValue{
&IntegerValue{big.NewInt(2)}, "a",
&IntegerValue{big.NewInt(3)}, &ListValue{
[]Value{
&NumberValue{1},
&NumberValue{2},
&NumberValue{3},
},
},
0,
}, },
}},
}, },
}, },
"list_concat": { "list_concat": {
"a := [1.0, 2.0]\nb := a + [3.0]\nnil", "a := [1, 2]\nb := a + [3]",
[]Value{&NilValue{}},
[]map[string]Value{
{
"a": &ListValue{
[]Value{ []Value{
&FloatValue{1}, &VariableValue{
&FloatValue{2}, "a",
}, &ListValue{
},
"b": &ListValue{
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
&FloatValue{3},
}, },
}, },
0,
},
&VariableValue{
"b",
&ListValue{
[]Value{
&NumberValue{1},
&NumberValue{2},
&NumberValue{3},
},
},
0,
}, },
}, },
}, },
@ -119,13 +159,7 @@ func TestAll(t *testing.T) {
} }
t.Log("Comparing stacks") t.Log("Comparing stacks")
CompareStacks(t, tc.expectedStack, vm.stack)
CompareStacks(t, tc.expectedStack, vm.Stack)
// expected scope == nil => we don't care
if tc.expectedScope != nil {
CompareScope(t, tc.expectedScope, vm.scope)
}
}) })
} }
} }

View file

@ -2,7 +2,6 @@ package core
import ( import (
"fmt" "fmt"
"math/big"
"strings" "strings"
) )
@ -18,9 +17,6 @@ type Compiler struct {
source []rune source []rune
Warnings []CompilerError Warnings []CompilerError
// optimize Whether to attempt some optimization of the emitted bytecode
optimize bool
stack *Stack[LocalVariable] stack *Stack[LocalVariable]
} }
@ -132,7 +128,6 @@ func NewCompiler(source []rune) *Compiler {
nil, nil,
source, source,
[]CompilerError{}, []CompilerError{},
false,
NewStack[LocalVariable](256), NewStack[LocalVariable](256),
} }
@ -173,14 +168,10 @@ func (c *Compiler) Compile(p *Program) error {
} }
} }
for i, s := range p.Block.statements { for _, s := range p.Block.statements {
if err := c.compile(s); err != nil { if err := c.compile(s); err != nil {
return err return err
} }
if i != len(p.Block.statements)-1 {
c.add(InstructionPop)
}
} }
c.fileStack.Pop() c.fileStack.Pop()
@ -200,20 +191,16 @@ func (c *Compiler) compile(tree Node) error {
tree.(*StringNode).value, tree.(*StringNode).value,
}) })
case FloatNodeType: case NumberNodeType:
c.add(InstructionConstant) c.add(InstructionConstant)
c.addConstant(&FloatValue{tree.(*FloatNode).value}) c.addConstant(&NumberValue{tree.(*NumberNode).value})
case IntegerNodeType:
c.add(InstructionConstant)
c.addConstant(&IntegerValue{tree.(*IntegerNode).value})
case ListNodeType: case ListNodeType:
l := tree.(*ListNode) l := tree.(*ListNode)
if len(l.items) == 0 { if len(l.items) == 0 {
c.add(InstructionNewList) c.add(InstructionNewList)
} else if c.optimize && c.isTreeConstant(l) { } else if c.isTreeConstant(l) {
v, err := c.compute(l) v, err := c.compute(l)
if err != nil { if err != nil {
panic(err) // this shouldn't happen panic(err) // this shouldn't happen
@ -242,7 +229,7 @@ func (c *Compiler) compile(tree Node) error {
} }
case UnaryNodeType: case UnaryNodeType:
if c.optimize && c.isTreeConstant(tree.(*UnaryNode).value) { if c.isTreeConstant(tree.(*UnaryNode).value) {
v, err := c.compute(tree) v, err := c.compute(tree)
if err != nil { if err != nil {
return err return err
@ -256,19 +243,9 @@ 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:
if vt.Type() == TypeInteger { c.add(InstructionNegate)
c.add(InstructionNegateInt)
} else {
c.add(InstructionNegateFloat)
}
case UnaryNot: case UnaryNot:
c.add(InstructionNot) c.add(InstructionNot)
} }
@ -285,21 +262,12 @@ func (c *Compiler) compile(tree Node) error {
c.add(InstructionNil) c.add(InstructionNil)
case BlockNodeType: case BlockNodeType:
if len(tree.(*BlockNode).statements) == 0 {
c.add(InstructionNil)
return nil
}
c.addDescend() c.addDescend()
for i, n := range tree.(*BlockNode).statements { for _, n := range tree.(*BlockNode).statements {
err := c.compile(n) err := c.compile(n)
if err != nil { if err != nil {
return err return err
} }
if i != len(tree.(*BlockNode).statements)-1 {
c.add(InstructionPop)
}
} }
c.addAscend() c.addAscend()
@ -315,7 +283,7 @@ func (c *Compiler) compile(tree Node) error {
return c.error(fmt.Sprintf("conditional requires boolean; cannot use non-boolean type %s", sig), n.condition) return c.error(fmt.Sprintf("conditional requires boolean; cannot use non-boolean type %s", sig), n.condition)
} }
if c.optimize && c.isTreeConstant(n.condition) { if c.isTreeConstant(n.condition) {
v, err := c.compute(n.condition) v, err := c.compute(n.condition)
if err != nil { if err != nil {
return err return err
@ -353,10 +321,13 @@ func (c *Compiler) compile(tree Node) error {
} }
// we store the position of the jump over the else code here // we store the position of the jump over the else code here
var jumpOverElse Pos
if n.otherwise != nil {
// this would jump over the else/otherwise block in the code // this would jump over the else/otherwise block in the code
c.add(InstructionJump) c.add(InstructionJump)
jumpOverElse := c.ip jumpOverElse = c.ip
c.advance(2) c.advance(2)
}
// put the u16 of where to jump if the condition was false // put the u16 of where to jump if the condition was false
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2)) c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
@ -366,11 +337,8 @@ func (c *Compiler) compile(tree Node) error {
if err != nil { if err != nil {
return err return err
} }
} else {
c.add(InstructionNil)
}
c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2)) c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
}
case LoopNodeType: case LoopNodeType:
n := tree.(*LoopNode) n := tree.(*LoopNode)
@ -385,7 +353,7 @@ func (c *Compiler) compile(tree Node) error {
} }
alwaysLoop := false alwaysLoop := false
if c.optimize && c.isTreeConstant(n.condition) { if c.isTreeConstant(n.condition) {
v, err := c.compute(n.condition) v, err := c.compute(n.condition)
if err != nil { if err != nil {
return err return err
@ -400,8 +368,6 @@ func (c *Compiler) compile(tree Node) error {
} }
} }
c.add(InstructionNil)
conditionPos := c.ip conditionPos := c.ip
jumpValuePos := Pos(0) jumpValuePos := Pos(0)
if !alwaysLoop { if !alwaysLoop {
@ -415,8 +381,6 @@ func (c *Compiler) compile(tree Node) error {
c.advance(2) c.advance(2)
} }
c.add(InstructionPop)
err = c.compile(n.do) err = c.compile(n.do)
if err != nil { if err != nil {
return err return err
@ -433,26 +397,6 @@ func (c *Compiler) compile(tree Node) error {
case AssignNodeType: case AssignNodeType:
n := tree.(*AssignNode) n := tree.(*AssignNode)
switch n.dest.Type() {
case ReferenceNodeType:
d := n.dest.(*ReferenceNode)
if d.name == "_" {
return c.compile(n.value)
}
if n.declare && c.isVarDeclaredHere(d.name) {
return c.error(fmt.Sprintf("%s is already declared in this scope", d.name), n)
}
if err := c.addSetVar(d.name, n.value, n.declare); err != nil {
return err
}
default:
return c.error(fmt.Sprintf("cannot assign to %s", n.dest.Type()), n.dest)
}
/*
if n.name == "_" { if n.name == "_" {
// allow non-ish statements // allow non-ish statements
err := c.compile(n.value) err := c.compile(n.value)
@ -470,10 +414,9 @@ func (c *Compiler) compile(tree Node) error {
return err return err
} }
} }
*/
case InvokeNodeType: case CallNodeType:
n := tree.(*InvokeNode) n := tree.(*CallNode)
s, err := c.deduceSignature(n.source) s, err := c.deduceSignature(n.source)
if err != nil { if err != nil {
@ -485,6 +428,10 @@ func (c *Compiler) compile(tree Node) error {
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n) return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
} }
if !n.keep && f.Out.Type() != TypeNil {
c.warn(fmt.Sprintf("shouldn't void result of function call (output is non-nil %s)", f.Out), n)
}
if len(n.args) != len(f.In) { if len(n.args) != len(f.In) {
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n) return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
} }
@ -516,7 +463,7 @@ func (c *Compiler) compile(tree Node) error {
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg) return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
} }
if c.optimize && c.isTreeConstant(arg) { if c.isTreeConstant(arg) {
v, err := c.compute(arg) v, err := c.compute(arg)
if err != nil { if err != nil {
return err return err
@ -539,6 +486,10 @@ func (c *Compiler) compile(tree Node) error {
c.add(InstructionCall) c.add(InstructionCall)
if !n.keep {
c.add(InstructionPop)
}
case FunctionNodeType: case FunctionNodeType:
n := tree.(*FunctionNode) n := tree.(*FunctionNode)
@ -587,7 +538,6 @@ func (c *Compiler) compile(tree Node) error {
n.yield, n.yield,
c.Chunk, c.Chunk,
nil, nil,
nil,
} }
// restore old chunk and ip // restore old chunk and ip
@ -623,7 +573,7 @@ func (c *Compiler) compile(tree Node) error {
} }
func (c *Compiler) compileBinary(binary *BinaryNode) error { func (c *Compiler) compileBinary(binary *BinaryNode) error {
if c.optimize && c.isTreeConstant(binary) { if c.isTreeConstant(binary) {
v, err := c.compute(binary) v, err := c.compute(binary)
if err != nil { if err != nil {
return err return err
@ -643,77 +593,38 @@ 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
} }
inType, err := c.deduceSignature(binary.Left) // type(left) == type(right) because res is valid
if err != nil {
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 {
return c.error("unimplemented binary compilation", binary) c.add(InstructionAdd)
} }
case BinarySubtraction: case BinarySubtraction:
if res.Type() == TypeFloat { c.add(InstructionSub)
c.add(InstructionSubFloat)
} else {
c.add(InstructionSubInt)
}
case BinaryMultiplication: case BinaryMultiplication:
if res.Type() == TypeFloat { c.add(InstructionMul)
c.add(InstructionMulFloat)
} else {
c.add(InstructionMulInt)
}
case BinaryDivision: case BinaryDivision:
if res.Type() == TypeFloat { c.add(InstructionDiv)
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:
if inType.Type() == TypeFloat { c.add(InstructionLess)
c.add(InstructionLessFloat)
} else {
c.add(InstructionLessInt)
}
case BinaryGreater: case BinaryGreater:
if inType.Type() == TypeFloat { c.add(InstructionGreater)
c.add(InstructionGreaterFloat)
} else {
c.add(InstructionGreaterInt)
}
case BinaryLessEqual: case BinaryLessEqual:
if inType.Type() == TypeFloat { c.add(InstructionLessOrEqual)
c.add(InstructionLessOrEqualFloat)
} else {
c.add(InstructionLessOrEqualInt)
}
case BinaryGreaterEqual: case BinaryGreaterEqual:
if inType.Type() == TypeFloat { c.add(InstructionGreaterOrEqual)
c.add(InstructionGreaterOrEqualFloat)
} else {
c.add(InstructionGreaterOrEqualInt)
}
case BinaryAnd: case BinaryAnd:
c.add(InstructionAnd) c.add(InstructionAnd)
case BinaryOr: case BinaryOr:
@ -727,10 +638,8 @@ 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 FloatNodeType: case NumberNodeType:
return &FloatSignature{}, nil return &NumberSignature{}, 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)
@ -786,23 +695,17 @@ 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() == TypeInteger { if l.Type() != TypeNumber {
return &IntegerSignature{}, 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) return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
}
return &NumberSignature{}, nil
case BinaryAddition: case BinaryAddition:
switch l.Type() { switch l.Type() {
case TypeString: case TypeString:
return &StringSignature{}, nil return &StringSignature{}, nil
case TypeInteger: case TypeNumber:
return &IntegerSignature{}, nil return &NumberSignature{}, nil
case TypeFloat:
return &FloatSignature{}, nil
case TypeList: case TypeList:
return &ListSignature{ return &ListSignature{
l.(*ListSignature).Contents, l.(*ListSignature).Contents,
@ -819,7 +722,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() != TypeInteger && l.Type() != TypeFloat { if l.Type() != TypeNumber {
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)
} }
@ -865,8 +768,8 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n) return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
} }
case InvokeNodeType: case CallNodeType:
n := tree.(*InvokeNode) n := tree.(*CallNode)
sig, err := c.deduceSignature(n.source) sig, err := c.deduceSignature(n.source)
if err != nil { if err != nil {
return nil, err return nil, err
@ -949,13 +852,10 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
switch n.UnaryOperation { switch n.UnaryOperation {
case UnaryNegate: case UnaryNegate:
if sig.Type() == TypeFloat { if sig.Type() != TypeNumber {
return &FloatSignature{}, nil
} else if sig.Type() == TypeInteger {
return &IntegerSignature{}, nil
}
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n) return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
}
return &NumberSignature{}, nil
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)
@ -964,15 +864,6 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
} }
return nil, c.error(fmt.Sprintf("unimplemented result type deduction for unary %s", n.UnaryOperation), n) return nil, c.error(fmt.Sprintf("unimplemented result type deduction for unary %s", n.UnaryOperation), n)
case BlockNodeType:
n := tree.(*BlockNode)
if len(n.statements) == 0 {
return &NilSignature{}, nil
}
return c.deduceSignature(n.statements[len(n.statements)-1])
case AssignNodeType:
return c.deduceSignature(tree.(*AssignNode).value)
default: default:
return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree) return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree)
} }
@ -1038,12 +929,8 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
case AssignNodeType: case AssignNodeType:
n := tree.(*AssignNode) n := tree.(*AssignNode)
switch n.dest.Type() {
case ReferenceNodeType:
name := n.dest.(*ReferenceNode).name
if !n.declare { if !n.declare {
prev, err := c.getVarSignature(name, n) prev, err := c.getVarSignature(n.name, n)
if err != nil { if err != nil {
return err return err
} }
@ -1054,7 +941,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
} }
if !sig.Matches(prev) { if !sig.Matches(prev) {
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, name, prev), n.value) return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, n.name, prev), n.value)
} }
return nil return nil
@ -1064,11 +951,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
if err != nil { if err != nil {
return err return err
} }
c.registerVar(name, sig) c.registerVar(n.name, sig)
default:
return c.error("can neither assign nor declare to", n.dest)
}
default: default:
} }
@ -1157,7 +1040,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, FloatNodeType, IntegerNodeType, BooleanNodeType, NilNodeType: case StringNodeType, NumberNodeType, BooleanNodeType, NilNodeType:
return true return true
case ListNodeType: case ListNodeType:
for _, item := range tree.(*ListNode).items { for _, item := range tree.(*ListNode).items {
@ -1171,13 +1054,13 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
return c.isTreeConstant(tree.(*UnaryNode).value) return c.isTreeConstant(tree.(*UnaryNode).value)
case BinaryNodeType: case BinaryNodeType:
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right) return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
case InvokeNodeType: case CallNodeType:
for _, arg := range tree.(*InvokeNode).args { for _, arg := range tree.(*CallNode).args {
if !c.isTreeConstant(arg) { if !c.isTreeConstant(arg) {
return false return false
} }
} }
return c.isTreeConstant(tree.(*InvokeNode).source) return c.isTreeConstant(tree.(*CallNode).source)
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType, case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType: ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
return false return false
@ -1193,13 +1076,8 @@ func (c *Compiler) compute(tree Node) (Value, error) {
n.value, n.value,
}, nil }, nil
case *FloatNode: case *NumberNode:
return &FloatValue{ return &NumberValue{
n.value,
}, nil
case *IntegerNode:
return &IntegerValue{
n.value, n.value,
}, nil }, nil
@ -1236,17 +1114,13 @@ func (c *Compiler) compute(tree Node) (Value, error) {
switch n.UnaryOperation { switch n.UnaryOperation {
case UnaryNegate: case UnaryNegate:
if v.Type() == FloatValueType { if v.Type() != NumberValueType {
return &FloatValue{ return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
-v.(*FloatValue).Number,
}, nil
} else if v.Type() == IntegerValueType {
return &IntegerValue{
new(big.Int).Neg(v.(*IntegerValue).Number),
}, nil
} }
return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n) return &NumberValue{
-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)
@ -1259,7 +1133,7 @@ func (c *Compiler) compute(tree Node) (Value, error) {
return nil, c.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n) return nil, c.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
case *InvokeNode: case *CallNode:
source, err := c.compute(n.source) source, err := c.compute(n.source)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1306,7 +1180,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() != FloatValueType && l.Type() != IntegerValueType { if l.Type() != NumberValueType {
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:
@ -1322,67 +1196,37 @@ 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 FloatValueType: case NumberValueType:
v = l.(*FloatValue).Number + r.(*FloatValue).Number v = l.(*NumberValue).Number + r.(*NumberValue).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:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number - r.(*NumberValue).Number
v = l.(*FloatValue).Number - r.(*FloatValue).Number
} else {
v = new(big.Int).Sub(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
}
case BinaryMultiplication: case BinaryMultiplication:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number * r.(*NumberValue).Number
v = l.(*FloatValue).Number * r.(*FloatValue).Number
} else {
v = new(big.Int).Mul(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
}
case BinaryDivision: case BinaryDivision:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number / r.(*NumberValue).Number
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:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number < r.(*NumberValue).Number
v = l.(*FloatValue).Number < r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == -1
}
case BinaryGreater: case BinaryGreater:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number > r.(*NumberValue).Number
v = l.(*FloatValue).Number > r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == 1
}
case BinaryLessEqual: case BinaryLessEqual:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number <= r.(*NumberValue).Number
v = l.(*FloatValue).Number <= r.(*FloatValue).Number
} else {
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
}
case BinaryGreaterEqual: case BinaryGreaterEqual:
if l.Type() == FloatValueType { v = l.(*NumberValue).Number >= r.(*NumberValue).Number
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

@ -30,7 +30,6 @@ func BenchmarkNewCompiler(b *testing.B) {
type CompileTestData struct { type CompileTestData struct {
program *Program program *Program
expectedStack []Value expectedStack []Value
expectedScope []map[string]Value
} }
func GetCompileTestData() map[string]CompileTestData { func GetCompileTestData() map[string]CompileTestData {
@ -41,7 +40,7 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&StringNode{ &StringNode{
"Hello world!", "Hello world!",
"\"Hello world!\"", "\"Hello world!\"",
@ -55,22 +54,22 @@ func GetCompileTestData() map[string]CompileTestData {
}, },
"", "",
}, },
[]Value{&StringValue{"Hello world!"}}, []Value{
[]map[string]Value{ &VariableValue{
{ "a",
"a": &StringValue{"Hello world!"}, &StringValue{"Hello world!"},
0,
}, },
}, },
}, },
/* these tests are so fucking unmaintainable
"conditional_false": { "conditional_false": {
&Program{ &Program{
[]Import{}, []Import{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -85,8 +84,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -107,7 +106,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&FloatValue{0}, &NumberValue{0},
0, 0,
}, },
}, },
@ -118,8 +117,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -134,8 +133,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -156,7 +155,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&FloatValue{1}, &NumberValue{1},
0, 0,
}, },
}, },
@ -167,8 +166,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -183,8 +182,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -197,8 +196,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -218,7 +217,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&FloatValue{2}, &NumberValue{2},
0, 0,
}, },
}, },
@ -229,8 +228,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -245,8 +244,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -259,8 +258,8 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -280,7 +279,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&FloatValue{1}, &NumberValue{1},
0, 0,
}, },
}, },
@ -291,14 +290,14 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -315,7 +314,7 @@ func GetCompileTestData() map[string]CompileTestData {
[]Value{ []Value{
&VariableValue{ &VariableValue{
"a", "a",
&FloatValue{3}, &NumberValue{3},
0, 0,
}, },
}, },
@ -326,20 +325,20 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{name: "sum"}, "sum",
&FunctionNode{ &FunctionNode{
"sum", "sum",
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
&FloatSignature{}, &NumberSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -379,20 +378,20 @@ func GetCompileTestData() map[string]CompileTestData {
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
&FloatSignature{}, &NumberSignature{},
NewChunk( NewChunk(
[]Bytecode{ []Bytecode{
InstructionDescend, InstructionDescend,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionAddFloat, InstructionAdd,
InstructionReturn, InstructionReturn,
InstructionAscend, InstructionAscend,
}, },
@ -412,16 +411,16 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FunctionNode{ &FunctionNode{
"a", "a",
[]FunctionParameter{}, []FunctionParameter{},
&FloatSignature{}, &NumberSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"b", 0, 0}, "b",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -449,6 +448,7 @@ func GetCompileTestData() map[string]CompileTestData {
0, 0, 0, 0,
}, },
[]Node{}, []Node{},
false,
0, 0, 0, 0,
}, },
}, },
@ -462,7 +462,7 @@ func GetCompileTestData() map[string]CompileTestData {
&FunctionValue{ &FunctionValue{
"a", "a",
[]FunctionParameter{}, []FunctionParameter{},
&FloatSignature{}, &NumberSignature{},
NewChunk( NewChunk(
[]Bytecode{ []Bytecode{
InstructionDescend, InstructionDescend,
@ -473,7 +473,7 @@ func GetCompileTestData() map[string]CompileTestData {
InstructionAscend, InstructionAscend,
}, },
[]Value{ []Value{
&FloatValue{1}, &StringValue{"b"}, &NumberValue{1}, &StringValue{"b"},
}, },
), ),
nil, nil,
@ -488,17 +488,17 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
statements: []Node{ statements: []Node{
&AssignNode{ &AssignNode{
dest: &ReferenceNode{"a", 0, 0}, name: "a",
value: &ListNode{ value: &ListNode{
items: []Node{ items: []Node{
&FloatNode{value: 1}, &NumberNode{value: 1},
&FloatNode{value: 2}, &NumberNode{value: 2},
}, },
}, },
declare: true, declare: true,
}, },
&AssignNode{ &AssignNode{
dest: &ReferenceNode{"b", 0, 0}, name: "b",
value: &ListNode{ value: &ListNode{
items: []Node{ items: []Node{
&StringNode{value: "Hello"}, &StringNode{value: "Hello"},
@ -516,8 +516,8 @@ func GetCompileTestData() map[string]CompileTestData {
name: "a", name: "a",
value: &ListValue{ value: &ListValue{
Items: []Value{ Items: []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
}, },
}, },
scope: 0, scope: 0,
@ -540,10 +540,10 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
dest: &ReferenceNode{"a", 0, 0}, name: "a",
value: &UnaryNode{ value: &UnaryNode{
UnaryNegate, UnaryNegate,
&FloatNode{ &NumberNode{
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: &FloatValue{-1}, value: &NumberValue{-1},
scope: 0, scope: 0,
}, },
}, },
@ -570,7 +570,7 @@ func GetCompileTestData() map[string]CompileTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
dest: &ReferenceNode{"a", 0, 0}, name: "a",
value: &UnaryNode{ value: &UnaryNode{
UnaryNot, UnaryNot,
&BooleanNode{ &BooleanNode{
@ -593,7 +593,6 @@ func GetCompileTestData() map[string]CompileTestData {
}, },
}, },
}, },
*/
} }
} }
@ -642,8 +641,7 @@ func TestCompile(t *testing.T) {
} }
t.Log("Executed bytecode") t.Log("Executed bytecode")
CompareStacks(t, testCase.expectedStack, vm.Stack) CompareStacks(t, testCase.expectedStack, vm.stack)
CompareScope(t, testCase.expectedScope, vm.scope)
}) })
} }
} }
@ -693,8 +691,8 @@ func TestCompiler_CleanStack(t *testing.T) {
} }
// make sure stack has only assigned values // make sure stack has only assigned values
for i := 1; i < int(vm.Stack.Current); i++ { for i := 0; i < int(vm.stack.Current); i++ {
v := vm.Stack.items[i] v := vm.stack.items[i]
if v == nil || v.Type() != VariableValueType { if v == nil || v.Type() != VariableValueType {
t.Errorf("Unclean stack! value %v at %d on the stack is intermediary", v.String(), i) t.Errorf("Unclean stack! value %v at %d on the stack is intermediary", v.String(), i)

View file

@ -9,13 +9,13 @@ import (
type Token struct { type Token struct {
Type TokenType Type TokenType
Start Pos Start Pos
End Pos Length Pos
Line Pos Line Pos
Lexeme string Lexeme string
} }
func (t Token) String() string { func (t Token) String() string {
return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.End, t.Line) return fmt.Sprintf("token %s, '%s' %d -> %d, line %d", t.Type.String(), t.Lexeme, t.Start, t.Length, t.Line)
} }
type TokenType uint64 type TokenType uint64
@ -28,8 +28,7 @@ const (
TokenBang TokenBang
TokenSemicolon TokenSemicolon
TokenInteger TokenNumber
TokenFloat
TokenHexadecimal TokenHexadecimal
TokenString TokenString
TokenName TokenName
@ -56,7 +55,6 @@ const (
TokenComma TokenComma
TokenDot TokenDot
TokenColon TokenColon
TokenArrow
TokenAssign TokenAssign
TokenDeclare TokenDeclare
@ -71,7 +69,6 @@ const (
TokenPipe TokenPipe
TokenDoublePipe TokenDoublePipe
TokenNewLine
TokenBreakpoint TokenBreakpoint
TokenEOF TokenEOF
TokenError TokenError
@ -89,10 +86,8 @@ func (t TokenType) String() string {
return "slash" return "slash"
case TokenBang: case TokenBang:
return "bang" return "bang"
case TokenFloat: case TokenNumber:
return "float" return "number"
case TokenInteger:
return "integer"
case TokenString: case TokenString:
return "string" return "string"
case TokenTrue: case TokenTrue:
@ -140,7 +135,7 @@ func (t TokenType) String() string {
case TokenDeclare: case TokenDeclare:
return "declare" return "declare"
case TokenFunc: case TokenFunc:
return "fn" return "func"
case TokenReturn: case TokenReturn:
return "return" return "return"
case TokenWhile: case TokenWhile:
@ -167,10 +162,6 @@ func (t TokenType) String() string {
return "pipe" return "pipe"
case TokenHexadecimal: case TokenHexadecimal:
return "hexadecimal" return "hexadecimal"
case TokenArrow:
return "arrow"
case TokenNewLine:
return "newline"
} }
panic("UNDEFINED TOKENTYPE STRING CONVERSION") panic("UNDEFINED TOKENTYPE STRING CONVERSION")
@ -215,14 +206,9 @@ func (l *Lexer) NextToken() (Token, error) {
l.advance() l.advance()
switch c { switch c {
case '\n':
return l.makeToken(TokenNewLine), nil
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
@ -353,7 +339,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 "fn": case "func":
return l.makeToken(TokenFunc), nil return l.makeToken(TokenFunc), nil
case "while": case "while":
return l.makeToken(TokenWhile), nil return l.makeToken(TokenWhile), nil
@ -378,7 +364,7 @@ func (l *Lexer) NextToken() (Token, error) {
return l.makeToken(TokenHexadecimal), nil return l.makeToken(TokenHexadecimal), nil
} }
return l.makeToken(TokenInteger), nil return l.makeToken(TokenNumber), 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()
@ -389,22 +375,20 @@ 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(TokenInteger), nil return l.makeToken(TokenNumber), 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))
} }
} }
func NewToken(t TokenType, start Pos, end Pos, line Pos, lexeme string) Token { func NewToken(t TokenType, start Pos, length Pos, line Pos, lexeme string) Token {
return Token{ return Token{
Type: t, Type: t,
Start: start, Start: start,
End: end, Length: length,
Line: line, Line: line,
Lexeme: lexeme, Lexeme: lexeme,
} }
@ -426,7 +410,7 @@ func (l *Lexer) Tokenize() ([]Token, error) {
} }
func (l *Lexer) makeToken(t TokenType) Token { func (l *Lexer) makeToken(t TokenType) Token {
return NewToken(t, l.start, l.current, l.line, string(l.src[l.start:l.current])) return NewToken(t, l.start, l.current-l.start, l.line, string(l.src[l.start:l.current]))
} }
func (l *Lexer) peek() rune { func (l *Lexer) peek() rune {
@ -475,7 +459,7 @@ func (l *Lexer) isAtEnd() bool {
} }
func (l *Lexer) skipWhitespace() { func (l *Lexer) skipWhitespace() {
for !l.isAtEnd() && unicode.IsSpace(l.peek()) && l.peek() != '\n' { for !l.isAtEnd() && unicode.IsSpace(l.peek()) {
l.advance() l.advance()
} }
} }

View file

@ -21,33 +21,33 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"simple number(1)": { "simple number(1)": {
"1024", "1024",
[]TokenType{TokenInteger, TokenEOF}, []TokenType{TokenNumber, TokenEOF},
}, },
"simple_arithmetics(7)": { "simple_arithmetics(7)": {
"1 + 23 / 4 * 3", "1 + 23 / 4 * 3",
[]TokenType{ []TokenType{
TokenInteger, TokenPlus, TokenInteger, TokenSlash, TokenNumber, TokenPlus, TokenNumber, TokenSlash,
TokenInteger, TokenStar, TokenInteger, TokenEOF, TokenNumber, TokenStar, TokenNumber, TokenEOF,
}, },
}, },
"condition(3)": { "condition(3)": {
"a <= 200", "a <= 200",
[]TokenType{TokenName, TokenLessThanOrEqual, TokenInteger, TokenEOF}, []TokenType{TokenName, TokenLessThanOrEqual, TokenNumber, 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, TokenInteger, TokenOpenBrace, TokenNewLine, TokenIf, TokenName, TokenGreaterThanOrEqual, TokenNumber, TokenOpenBrace,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace,
TokenCloseBrace, TokenEOF, TokenEOF,
}, },
}, },
"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, TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenGreaterThan, TokenInteger, TokenOpenBrace, TokenNewLine, TokenIf, TokenNumber, TokenStar, TokenNumber, TokenSlash, TokenNumber, TokenGreaterThan, TokenNumber, TokenOpenBrace,
TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, TokenCloseBrace, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenCloseBrace,
TokenElse, TokenOpenBrace, TokenNewLine, TokenName, TokenOpenParenthesis, TokenString, TokenCloseParenthesis, TokenNewLine, 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, TokenInteger, TokenEquals, TokenName, TokenPlus, TokenNumber, TokenEquals,
TokenInteger, TokenStar, TokenInteger, TokenSlash, TokenInteger, TokenNumber, TokenStar, TokenNumber, TokenSlash, TokenNumber,
TokenEOF, TokenEOF,
}, },
}, },
@ -77,7 +77,7 @@ func GetLexerTestData() map[string]LexerTestData {
}, },
"space_before_string": { "space_before_string": {
"\n \"\"", "\n \"\"",
[]TokenType{TokenNewLine, TokenString, TokenEOF}, []TokenType{TokenString, TokenEOF},
}, },
"write_call": { "write_call": {
"write(\"Hello world\")", "write(\"Hello world\")",
@ -86,46 +86,46 @@ func GetLexerTestData() map[string]LexerTestData {
"complex_comparison": { "complex_comparison": {
"!(h__elo123 >= 1)", "!(h__elo123 >= 1)",
[]TokenType{ []TokenType{
TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenInteger, TokenCloseParenthesis, TokenBang, TokenOpenParenthesis, TokenName, TokenGreaterThanOrEqual, TokenNumber, 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, TokenInteger, TokenStar, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenNumber, TokenStar, TokenNumber,
TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenName, TokenGreaterThan, TokenNumber,
TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenInteger, TokenNewLine, TokenName, TokenAssign, TokenName, TokenLessThanOrEqual, TokenNumber,
TokenBang, TokenName, TokenEquals, TokenName, TokenEOF, TokenBang, TokenName, TokenEquals, TokenName, TokenEOF,
}, },
}, },
"function": { "func": {
"fn sum(a, b) {\n return a + b\n}", "func 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, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace, TokenOpenBrace, TokenReturn, TokenName, TokenPlus, TokenName, TokenCloseBrace,
}, },
}, },
"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, TokenInteger, TokenOpenBrace, TokenNewLine, TokenWhile, TokenName, TokenLessThan, TokenNumber, TokenOpenBrace,
TokenName, TokenAssign, TokenName, TokenPlus, TokenInteger, TokenNewLine, TokenCloseBrace, TokenEOF, TokenName, TokenAssign, TokenName, TokenPlus, TokenNumber, TokenCloseBrace,
}, },
}, },
"lambda": { "lambda": {
"sum := fn(a, b) {\n" + "sum := func(a, b) {\n" +
" return a + b\n" + " return a + b\n" +
"}", "}",
[]TokenType{ []TokenType{
TokenName, TokenDeclare, TokenFunc, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis, TokenName, TokenDeclare, TokenFunc, TokenOpenParenthesis, TokenName, TokenComma, TokenName, TokenCloseParenthesis,
TokenOpenBrace, TokenNewLine, TokenReturn, TokenName, TokenPlus, TokenName, TokenNewLine, TokenCloseBrace, TokenOpenBrace, TokenReturn, TokenName, TokenPlus, TokenName, TokenCloseBrace,
}, },
}, },
"list": { "list": {
"data := [3, 1, 4, 1]", "data := [3, 1, 4, 1]",
[]TokenType{ []TokenType{
TokenName, TokenDeclare, TokenOpenBracket, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenComma, TokenInteger, TokenCloseBracket, TokenName, TokenDeclare, TokenOpenBracket, TokenNumber, TokenComma, TokenNumber, TokenComma, TokenNumber, TokenComma, TokenNumber, TokenCloseBracket,
}, },
}, },
} }

View file

@ -2,7 +2,6 @@ package core
import ( import (
"fmt" "fmt"
"math/big"
"strconv" "strconv"
"strings" "strings"
) )
@ -22,20 +21,17 @@ type Boundary interface {
const ( const (
StringNodeType NodeType = iota StringNodeType NodeType = iota
FloatNodeType NumberNodeType
IntegerNodeType
ReferenceNodeType ReferenceNodeType
BooleanNodeType BooleanNodeType
NilNodeType NilNodeType
ListNodeType ListNodeType
TupleNodeType
BinaryNodeType BinaryNodeType
UnaryNodeType UnaryNodeType
BlockNodeType BlockNodeType
ConditionalNodeType ConditionalNodeType
LoopNodeType LoopNodeType
AssignNodeType AssignNodeType
InvokeNodeType
CallNodeType CallNodeType
FunctionNodeType FunctionNodeType
ReturnNodeType ReturnNodeType
@ -47,10 +43,8 @@ func (n NodeType) String() string {
switch n { switch n {
case StringNodeType: case StringNodeType:
return "String" return "String"
case FloatNodeType: case NumberNodeType:
return "Float" return "Number"
case IntegerNodeType:
return "Integer"
case ReferenceNodeType: case ReferenceNodeType:
return "Reference" return "Reference"
case BinaryNodeType: case BinaryNodeType:
@ -67,7 +61,7 @@ func (n NodeType) String() string {
return "Loop" return "Loop"
case AssignNodeType: case AssignNodeType:
return "Assign" return "Assign"
case InvokeNodeType: case CallNodeType:
return "Call" return "Call"
case FunctionNodeType: case FunctionNodeType:
return "Function" return "Function"
@ -75,16 +69,12 @@ func (n NodeType) String() string {
return "Return" return "Return"
case ListNodeType: case ListNodeType:
return "List" return "List"
case TupleNodeType:
return "Tuple"
case AccessNodeType: case AccessNodeType:
return "Access" return "Access"
case BreakpointNodeType: case BreakpointNodeType:
return "Breakpoint" return "Breakpoint"
case UnaryNodeType: case UnaryNodeType:
return "Unary" return "Unary"
case CallNodeType:
return "Call"
} }
return "Invalid Node Type" return "Invalid Node Type"
} }
@ -130,41 +120,22 @@ func (n StringNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
type FloatNode struct { type NumberNode struct {
value float64 value float64
start Pos start Pos
end Pos end Pos
} }
func (n FloatNode) Type() NodeType { func (n NumberNode) Type() NodeType {
return FloatNodeType return NumberNodeType
} }
func (n FloatNode) String() string { func (n NumberNode) String() string {
return strconv.FormatFloat(n.value, 'g', -1, FloatSize) return strconv.FormatFloat(n.value, 'g', -1, NumberSize)
} }
func (n FloatNode) Bounds() (Pos, Pos) { func (n NumberNode) 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
} }
@ -198,37 +169,6 @@ func (n ListNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
type TupleNode struct {
items []Node
start Pos
end Pos
}
func (n TupleNode) Type() NodeType {
return TupleNodeType
}
func (n TupleNode) String() string {
sb := strings.Builder{}
sb.WriteString("(")
for i, item := range n.items {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(item.String())
}
sb.WriteString(")")
return sb.String()
}
func (n TupleNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
type AccessNode struct { type AccessNode struct {
source Node source Node
property string property string
@ -318,14 +258,14 @@ func (n BinaryOperation) Symbol() string {
return "<" return "<"
case BinaryGreater: case BinaryGreater:
return ">" return ">"
case BinaryLessEqual:
return "<="
case BinaryGreaterEqual:
return ">="
case BinaryAnd: case BinaryAnd:
return "&&" return "&&"
case BinaryOr: case BinaryOr:
return "||" return "||"
case BinaryLessEqual:
return "<="
case BinaryGreaterEqual:
return ">="
} }
panic("unsupported binary operation to symbol conversion for " + n.String()) panic("unsupported binary operation to symbol conversion for " + n.String())
@ -516,7 +456,7 @@ func (n LoopNode) Bounds() (Pos, Pos) {
// AssignNode assignment // AssignNode assignment
type AssignNode struct { type AssignNode struct {
dest Node name string
value Node value Node
declare bool declare bool
@ -529,39 +469,18 @@ func (n AssignNode) Type() NodeType {
} }
func (n AssignNode) String() string { func (n AssignNode) String() string {
return fmt.Sprintf("set %s to %s", n.dest, n.value) return fmt.Sprintf("set %s to %s", n.name, n.value)
} }
func (n AssignNode) Bounds() (Pos, Pos) { func (n AssignNode) Bounds() (Pos, Pos) {
return n.start, n.end return n.start, n.end
} }
// InvokeNode function call // CallNode function call
type InvokeNode struct {
source Node
args []Node
start Pos
end Pos
}
func (n InvokeNode) Type() NodeType {
return InvokeNodeType
}
func (n InvokeNode) String() string {
return fmt.Sprintf("invoke %s with args (%s)", n.source.String(), n.args)
}
func (n InvokeNode) Bounds() (Pos, Pos) {
return n.start, n.end
}
// CallNode call a function of a value
type CallNode struct { type CallNode struct {
source Node source Node
name Token
args []Node args []Node
keep bool
start Pos start Pos
end Pos end Pos
@ -572,7 +491,7 @@ func (n CallNode) Type() NodeType {
} }
func (n CallNode) String() string { func (n CallNode) String() string {
return fmt.Sprintf("call %s on %s with args (%s)", n.name, n.source.String(), n.args) return fmt.Sprintf("call %s with args (%s)", n.source.String(), n.args)
} }
func (n CallNode) Bounds() (Pos, Pos) { func (n CallNode) Bounds() (Pos, Pos) {

View file

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"math/big"
"strconv" "strconv"
"strings" "strings"
) )
@ -62,7 +61,7 @@ func (p ParsingError) Format() string {
b.WriteRune(' ') b.WriteRune(' ')
} }
for i := 0; i < len(p.Causer.Lexeme); i++ { for i := 0; i < int(p.Causer.Length); i++ {
b.WriteRune('^') b.WriteRune('^')
} }
b.WriteRune('\n') b.WriteRune('\n')
@ -82,7 +81,6 @@ type Parser struct {
prev *Token prev *Token
curr *Token curr *Token
pos Pos pos Pos
ignoreNewLine bool
} }
func NewParser(source string, trace []string, tokens []Token) *Parser { func NewParser(source string, trace []string, tokens []Token) *Parser {
@ -144,19 +142,12 @@ func (p *Parser) Parse(path string) (*Program, error) {
imports = append(imports, Import{ imports = append(imports, Import{
p.prev.Lexeme[1 : len(p.prev.Lexeme)-1], p.prev.Lexeme[1 : len(p.prev.Lexeme)-1],
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}) })
continue continue
} }
for p.accept(TokenNewLine) { b, err := p.block(true)
}
if p.curr.Type == TokenEOF {
break
}
b, err := p.expression(false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -172,7 +163,7 @@ func (p *Parser) Parse(path string) (*Program, error) {
&BlockNode{ &BlockNode{
statements, statements,
0, 0,
p.curr.End, p.curr.Start + p.curr.Length,
}, },
path, path,
}, nil }, nil
@ -184,12 +175,6 @@ func (p *Parser) accept(tokenType TokenType) bool {
return false return false
} }
if p.ignoreNewLine && tokenType != TokenNewLine {
for p.curr.Type == TokenNewLine {
p.advance()
}
}
if (*p.curr).Type == tokenType { if (*p.curr).Type == tokenType {
p.advance() p.advance()
return true return true
@ -218,11 +203,10 @@ 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]
} else {
p.curr = nil
}
p.pos++ p.pos++
} else {
panic("no more tokens")
}
} }
func (p *Parser) error(error string, causer *Token) error { func (p *Parser) error(error string, causer *Token) error {
@ -234,370 +218,6 @@ func (p *Parser) error(error string, causer *Token) error {
} }
} }
func (p *Parser) expression(mustBeBlock bool) (Node, error) {
if mustBeBlock || p.accept(TokenOpenBrace) {
if mustBeBlock {
if err := p.expect(TokenOpenBrace, "expected block"); err != nil {
return nil, err
}
}
oldIgnoreNewline := p.ignoreNewLine
p.ignoreNewLine = false
start := p.prev.Start
var statements []Node
for !p.accept(TokenCloseBrace) {
if p.accept(TokenNewLine) {
continue
}
s, err := p.expression(false)
if err != nil {
return nil, err
}
statements = append(statements, s)
if !p.accept(TokenNewLine) {
if err := p.expect(TokenCloseBrace, "blocks must be closed"); err != nil {
return nil, err
}
break
}
}
p.ignoreNewLine = oldIgnoreNewline
return &BlockNode{statements, start, p.prev.End}, nil
}
t := p.curr
switch t.Type {
case TokenIf:
p.advance()
cond, err := p.expression(false)
if err != nil {
return nil, err
}
do, err := p.expression(true)
if err != nil {
return nil, err
}
var otherwise Node
if p.accept(TokenElse) {
otherwise, err = p.expression(p.curr.Type != TokenIf)
if err != nil {
return nil, err
}
}
return &ConditionalNode{
cond,
do,
otherwise,
t.Start,
t.End,
}, nil
case TokenFunc:
p.advance()
start := p.prev.Start
var name *Token
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
name = p.prev
}
params, err := p.parseParams()
if err != nil {
return nil, err
}
var yield TypeSignature
if p.accept(TokenArrow) {
yield, err = p.parseSignature()
if err != nil {
return nil, err
}
}
logic, err := p.expression(true)
if err != nil {
return nil, err
}
names := "*"
if name != nil {
names = name.Lexeme
}
fn := &FunctionNode{
names,
params,
yield,
logic,
start,
p.prev.End,
}
if name != nil {
return &AssignNode{
&ReferenceNode{name.Lexeme, name.Start, name.End},
fn,
true,
start,
p.prev.End,
}, nil
}
return fn, nil
case TokenReturn:
p.advance()
start := p.prev.Start
v, err := p.expression(false)
if err != nil {
return nil, err
}
return &ReturnNode{
v,
start,
p.prev.End,
}, nil
case TokenWhile:
p.advance()
start := p.prev.Start
cond, err := p.expression(false)
if err != nil {
return nil, err
}
logic, err := p.expression(true)
if err != nil {
return nil, err
}
return &LoopNode{
cond,
logic,
start,
p.prev.End,
}, nil
default:
s, err := p.binary()
if err != nil {
return nil, err
}
if p.accept(TokenDeclare) || p.accept(TokenAssign) {
isDeclaration := p.prev.Type == TokenDeclare
// possibly assign tuples; not implemented yet
v, err := p.expression(false)
if err != nil {
return nil, err
}
start, _ := s.Bounds()
_, end := v.Bounds()
return &AssignNode{
s,
v,
isDeclaration,
start,
end,
}, nil
}
return s, nil
}
}
func isBinaryOperator(tokenType TokenType) bool {
switch tokenType {
case TokenPlus, TokenMinus, TokenStar, TokenSlash, TokenPipe, TokenDoubleAmpersand, TokenDoublePipe, TokenEquals, TokenBangEquals, TokenLessThan, TokenLessThanOrEqual, TokenGreaterThan, TokenGreaterThanOrEqual:
return true
default:
return false
}
}
func binaryPrecedence(op TokenType) int {
switch op {
case TokenDoubleAmpersand, TokenDoublePipe:
return 1
case TokenEquals, TokenBangEquals, TokenLessThan, TokenGreaterThan, TokenLessThanOrEqual, TokenGreaterThanOrEqual:
return 2
case TokenPlus, TokenMinus, TokenPipe:
return 3
case TokenStar, TokenSlash:
return 5
default:
panic("unimplemented")
}
}
func tokenToBinaryOperation(tokenType TokenType) BinaryOperation {
switch tokenType {
case TokenPlus:
return BinaryAddition
case TokenMinus:
return BinarySubtraction
case TokenStar:
return BinaryMultiplication
case TokenSlash:
return BinaryDivision
case TokenPipe:
panic("unimplemented bitwise ops")
case TokenDoubleAmpersand:
return BinaryAnd
case TokenDoublePipe:
return BinaryOr
case TokenEquals:
return BinaryEquality
case TokenBangEquals:
return BinaryInequality
case TokenLessThan:
return BinaryLess
case TokenLessThanOrEqual:
return BinaryLessEqual
case TokenGreaterThan:
return BinaryGreater
case TokenGreaterThanOrEqual:
return BinaryGreaterEqual
default:
panic("unimplemented")
}
}
func (p *Parser) binary() (Node, error) {
t, err := p.chain()
if err != nil {
return nil, err
}
ops := NewStack[*Token](128)
values := NewStack[Node](256)
values.pushItem(t)
for isBinaryOperator(p.curr.Type) {
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
r := values.Pop()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := r.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
}
ops.Push(p.curr)
p.advance()
v, err := p.chain()
if err != nil {
return nil, err
}
values.Push(v)
}
for ops.Current > 0 {
r := values.Pop()
l := values.Pop()
op := tokenToBinaryOperation(ops.Pop().Type)
start, _ := l.Bounds()
_, end := l.Bounds()
values.Push(&BinaryNode{
op,
l,
r,
start,
end,
})
}
return values.Pop(), nil
}
func (p *Parser) chain() (Node, error) {
f, err := p.factor()
if err != nil {
return nil, err
}
for {
if p.accept(TokenDot) {
if err = p.expect(TokenName, "can only access properties by name"); err != nil {
return nil, err
}
name := p.prev
f = &AccessNode{
f,
p.prev.Lexeme,
name.Start,
name.End,
}
if p.curr.Type == TokenOpenParenthesis {
args, err := p.parseArgs()
if err != nil {
return nil, err
}
f = &InvokeNode{
f,
args,
name.Start,
p.prev.End,
}
}
} else if p.curr.Type == TokenOpenParenthesis {
start := p.curr.Start
args, err := p.parseArgs()
if err != nil {
return nil, err
}
f = &InvokeNode{
f,
args,
start,
p.prev.End,
}
} else {
break
}
}
return f, nil
}
func (p *Parser) factor() (Node, error) { func (p *Parser) factor() (Node, error) {
switch (*p.curr).Type { switch (*p.curr).Type {
case TokenString: case TokenString:
@ -606,49 +226,35 @@ func (p *Parser) factor() (Node, error) {
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1], (*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
(*p.prev).Lexeme, (*p.prev).Lexeme,
p.prev.Start, p.prev.Start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenInteger: case TokenNumber:
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.End,
}, 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 &FloatNode{ return &NumberNode{
num, num,
p.prev.Start, p.prev.Start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenHexadecimal: case TokenHexadecimal:
p.advance() p.advance()
start := (*p.prev).Start start := (*p.prev).Start
num, ok := new(big.Int).SetString(p.prev.Lexeme[2:], 16) num, err := strconv.ParseUint((*p.prev).Lexeme[2:], 16, NumberSize)
if !ok { if err != nil {
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.prev.Lexeme), p.prev) return nil, err
} }
return &IntegerNode{ return &NumberNode{
num, float64(num),
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenTrue: case TokenTrue:
@ -656,14 +262,14 @@ func (p *Parser) factor() (Node, error) {
return &BooleanNode{ return &BooleanNode{
true, true,
p.prev.Start, p.prev.Start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenFalse: case TokenFalse:
p.advance() p.advance()
return &BooleanNode{ return &BooleanNode{
false, false,
p.prev.Start, p.prev.Start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenNil: case TokenNil:
@ -674,8 +280,6 @@ 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 {
@ -686,13 +290,10 @@ func (p *Parser) factor() (Node, error) {
[]Node{}, []Node{},
s, s,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
oldIgnoreNewline := p.ignoreNewLine
p.ignoreNewLine = true
var values []Node var values []Node
for !p.accept(TokenCloseBracket) { for !p.accept(TokenCloseBracket) {
if len(values) > 0 { if len(values) > 0 {
@ -709,13 +310,11 @@ func (p *Parser) factor() (Node, error) {
values = append(values, value) values = append(values, value)
} }
p.ignoreNewLine = oldIgnoreNewline
return &ListNode{ return &ListNode{
values, values,
nil, nil,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
// unary minus // unary minus
@ -731,7 +330,7 @@ func (p *Parser) factor() (Node, error) {
UnaryNegate, UnaryNegate,
f, f,
first.Start, first.Start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenBang: case TokenBang:
@ -747,14 +346,14 @@ func (p *Parser) factor() (Node, error) {
UnaryNot, UnaryNot,
v, v,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenName: case TokenName:
p.advance() p.advance()
name := (*p.prev).Lexeme name := (*p.prev).Lexeme
start := p.prev.Start start := p.prev.Start
nameEnd := p.prev.End nameEnd := start + p.prev.Length
if p.curr.Type == TokenOpenParenthesis { if p.curr.Type == TokenOpenParenthesis {
args, err := p.parseArgs() args, err := p.parseArgs()
@ -762,15 +361,16 @@ func (p *Parser) factor() (Node, error) {
return nil, err return nil, err
} }
return &InvokeNode{ return &CallNode{
&ReferenceNode{ &ReferenceNode{
name, name,
start, start,
nameEnd, nameEnd,
}, },
args, args,
true,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
@ -790,7 +390,7 @@ func (p *Parser) factor() (Node, error) {
} }
var sig TypeSignature = &NilSignature{} var sig TypeSignature = &NilSignature{}
if p.accept(TokenArrow) { if p.curr.Type != TokenOpenBrace {
sig, err = p.parseSignature() sig, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
@ -808,7 +408,7 @@ func (p *Parser) factor() (Node, error) {
sig, sig,
b, b,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenOpenParenthesis: case TokenOpenParenthesis:
@ -823,16 +423,8 @@ func (p *Parser) factor() (Node, error) {
return v, nil return v, nil
case TokenBreakpoint:
p.advance()
return &BreakpointNode{
p.prev.Start,
p.prev.End,
}, nil
default: default:
return nil, p.error(fmt.Sprintf("invalid factor %s", p.curr), p.curr) return nil, p.error("invalid factor", p.curr)
} }
} }
@ -855,7 +447,7 @@ func (p *Parser) prop() (Node, error) {
v, v,
property, property,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
} }
// if called, also add // if called, also add
@ -865,11 +457,12 @@ func (p *Parser) prop() (Node, error) {
return nil, err return nil, err
} }
v = &InvokeNode{ v = &CallNode{
v, v,
args, args,
true,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
} }
} }
} }
@ -901,7 +494,7 @@ func (p *Parser) product() (Node, error) {
left, left,
f, f,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
} }
} }
@ -933,7 +526,7 @@ func (p *Parser) term() (Node, error) {
left, left,
pr, pr,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
} }
} }
@ -980,7 +573,7 @@ func (p *Parser) comparison() (Node, error) {
left, left,
t, t,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
@ -1014,7 +607,7 @@ func (p *Parser) condition() (Node, error) {
left, left,
c, c,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
@ -1053,18 +646,19 @@ func (p *Parser) statement() (Node, error) {
then, then,
otherwise, otherwise,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenName: case TokenName:
p.advance() p.advance()
name := p.prev start := p.prev.Start
name := (*p.prev).Lexeme
if (*p.curr).Type == TokenDot { if (*p.curr).Type == TokenDot {
var v Node = &ReferenceNode{ var v Node = &ReferenceNode{
name.Lexeme, name,
name.Start, start,
name.End, p.prev.Start + p.prev.Length,
} }
// parse chains of prop-getting ( "".split().join().length.round() ) // parse chains of prop-getting ( "".split().join().length.round() )
@ -1077,8 +671,8 @@ func (p *Parser) statement() (Node, error) {
v = &AccessNode{ v = &AccessNode{
v, v,
property, property,
name.Start, start,
p.prev.End, p.prev.Start + p.prev.Length,
} }
// if called, also add // if called, also add
@ -1088,11 +682,12 @@ func (p *Parser) statement() (Node, error) {
return nil, err return nil, err
} }
v = &InvokeNode{ v = &CallNode{
v, v,
args, args,
name.Start, (*p.curr).Type == TokenDot, // if the chain is continued, keep the value.
p.prev.End, start,
p.prev.Start + p.prev.Length,
} }
} }
} }
@ -1104,15 +699,16 @@ func (p *Parser) statement() (Node, error) {
return nil, err return nil, err
} }
return &InvokeNode{ return &CallNode{
&ReferenceNode{ &ReferenceNode{
name.Lexeme, name,
name.Start, start,
name.End, start + Pos(len(name)),
}, },
args, args,
name.Start, false,
p.prev.End, start,
p.prev.Start + p.prev.Length,
}, nil }, nil
} else if p.accept(TokenAssign) || p.accept(TokenDeclare) { } else if p.accept(TokenAssign) || p.accept(TokenDeclare) {
isDeclaration := p.prev.Type == TokenDeclare isDeclaration := p.prev.Type == TokenDeclare
@ -1121,16 +717,12 @@ func (p *Parser) statement() (Node, error) {
return nil, err return nil, err
} }
return &AssignNode{ // THIS COULD BE MORE PERMISSIVE; its a new system return &AssignNode{
&ReferenceNode{ name,
name.Lexeme,
name.Start,
name.End,
},
c, c,
isDeclaration, isDeclaration,
name.Start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
@ -1144,7 +736,7 @@ func (p *Parser) statement() (Node, error) {
if err := p.expect(TokenName, "function must have a name"); err != nil { if err := p.expect(TokenName, "function must have a name"); err != nil {
return nil, err return nil, err
} }
name := p.prev name := p.prev.Lexeme
params, err := p.parseParams() params, err := p.parseParams()
if err != nil { if err != nil {
@ -1152,7 +744,7 @@ func (p *Parser) statement() (Node, error) {
} }
var yield TypeSignature = &NilSignature{} var yield TypeSignature = &NilSignature{}
if p.accept(TokenArrow) { if p.curr.Type != TokenOpenBrace {
yield, err = p.parseSignature() yield, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
@ -1165,22 +757,18 @@ func (p *Parser) statement() (Node, error) {
} }
return &AssignNode{ return &AssignNode{
&ReferenceNode{ name,
name.Lexeme,
name.Start,
name.End,
},
&FunctionNode{ &FunctionNode{
name.Lexeme, name,
params, params,
yield, yield,
b, b,
funcStart, funcStart,
p.prev.End, p.prev.Start + p.prev.Length,
}, },
true, true,
funcStart, funcStart,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenWhile: case TokenWhile:
@ -1201,7 +789,7 @@ func (p *Parser) statement() (Node, error) {
c, c,
b, b,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenReturn: case TokenReturn:
@ -1216,7 +804,7 @@ func (p *Parser) statement() (Node, error) {
return &ReturnNode{ return &ReturnNode{
c, c,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
case TokenBreakpoint: case TokenBreakpoint:
@ -1266,7 +854,7 @@ func (p *Parser) block(canBeStatement bool) (Node, error) {
return &BlockNode{ return &BlockNode{
statements, statements,
start, start,
p.prev.End, p.prev.Start + p.prev.Length,
}, nil }, nil
} }
@ -1278,7 +866,7 @@ func (p *Parser) parseArgs() ([]Node, error) {
} }
if !p.accept(TokenCloseParenthesis) { if !p.accept(TokenCloseParenthesis) {
c, err := p.expression(false) c, err := p.condition()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1287,7 +875,7 @@ func (p *Parser) parseArgs() ([]Node, error) {
if err := p.expect(TokenComma, "arguments must be separated by comma"); err != nil { if err := p.expect(TokenComma, "arguments must be separated by comma"); err != nil {
return nil, err return nil, err
} }
c, err = p.expression(false) c, err = p.condition()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1354,17 +942,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
func (p *Parser) parseSignature() (TypeSignature, error) { func (p *Parser) parseSignature() (TypeSignature, error) {
var s TypeSignature var s TypeSignature
if p.accept(TokenOpenParenthesis) { if p.accept(TokenFunc) {
is, err := p.parseSignature()
if err != nil {
return nil, err
}
if err := p.expect(TokenCloseParenthesis, "expected closing parenthesis"); err != nil {
return nil, err
}
s = is
} else if p.accept(TokenFunc) {
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil { if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
return nil, err return nil, err
} }
@ -1387,31 +965,15 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
in = append(in, sig) in = append(in, sig)
} }
var out TypeSignature out, err := p.parseSignature()
var err error
if p.accept(TokenArrow) {
out, err = p.parseSignature()
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else {
out = &NilSignature{}
}
s = &FunctionSignature{ s = &FunctionSignature{
in, in,
out, out,
} }
} else if p.accept(TokenOpenBracket) {
inner, err := p.parseSignature()
if err != nil {
return nil, err
}
if err := p.expect(TokenCloseBracket, "list type must be enclosed in brackets"); err != nil {
return nil, err
}
return &ListSignature{inner}, nil
} else { } else {
if err := p.expect(TokenName, "type must be a name"); err != nil { if err := p.expect(TokenName, "type must be a name"); err != nil {
return nil, err return nil, err
@ -1421,10 +983,8 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
switch name { switch name {
case "string": case "string":
s = &StringSignature{} s = &StringSignature{}
case "int": case "number":
s = &IntegerSignature{} s = &NumberSignature{}
case "float":
s = &FloatSignature{}
case "boolean": case "boolean":
s = &BooleanSignature{} s = &BooleanSignature{}
case "list": case "list":
@ -1437,7 +997,7 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
return nil, err return nil, err
} }
if err := p.expect(TokenCloseBracket, "square brackets enclose list content signature"); err != nil { if err := p.expect(TokenCloseBracket, "list must close parameter list"); err != nil {
return nil, err return nil, err
} }

View file

@ -55,22 +55,22 @@ 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(TokenFloat, 3, 1, 0, "1"), NewToken(TokenNumber, 3, 1, 0, "1"),
NewToken(TokenPlus, 4, 1, 0, "+"), NewToken(TokenPlus, 4, 1, 0, "+"),
NewToken(TokenFloat, 5, 1, 0, "2"), NewToken(TokenNumber, 5, 1, 0, "2"),
NewToken(TokenEOF, 6, 0, 0, ""), NewToken(TokenEOF, 6, 0, 0, ""),
}, },
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"_", 0, 0}, "_",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -93,7 +93,7 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"hello", 0, 0}, "hello",
&StringNode{ &StringNode{
"Hello world!", "Hello world!",
"\"Hello world!\"", "\"Hello world!\"",
@ -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(TokenFloat, 3, 1, 0, "1"), NewToken(TokenNumber, 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, ""),
@ -118,10 +118,10 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -145,35 +145,35 @@ 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(TokenFloat, 4, 1, 0, "2"), NewToken(TokenNumber, 4, 1, 0, "2"),
NewToken(TokenPlus, 5, 1, 0, "+"), NewToken(TokenPlus, 5, 1, 0, "+"),
NewToken(TokenFloat, 6, 1, 0, "1"), NewToken(TokenNumber, 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(TokenFloat, 9, 1, 0, "5"), NewToken(TokenNumber, 9, 1, 0, "5"),
NewToken(TokenPlus, 10, 1, 0, "+"), NewToken(TokenPlus, 10, 1, 0, "+"),
NewToken(TokenFloat, 11, 1, 0, "3"), NewToken(TokenNumber, 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(TokenFloat, 14, 1, 0, "6"), NewToken(TokenNumber, 14, 1, 0, "6"),
NewToken(TokenMinus, 15, 1, 0, "-"), NewToken(TokenMinus, 15, 1, 0, "-"),
NewToken(TokenFloat, 16, 1, 0, "2"), NewToken(TokenNumber, 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(TokenFloat, 19, 2, 0, "10"), NewToken(TokenNumber, 19, 2, 0, "10"),
NewToken(TokenSlash, 20, 1, 0, "/"), NewToken(TokenSlash, 20, 1, 0, "/"),
NewToken(TokenFloat, 21, 1, 0, "2"), NewToken(TokenNumber, 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
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"_", 0, 0}, "_",
&BinaryNode{ &BinaryNode{
BinarySubtraction, BinarySubtraction,
&BinaryNode{ &BinaryNode{
@ -182,17 +182,17 @@ func GetTokenTestData() map[string]TokenTestData {
BinaryMultiplication, BinaryMultiplication,
&BinaryNode{ &BinaryNode{
BinaryAddition, BinaryAddition,
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
5, 5,
0, 0, 0, 0,
}, },
@ -200,17 +200,17 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&BinaryNode{ &BinaryNode{
BinaryDivision, BinaryDivision,
&FloatNode{ &NumberNode{
3, 3,
0, 0, 0, 0,
}, },
&BinaryNode{ &BinaryNode{
BinarySubtraction, BinarySubtraction,
&FloatNode{ &NumberNode{
6, 6,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -222,11 +222,11 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&BinaryNode{ &BinaryNode{
BinaryDivision, BinaryDivision,
&FloatNode{ &NumberNode{
10, 10,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, },
@ -245,22 +245,22 @@ 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(TokenFloat, 2, 2, 0, "20"), NewToken(TokenNumber, 2, 2, 0, "20"),
NewToken(TokenEquals, 4, 2, 0, "=="), NewToken(TokenEquals, 4, 2, 0, "=="),
NewToken(TokenFloat, 6, 2, 0, "15"), NewToken(TokenNumber, 6, 2, 0, "15"),
NewToken(TokenEOF, 8, 0, 0, ""), NewToken(TokenEOF, 8, 0, 0, ""),
}, },
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"_", 0, 0}, "_",
&BinaryNode{ &BinaryNode{
BinaryEquality, BinaryEquality,
&FloatNode{ &NumberNode{
20, 20,
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
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(TokenFloat, 5, 1, 0, "0"), NewToken(TokenNumber, 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(TokenFloat, 9, 1, 1, "1"), NewToken(TokenNumber, 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,
}, },
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -304,8 +304,8 @@ func GetTokenTestData() map[string]TokenTestData {
do: &BlockNode{ do: &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"b", 0, 0}, "b",
&FloatNode{ &NumberNode{
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(TokenFloat, 5, 1, 0, "0"), NewToken(TokenNumber, 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(TokenFloat, 9, 1, 1, "1"), NewToken(TokenNumber, 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(TokenFloat, 18, 1, 2, "0"), NewToken(TokenNumber, 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,
}, },
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -357,8 +357,8 @@ func GetTokenTestData() map[string]TokenTestData {
do: &BlockNode{ do: &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"b", 0, 0}, "b",
&FloatNode{ &NumberNode{
1, 1,
0, 0, 0, 0,
}, },
@ -371,8 +371,8 @@ func GetTokenTestData() map[string]TokenTestData {
otherwise: &BlockNode{ otherwise: &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"b", 0, 0}, "b",
&FloatNode{ &NumberNode{
0, 0,
0, 0, 0, 0,
}, },
@ -403,22 +403,21 @@ func GetTokenTestData() map[string]TokenTestData {
0, 0, 0, 0,
}, },
}, },
"lambda": { // a := fn(a: float, b: float) -> float { return a + b } "lambda": { // a := func(a, b) { 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, 2, 0, "fn"), NewToken(TokenFunc, 3, 4, 0, "func"),
NewToken(TokenOpenParenthesis, 5, 1, 0, "("), NewToken(TokenOpenParenthesis, 7, 1, 0, "("),
NewToken(TokenName, 6, 1, 0, "a"), NewToken(TokenName, 8, 1, 0, "a"),
NewToken(TokenColon, 6, 1, 0, ":"), NewToken(TokenColon, 9, 1, 0, ":"),
NewToken(TokenName, 10, 5, 0, "float"), NewToken(TokenName, 10, 5, 0, "number"),
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, "float"), NewToken(TokenName, 10, 5, 0, "number"),
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"), NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
NewToken(TokenArrow, 12, 1, 0, "->"), NewToken(TokenName, 10, 5, 0, "number"),
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"),
@ -432,20 +431,20 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FunctionNode{ &FunctionNode{
"*", "*",
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
&FloatSignature{}, &NumberSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -477,19 +476,13 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
"function_declaration": { "function_declaration": {
[]Token{ []Token{
NewToken(TokenFunc, 0, 2, 0, "fn"), NewToken(TokenFunc, 0, 4, 0, "func"),
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"),
@ -503,20 +496,20 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"a", 0, 0}, "a",
&FunctionNode{ &FunctionNode{
"a", "a",
[]FunctionParameter{ []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
&FloatSignature{}, &NumberSignature{},
&BlockNode{ &BlockNode{
[]Node{ []Node{
&ReturnNode{ &ReturnNode{
@ -559,7 +552,7 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"p", 0, 0}, "p",
&AccessNode{ &AccessNode{
&ReferenceNode{ &ReferenceNode{
"a", "a",
@ -584,7 +577,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(TokenFloat, 8, 1, 0, "3.141"), NewToken(TokenNumber, 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!\""),
@ -594,10 +587,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(TokenFloat, 6, 1, 0, "2"), NewToken(TokenNumber, 6, 1, 0, "2"),
NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenComma, 11, 1, 0, ","),
NewToken(TokenFloat, 6, 1, 0, "3"), NewToken(TokenNumber, 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,14 +600,14 @@ func GetTokenTestData() map[string]TokenTestData {
&BlockNode{ &BlockNode{
[]Node{ []Node{
&AssignNode{ &AssignNode{
&ReferenceNode{"data", 0, 0}, "data",
&ListNode{ &ListNode{
[]Node{ []Node{
&ReferenceNode{ &ReferenceNode{
"a", "a",
0, 0, 0, 0,
}, },
&FloatNode{ &NumberNode{
3.141, 3.141,
0, 0, 0, 0,
}, },
@ -629,10 +622,10 @@ func GetTokenTestData() map[string]TokenTestData {
}, },
&ListNode{ &ListNode{
[]Node{ []Node{
&FloatNode{ &NumberNode{
2, 2,
0, 0, 0, 0,
}, &FloatNode{ }, &NumberNode{
3, 3,
0, 0, 0, 0,
}, },
@ -684,20 +677,12 @@ 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 FloatNodeType: case NumberNodeType:
if n1.(*FloatNode).value != n2.(*FloatNode).value { if n1.(*NumberNode).value != n2.(*NumberNode).value {
t.Errorf("Float node values don't match (%f and %f)", n1.(*FloatNode).value, n2.(*FloatNode).value) t.Errorf("Number node values don't match (%f and %f)", n1.(*NumberNode).value, n2.(*NumberNode).value)
} else { } else {
t.Logf("Float node values match (%f)", n1.(*FloatNode).value) t.Logf("Number node values match (%f)", n1.(*NumberNode).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)
@ -749,8 +734,11 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
NodeEquality(t, n1.(*LoopNode).do, n2.(*LoopNode).do) NodeEquality(t, n1.(*LoopNode).do, n2.(*LoopNode).do)
case AssignNodeType: case AssignNodeType:
t.Logf("Checking if value destination matches") if n1.(*AssignNode).name != n2.(*AssignNode).name {
NodeEquality(t, n1.(*AssignNode).dest, n2.(*AssignNode).dest) t.Errorf("Assigned value name is not the same (%s and %s)", n1.(*AssignNode).name, n2.(*AssignNode).name)
} else {
t.Logf("Assigned value name matches (%s)", n1.(*AssignNode).name)
}
if n1.(*AssignNode).declare != n2.(*AssignNode).declare { if n1.(*AssignNode).declare != n2.(*AssignNode).declare {
t.Errorf("Not same type of assigning (1: %v; 2: %v)", n1.(*AssignNode).declare, n2.(*AssignNode).declare) t.Errorf("Not same type of assigning (1: %v; 2: %v)", n1.(*AssignNode).declare, n2.(*AssignNode).declare)
@ -759,18 +747,22 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
t.Logf("Checking equality of assignment values") t.Logf("Checking equality of assignment values")
NodeEquality(t, n1.(*AssignNode).value, n2.(*AssignNode).value) NodeEquality(t, n1.(*AssignNode).value, n2.(*AssignNode).value)
case InvokeNodeType: case CallNodeType:
n := n1.(*InvokeNode) n := n1.(*CallNode)
m := n2.(*InvokeNode) m := n2.(*CallNode)
NodeEquality(t, n.source, m.source) NodeEquality(t, n.source, m.source)
if n.keep == m.keep {
t.Logf("Call node keep modifier doesn't match (%v and %v)", n.keep, m.keep)
}
if len(n.args) != len(m.args) { if len(n.args) != len(m.args) {
t.Fatalf("Call node arguments count does not match (%d and %d)", len(n.args), m.args) t.Fatalf("Call node arguments count does not match (%d and %d)", len(n.args), m.args)
} }
for i, arg := range m.args { for i, arg := range m.args {
NodeEquality(t, n1.(*InvokeNode).args[i], arg) NodeEquality(t, n1.(*CallNode).args[i], arg)
} }
case FunctionNodeType: case FunctionNodeType:
@ -803,37 +795,6 @@ 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")
} }
@ -857,7 +818,7 @@ func SerializeTokens(tokens []Token) string {
out.WriteString("!") out.WriteString("!")
case TokenSemicolon: case TokenSemicolon:
out.WriteString(";") out.WriteString(";")
case TokenFloat: case TokenNumber:
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))
@ -884,7 +845,7 @@ func SerializeTokens(tokens []Token) string {
case TokenNil: case TokenNil:
out.WriteString("nil") out.WriteString("nil")
case TokenFunc: case TokenFunc:
out.WriteString("fn") out.WriteString("func")
case TokenReturn: case TokenReturn:
out.WriteString("return ") out.WriteString("return ")
case TokenWhile: case TokenWhile:
@ -943,6 +904,10 @@ 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)

View file

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

View file

@ -5,30 +5,6 @@ import (
"testing" "testing"
) )
func CompareScope(t *testing.T, expectedScope []map[string]Value, actualScope *Scope) {
s := actualScope
for i := len(expectedScope) - 1; i >= 0; i-- {
if s == nil {
t.Fatal("scope cut unexpecetantly short")
}
for name, value := range expectedScope[i] {
v, ok := s.current[name]
if !ok {
t.Errorf("variable %s not found in correct scope", name)
continue
}
if !v.Equals(value) {
t.Errorf("variable %s has value %s but expected %s", name, v.String(), value.String())
} else {
t.Logf("variable %s has expected value %s", name, v.String())
}
}
}
}
func CompareStacks[T Value](t *testing.T, expected []T, actual *Stack[T]) { func CompareStacks[T Value](t *testing.T, expected []T, actual *Stack[T]) {
if actual.Current != Pos(len(expected)) { if actual.Current != Pos(len(expected)) {
t.Errorf("Unexpected stack size. Expected %d, got %d", len(expected), actual.Current) t.Errorf("Unexpected stack size. Expected %d, got %d", len(expected), actual.Current)

View file

@ -9,8 +9,7 @@ type Type int
const ( const (
TypeString Type = iota TypeString Type = iota
TypeInteger TypeNumber
TypeFloat
TypeBoolean TypeBoolean
TypeNil TypeNil
TypeList TypeList
@ -25,10 +24,8 @@ func (t Type) String() string {
switch t { switch t {
case TypeString: case TypeString:
return "string" return "string"
case TypeInteger: case TypeNumber:
return "integer" return "number"
case TypeFloat:
return "float"
case TypeBoolean: case TypeBoolean:
return "boolean" return "boolean"
case TypeNil: case TypeNil:
@ -54,10 +51,8 @@ func SignatureOf(v Value) TypeSignature {
switch t := v.(type) { switch t := v.(type) {
case *StringValue: case *StringValue:
return &StringSignature{} return &StringSignature{}
case *FloatValue: case *NumberValue:
return &FloatSignature{} return &NumberSignature{}
case *IntegerValue:
return &IntegerSignature{}
case *BoolValue: case *BoolValue:
return &BooleanSignature{} return &BooleanSignature{}
case *ListValue: case *ListValue:
@ -143,40 +138,22 @@ func (*StringSignature) String() string {
return "string" return "string"
} }
type FloatSignature struct{} type NumberSignature struct{}
func (*FloatSignature) Type() Type { func (*NumberSignature) Type() Type {
return TypeFloat return TypeNumber
} }
func (s *FloatSignature) Matches(other TypeSignature) bool { func (s *NumberSignature) 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() == TypeFloat return other.Type() == TypeAny || other.Type() == TypeNumber
} }
func (*FloatSignature) String() string { func (*NumberSignature) String() string {
return "float" return "number"
}
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/big" "math"
"reflect" "reflect"
"strconv" "strconv"
) )
@ -13,8 +13,7 @@ type ValueType int
const ( const (
NilValueType ValueType = iota NilValueType ValueType = iota
BoolValueType BoolValueType
FloatValueType NumberValueType
IntegerValueType
StringValueType StringValueType
ListValueType ListValueType
ObjectValueType ObjectValueType
@ -31,10 +30,8 @@ func (v ValueType) String() string {
return "bool" return "bool"
case ObjectValueType: case ObjectValueType:
return "object" return "object"
case FloatValueType: case NumberValueType:
return "float" return "number"
case IntegerValueType:
return "int"
case StringValueType: case StringValueType:
return "string" return "string"
case ListValueType: case ListValueType:
@ -60,15 +57,11 @@ func GoToValue(gov interface{}) Value {
v, v,
} }
case int: case int:
return &IntegerValue{ return &NumberValue{
new(big.Int).SetInt64(int64(v)), float64(v),
}
case *big.Int:
return &IntegerValue{
v,
} }
case float64: case float64:
return &FloatValue{ return &NumberValue{
v, v,
} }
case string: case string:
@ -266,71 +259,40 @@ func (v *ObjectValue) Clone() Value {
} }
} }
// FloatValue floating-point values // NumberValue Integer or floating-point values
type FloatValue struct { type NumberValue struct {
Number float64 Number float64
} }
const FloatSize int = 64 const NumberSize int = 64
func (v *FloatValue) Type() ValueType { func (v *NumberValue) Type() ValueType {
return FloatValueType return NumberValueType
} }
func (v *FloatValue) String() string { func (v *NumberValue) String() string {
return strconv.FormatFloat(v.Number, 'g', -1, FloatSize) return strconv.FormatFloat(v.Number, 'g', -1, NumberSize)
} }
func (v *FloatValue) DebugString() string { func (v *NumberValue) DebugString() string {
return v.String() return v.String()
} }
func (v *FloatValue) Equals(other Value) bool { func (v *NumberValue) Equals(other Value) bool {
return other.Type() == FloatValueType && other.(*FloatValue).Number == v.Number return other.Type() == NumberValueType && other.(*NumberValue).Number == v.Number
} }
func (v *FloatValue) Get(_ string) (Value, error) { func (v *NumberValue) 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 *FloatValue) Clone() Value { func (v *NumberValue) Clone() Value {
return &FloatValue{ return &NumberValue{
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
} }
@ -386,7 +348,7 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
Name: "length", Name: "length",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
[]TypeSignature{}, []TypeSignature{},
&IntegerSignature{}, &NumberSignature{},
}, },
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
@ -395,11 +357,11 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
"at": { "at": {
Name: "at", Name: "at",
Signature: &FunctionSignature{ Signature: &FunctionSignature{
[]TypeSignature{&IntegerSignature{}}, []TypeSignature{&NumberSignature{}},
&StringSignature{}, &StringSignature{},
}, },
F: func(vm *VM, this Value, args []Value) (Value, error) { F: func(vm *VM, this Value, args []Value) (Value, error) {
i := int(args[0].(*IntegerValue).Number.Int64()) i := int(math.Floor(args[0].(*NumberValue).Number))
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")
@ -488,13 +450,13 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"at", "at",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{ []TypeSignature{
&IntegerSignature{}, &NumberSignature{},
}, },
&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].(*IntegerValue).Number.Int64()) index := int(p[0].(*NumberValue).Number)
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))
@ -509,13 +471,13 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"put", "put",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{ []TypeSignature{
&FloatSignature{}, &InnerSignature{}, &NumberSignature{}, &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].(*FloatValue).Number) i := int(args[0].(*NumberValue).Number)
v := args[1] v := args[1]
// bounds check // bounds check
@ -534,7 +496,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
"length", "length",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{}, []TypeSignature{},
&IntegerSignature{}, &NumberSignature{},
}, },
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
@ -603,7 +565,6 @@ type FunctionValue struct {
Yield TypeSignature Yield TypeSignature
Chunk *Chunk Chunk *Chunk
Parent Value Parent Value
Scope *Scope
} }
func (v *FunctionValue) Type() ValueType { func (v *FunctionValue) Type() ValueType {
@ -611,7 +572,7 @@ func (v *FunctionValue) Type() ValueType {
} }
func (v *FunctionValue) String() string { func (v *FunctionValue) String() string {
return fmt.Sprintf("<function name=%s block=%p>", v.Name, v.Chunk) return fmt.Sprintf("<function name=%s>", v.Name)
} }
func (v *FunctionValue) DebugString() string { func (v *FunctionValue) DebugString() string {
@ -620,6 +581,7 @@ func (v *FunctionValue) DebugString() string {
func (v *FunctionValue) Equals(other Value) bool { func (v *FunctionValue) Equals(other Value) bool {
return other.Type() == FunctionValueType && return other.Type() == FunctionValueType &&
v.Name == other.(*FunctionValue).Name &&
v.Chunk == other.(*FunctionValue).Chunk v.Chunk == other.(*FunctionValue).Chunk
} }
@ -634,7 +596,6 @@ func (v *FunctionValue) Clone() Value {
v.Yield, v.Yield,
v.Chunk, v.Chunk,
v.Parent, v.Parent,
v.Scope,
} }
} }
@ -676,3 +637,43 @@ func (v *BuiltinFunctionValue) Clone() Value {
v.Constant, v.Constant,
} }
} }
// VariableValue a value wrapper for variables kept on the stack
type VariableValue struct {
name string
value Value
scope Pos
}
func (v *VariableValue) Type() ValueType {
return VariableValueType
}
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 {
return v.String()
}
func (v *VariableValue) Equals(other Value) bool {
return other.Type() == VariableValueType &&
v.name == other.(*VariableValue).name &&
v.value.Equals(other.(*VariableValue).value)
}
func (v *VariableValue) Get(_ string) (Value, error) {
return nil, errors.New("variables have no properties")
}
func (v *VariableValue) Clone() Value {
return &VariableValue{
v.name,
v.value.Clone(),
v.scope,
}
}

View file

@ -21,21 +21,15 @@ 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 FloatValueType: case NumberValueType:
if got.(*FloatValue).Number != want.(*FloatValue).Number { if got.(*NumberValue).Number != want.(*NumberValue).Number {
t.Errorf("number value mismatch: got %v, want %v", got.(*FloatValue), want.(*FloatValue)) t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue))
} else { } else {
t.Logf("Both are same number (%s)", got.(*FloatValue).String()) t.Logf("Both are same number (%s)", got.(*NumberValue).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 %s, want %s", got.(*StringValue), want.(*StringValue)) t.Errorf("string value mismatch: got %v, want %v", got.(*StringValue), want.(*StringValue))
} else { } else {
t.Logf("Both are same string (%s)", got.(*StringValue).String()) t.Logf("Both are same string (%s)", got.(*StringValue).String())
} }
@ -70,6 +64,20 @@ func CompareValues(t *testing.T, got Value, want Value) {
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m) t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m)
} }
case VariableValueType:
n := got.(*VariableValue)
m := want.(*VariableValue)
if n.name != m.name {
t.Errorf("variable name mismatch: got %v, want %v", n.name, m.name)
}
if n.scope != m.scope {
t.Errorf("variable scope mismatch: got %v, want %v", n.scope, m.scope)
}
CompareValues(t, n.value, m.value)
case ListValueType: case ListValueType:
n := got.(*ListValue) n := got.(*ListValue)
m := want.(*ListValue) m := want.(*ListValue)

View file

@ -7,9 +7,7 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"math/big"
"os" "os"
"strconv"
"strings" "strings"
) )
@ -22,52 +20,30 @@ const (
// InstructionPop pop and delete the first item on the stack // InstructionPop pop and delete the first item on the stack
InstructionPop InstructionPop
// InstructionAddFloat pop two floats and add them // InstructionAdd pop two and add them
InstructionAddFloat InstructionAdd
// InstructionSubFloat pop two floats and subtract the second from the first // InstructionSub pop two and subtract the second from the first
InstructionSubFloat InstructionSub
// InstructionMulFloat pop two floats and multiply them // InstructionMul pop two and multiply them
InstructionMulFloat InstructionMul
// InstructionDivFloat pop two floats and divide the second by the first // InstructionDiv pop two and divide the second by the first
InstructionDivFloat InstructionDiv
// InstructionNegateFloat negate the float; if it was positive, make it negative, and vice versa. // InstructionNegate negate the value; if it was positive, make it negative, and vice versa.
InstructionNegateFloat InstructionNegate
// 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
// InstructionLessFloat pops two from stack, pushes whether the lowest is less than the highest InstructionLess
InstructionLessFloat // InstructionLessOrEqual pops two from stack, pushes whether the lowest is less or equal than the highest
// InstructionLessOrEqualFloat pops two from stack, pushes whether the lowest is less or equal than the highest InstructionLessOrEqual
InstructionLessOrEqualFloat // InstructionGreater pops two from stack, pushes whether the lowest is greater than the highest
// InstructionGreaterFloat pops two from stack, pushes whether the lowest is greater than the highest InstructionGreater
InstructionGreaterFloat // InstructionGreaterOrEqual pops two from stack, pushes whether the lowest is greater or equal than the highest
// InstructionGreaterOrEqualFloat pops two from stack, pushes whether the lowest is greater or equal than the highest InstructionGreaterOrEqual
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
@ -104,8 +80,6 @@ const (
// InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1) // InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1)
InstructionSwap InstructionSwap
// InstructionDuplicate push a copy of the item on top of the stack (1 -> 1, 1)
InstructionDuplicate
// InstructionAnd pop two booleans and push true if both are true // InstructionAnd pop two booleans and push true if both are true
InstructionAnd InstructionAnd
@ -142,48 +116,30 @@ func (b Bytecode) String() string {
return "RETURN" return "RETURN"
case InstructionPop: case InstructionPop:
return "POP" return "POP"
case InstructionAddFloat: case InstructionAdd:
return "ADD_FLOAT" return "ADD"
case InstructionSubFloat: case InstructionSub:
return "SUB_FLOAT" return "SUB"
case InstructionMulFloat: case InstructionMul:
return "MUL_FLOAT" return "MUL"
case InstructionDivFloat: case InstructionDiv:
return "DIV_FLOAT" return "DIV"
case InstructionNegateFloat: case InstructionNegate:
return "NEGATE_FLOAT" return "NEGATE"
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 InstructionLessFloat: case InstructionLess:
return "LESS_FLOAT" return "LESS"
case InstructionLessOrEqualFloat: case InstructionLessOrEqual:
return "LESS_OR_EQUAL_FLOAT" return "LESS_OR_EQUAL"
case InstructionGreaterFloat: case InstructionGreater:
return "GREATER_FLOAT" return "GREATER_OR_EQUAL"
case InstructionGreaterOrEqualFloat: case InstructionGreaterOrEqual:
return "GREATER_OR_EQUAL_FLOAT" return "GREATER_OR_EQUAL"
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:
@ -236,8 +192,6 @@ func (b Bytecode) String() string {
return "ACCESS_PROPERTY" return "ACCESS_PROPERTY"
case InstructionConcatLists: case InstructionConcatLists:
return "CONCAT_LISTS" return "CONCAT_LISTS"
case InstructionDuplicate:
return "DUPLICATE"
} }
return "UNDEFINED" return "UNDEFINED"
} }
@ -271,30 +225,6 @@ func (c Chunk) String() string {
return b.String() return b.String()
} }
func (c *Chunk) Equals(other *Chunk) bool {
if len(c.Bytecode) != len(other.Bytecode) {
return false
}
for i, bc := range c.Bytecode {
if other.Bytecode[i] != bc {
return false
}
}
if len(c.Constants) != len(other.Constants) {
return false
}
for i := 0; i < len(c.Constants); i++ {
if other.Constants[i] != c.Constants[i] {
return false
}
}
return true
}
func NewChunk(bytecode []Bytecode, constants []Value) *Chunk { func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
return &Chunk{bytecode, constants} return &Chunk{bytecode, constants}
} }
@ -302,7 +232,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(&FloatValue{0}) gob.Register(&NumberValue{0})
gob.Register(&FunctionValue{ gob.Register(&FunctionValue{
Name: "", Name: "",
Params: nil, Params: nil,
@ -311,7 +241,7 @@ func RegisterGOBTypes() {
// Signatures // Signatures
gob.Register(&NilSignature{}) gob.Register(&NilSignature{})
gob.Register(&FloatSignature{}) gob.Register(&NumberSignature{})
gob.Register(&StringSignature{}) gob.Register(&StringSignature{})
gob.Register(&FunctionSignature{}) gob.Register(&FunctionSignature{})
gob.Register(&ListSignature{}) gob.Register(&ListSignature{})
@ -356,29 +286,26 @@ type VM struct {
// instruction pointer // instruction pointer
ip Pos ip Pos
scope Pos
// global variable storage // global variable storage
globals map[string]Value globals map[string]Value
// local variable storage variableEnd Pos
scope *Scope
Stack *Stack[Value] stack *Stack[Value]
call *Stack[Call] call *Stack[Call]
} }
type Scope struct {
current map[string]Value
parent *Scope
}
type Call struct { type Call struct {
chunk *Chunk chunk *Chunk
ip Pos ip Pos
scope *Scope stackEnd Pos
variableEnd Pos
scope Pos
} }
var DefaultGlobals = map[string]Value{ var DefaultGlobals = map[string]Value{
"println": &BuiltinFunctionValue{ "write": &BuiltinFunctionValue{
"write", // always remember where you come from... "write", // always remember where you come from...
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&AnySignature{}}, []TypeSignature{&AnySignature{}},
@ -386,7 +313,7 @@ var DefaultGlobals = map[string]Value{
}, },
func(_ *VM, this Value, v []Value) (Value, error) { func(_ *VM, this Value, v []Value) (Value, error) {
println(v[0].String()) println(v[0].String())
return &NilValue{}, nil return nil, nil
}, },
nil, nil,
false, false,
@ -399,7 +326,7 @@ var DefaultGlobals = map[string]Value{
}, },
func(_ *VM, this Value, v []Value) (Value, error) { func(_ *VM, this Value, v []Value) (Value, error) {
print(v[0].String()) print(v[0].String())
return &NilValue{}, nil return nil, nil
}, },
nil, nil,
false, false,
@ -441,12 +368,12 @@ var DefaultGlobals = map[string]Value{
"char": &BuiltinFunctionValue{ "char": &BuiltinFunctionValue{
"char", "char",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&IntegerSignature{}}, []TypeSignature{&NumberSignature{}},
&StringSignature{}, &StringSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
n := args[0].(*IntegerValue).Number n := args[0].(*NumberValue).Number
b := n.Bytes()[0] b := byte(n)
return &StringValue{ return &StringValue{
string([]byte{b}), string([]byte{b}),
@ -456,16 +383,16 @@ var DefaultGlobals = map[string]Value{
true, true,
}, },
"byte": &BuiltinFunctionValue{ "byte": &BuiltinFunctionValue{
"byte", "char",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&StringSignature{}}, []TypeSignature{&StringSignature{}},
&IntegerSignature{}, &NumberSignature{},
}, },
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
n := new(big.Int).SetBytes([]byte(s)) b := []byte(s)[0]
return &IntegerValue{n}, nil return &NumberValue{float64(b)}, nil
}, },
nil, nil,
true, true,
@ -526,66 +453,6 @@ 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{
@ -603,11 +470,11 @@ var DefaultGlobals = map[string]Value{
"exit": &BuiltinFunctionValue{ "exit": &BuiltinFunctionValue{
"exit", "exit",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&FloatSignature{}}, []TypeSignature{&NumberSignature{}},
&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].(*FloatValue).Number)) os.Exit(int(args[0].(*NumberValue).Number))
return &NilValue{}, nil return &NilValue{}, nil
}, },
nil, nil,
@ -616,11 +483,11 @@ var DefaultGlobals = map[string]Value{
"floor": &BuiltinFunctionValue{ "floor": &BuiltinFunctionValue{
"floor", "floor",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&FloatSignature{}}, []TypeSignature{&NumberSignature{}},
&FloatSignature{}, &NumberSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
return &FloatValue{math.Floor(args[0].(*FloatValue).Number)}, nil return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil
}, },
nil, nil,
true, true,
@ -628,11 +495,11 @@ var DefaultGlobals = map[string]Value{
"ceil": &BuiltinFunctionValue{ "ceil": &BuiltinFunctionValue{
"ceil", "ceil",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&FloatSignature{}}, []TypeSignature{&NumberSignature{}},
&FloatSignature{}, &NumberSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
return &FloatValue{math.Ceil(args[0].(*FloatValue).Number)}, nil return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil
}, },
nil, nil,
true, true,
@ -640,14 +507,14 @@ var DefaultGlobals = map[string]Value{
"roundd": &BuiltinFunctionValue{ "roundd": &BuiltinFunctionValue{
"roundd", "roundd",
&FunctionSignature{ &FunctionSignature{
[]TypeSignature{&FloatSignature{}, &FloatSignature{}}, []TypeSignature{&NumberSignature{}, &NumberSignature{}},
&FloatSignature{}, &NumberSignature{},
}, },
func(vm *VM, this Value, args []Value) (Value, error) { func(vm *VM, this Value, args []Value) (Value, error) {
x := args[0].(*FloatValue).Number x := args[0].(*NumberValue).Number
decimals := args[1].(*FloatValue).Number decimals := args[1].(*NumberValue).Number
multiplier := math.Pow(10, decimals) multiplier := math.Pow(10, decimals)
return &FloatValue{math.Round(x*multiplier) / multiplier}, nil return &NumberValue{math.Round(x*multiplier) / multiplier}, nil
}, },
nil, nil,
true, true,
@ -657,13 +524,10 @@ var DefaultGlobals = map[string]Value{
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM { func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
vm := &VM{ vm := &VM{
chunk: chunk, chunk: chunk,
Stack: NewStack[Value](stackSize), stack: NewStack[Value](stackSize),
call: NewStack[Call](callstackSize), call: NewStack[Call](callstackSize),
globals: DefaultGlobals, globals: DefaultGlobals,
scope: &Scope{
current: map[string]Value{},
},
} }
return vm return vm
@ -680,190 +544,141 @@ func (vm *VM) Next() bool {
case InstructionReturn: case InstructionReturn:
if vm.call.Current == 0 { if vm.call.Current == 0 {
return false return false
} } else {
v := vm.stack.Pop()
v := vm.Stack.Pop()
c := vm.call.Pop() c := vm.call.Pop()
// reset stack current and variable end and scope // reset stack current and variable end and scope
vm.variableEnd = c.variableEnd
vm.stack.Current = c.stackEnd
vm.scope = c.scope vm.scope = c.scope
// reset to calling position // reset to calling position
vm.ip = c.ip vm.ip = c.ip
vm.chunk = c.chunk vm.chunk = c.chunk
vm.Stack.Push(v) vm.purgeVars()
case InstructionPop: vm.stack.Push(v)
vm.Stack.Pop()
case InstructionConstant:
c := vm.ReadConstant()
if c, ok := c.(*FunctionValue); ok {
c.Scope = vm.scope
} }
vm.Stack.Push(c) case InstructionPop:
vm.stack.Pop()
case InstructionAddFloat: case InstructionConstant:
r := vm.Stack.Pop().(*FloatValue).Number vm.stack.Push(vm.ReadConstant())
l := vm.Stack.Pop().(*FloatValue).Number
vm.Stack.Push(&FloatValue{l + r}) case InstructionAdd:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionSubFloat: vm.stack.Push(&NumberValue{l + r})
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.Stack.Push(&FloatValue{l - r}) case InstructionSub:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionMulFloat: vm.stack.Push(&NumberValue{l - r})
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.Stack.Push(&FloatValue{l * r}) case InstructionMul:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionDivFloat: vm.stack.Push(&NumberValue{l * r})
r := vm.Stack.Pop().(*FloatValue).Number
l := vm.Stack.Pop().(*FloatValue).Number
vm.Stack.Push(&FloatValue{l / r}) case InstructionDiv:
r := vm.stack.Pop().(*NumberValue).Number
l := vm.stack.Pop().(*NumberValue).Number
case InstructionNegateFloat: vm.stack.Push(&NumberValue{l / r})
v := vm.Stack.Pop().(*FloatValue).Number
vm.Stack.Push(&FloatValue{-v}) case InstructionNegate:
v := vm.stack.Pop().(*NumberValue).Number
case InstructionAddInt: vm.stack.Push(&NumberValue{-v})
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(
&BoolValue{vm.Stack.Pop().Equals(vm.Stack.Pop())}, &BoolValue{vm.stack.Pop().Equals(vm.stack.Pop())},
) )
case InstructionNotEqual: case InstructionNotEqual:
vm.Stack.Push( vm.stack.Push(
&BoolValue{!vm.Stack.Pop().Equals(vm.Stack.Pop())}, &BoolValue{!vm.stack.Pop().Equals(vm.stack.Pop())},
) )
case InstructionNot: case InstructionNot:
b := vm.Stack.Pop().(*BoolValue).Boolean b := vm.stack.Pop().(*BoolValue).Boolean
vm.Stack.Push(&BoolValue{!b}) vm.stack.Push(&BoolValue{!b})
case InstructionAnd: case InstructionAnd:
r := vm.Stack.Pop().(*BoolValue).Boolean r := vm.stack.Pop().(*BoolValue).Boolean
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 InstructionOr: case InstructionOr:
r := vm.Stack.Pop().(*BoolValue).Boolean r := vm.stack.Pop().(*BoolValue).Boolean
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 InstructionLessFloat: case InstructionLess:
r := vm.Stack.Pop().(*FloatValue).Number r := vm.stack.Pop().(*NumberValue).Number
l := vm.Stack.Pop().(*FloatValue).Number l := vm.stack.Pop().(*NumberValue).Number
vm.Stack.Push(&BoolValue{l < r}) vm.stack.Push(&BoolValue{l < r})
case InstructionLessOrEqualFloat: case InstructionLessOrEqual:
r := vm.Stack.Pop().(*FloatValue).Number r := vm.stack.Pop().(*NumberValue).Number
l := vm.Stack.Pop().(*FloatValue).Number l := vm.stack.Pop().(*NumberValue).Number
vm.Stack.Push(&BoolValue{l <= r}) vm.stack.Push(&BoolValue{l <= r})
case InstructionGreaterFloat: case InstructionGreater:
r := vm.Stack.Pop().(*FloatValue).Number r := vm.stack.Pop().(*NumberValue).Number
l := vm.Stack.Pop().(*FloatValue).Number l := vm.stack.Pop().(*NumberValue).Number
vm.Stack.Push(&BoolValue{l > r}) vm.stack.Push(&BoolValue{l > r})
case InstructionGreaterOrEqualFloat: case InstructionGreaterOrEqual:
r := vm.Stack.Pop().(*FloatValue).Number r := vm.stack.Pop().(*NumberValue).Number
l := vm.Stack.Pop().(*FloatValue).Number l := vm.stack.Pop().(*NumberValue).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) {
case *FunctionValue: case *FunctionValue:
vm.call.Push(Call{ vm.call.Push(Call{
chunk: vm.chunk, chunk: vm.chunk,
ip: vm.ip, ip: vm.ip,
stackEnd: vm.stack.Current - Pos(len(f.Params)),
variableEnd: vm.variableEnd,
scope: vm.scope, scope: vm.scope,
}) })
vm.scope = f.Scope
vm.descend()
for i := len(f.Params) - 1; i >= 0; i-- { for i := len(f.Params) - 1; i >= 0; i-- {
vm.addVar(f.Params[i].Name, vm.Stack.Pop()) p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
vm.stack.items[p] = &VariableValue{
f.Params[i].Name,
vm.stack.items[p],
vm.scope,
}
} }
if f.Parent != nil { if f.Parent != nil {
vm.addVar("this", f.Parent) vm.addVar("this", f.Parent)
} }
vm.variableEnd = vm.stack.Current
vm.chunk = f.Chunk vm.chunk = f.Chunk
vm.ip = 0 vm.ip = 0
case *BuiltinFunctionValue: case *BuiltinFunctionValue:
args := make([]Value, len(f.Signature.In)) args := make([]Value, len(f.Signature.In))
for i := len(f.Signature.In) - 1; i >= 0; i-- { for i := len(f.Signature.In) - 1; i >= 0; i-- {
args[i] = vm.Stack.Pop() args[i] = vm.stack.Pop()
} }
v, err := f.F(vm, f.Parent, args) v, err := f.F(vm, f.Parent, args)
@ -871,13 +686,9 @@ func (vm *VM) Next() bool {
vm.error(err.Error()) vm.error(err.Error())
} }
if v == nil { vm.stack.Push(v)
v = &NilValue{}
}
vm.Stack.Push(v)
default: default:
vm.error(fmt.Sprintf("%s (%s) is not callable ", v.DebugString(), v.Type())) vm.error(fmt.Sprintf("value called is not a function (%s, type %T)", v.DebugString(), v))
return false return false
} }
@ -889,7 +700,7 @@ func (vm *VM) Next() bool {
case InstructionJumpFalse: case InstructionJumpFalse:
n := vm.NextU16() n := vm.NextU16()
if !vm.Stack.Pop().(*BoolValue).Boolean { if !vm.stack.Pop().(*BoolValue).Boolean {
vm.ip += Pos(n) vm.ip += Pos(n)
} }
@ -902,61 +713,67 @@ func (vm *VM) Next() bool {
return false return false
} }
vm.Stack.Push(v) vm.stack.Push(v.value)
case InstructionSetLocal: case InstructionSetLocal:
value := vm.Stack.Peek().Clone() value := vm.stack.Pop().(Value)
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
vm.setVar(name, value) v := vm.getVar(name)
if v == nil {
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
}
v.value = value.Clone()
case InstructionDeclareLocal: case InstructionDeclareLocal:
vm.addVar( vm.addVar(
vm.GetConstant(vm.NextByte()).(*StringValue).Text, vm.GetConstant(vm.NextByte()).(*StringValue).Text,
vm.Stack.Peek().Clone(), vm.stack.Pop().Clone(),
) )
case InstructionGetGlobal: case InstructionGetGlobal:
vm.Stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text]) vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
case InstructionSetGlobal: case InstructionSetGlobal:
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.Stack.Pop() vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.stack.Pop()
case InstructionTrue: case InstructionTrue:
vm.Stack.Push(&BoolValue{true}) vm.stack.Push(&BoolValue{true})
case InstructionFalse: case InstructionFalse:
vm.Stack.Push(&BoolValue{false}) vm.stack.Push(&BoolValue{false})
case InstructionNil: case InstructionNil:
vm.Stack.Push(&NilValue{}) vm.stack.Push(&NilValue{})
case InstructionFormList: case InstructionFormList:
n := int(vm.NextU16()) n := int(vm.NextU16())
items := make([]Value, n) items := make([]Value, n)
for i := n - 1; i >= 0; i-- { for i := n - 1; i >= 0; i-- {
items[i] = vm.Stack.Pop() items[i] = vm.stack.Pop()
} }
vm.Stack.Push(&ListValue{ vm.stack.Push(&ListValue{
items, items,
}) })
case InstructionNewList: case InstructionNewList:
vm.Stack.Push(&ListValue{[]Value{}}) vm.stack.Push(&ListValue{[]Value{}})
case InstructionAppend: case InstructionAppend:
value := vm.Stack.Pop() value := vm.stack.Pop()
list := vm.Stack.Pop().(*ListValue) list := vm.stack.Pop().(*ListValue)
list.Items = append(list.Items, value) list.Items = append(list.Items, value)
vm.Stack.Push(list) vm.stack.Push(list)
case InstructionConcatLists: case InstructionConcatLists:
r := vm.Stack.Pop().(*ListValue) r := vm.stack.Pop().(*ListValue)
l := vm.Stack.Pop().(*ListValue) l := vm.stack.Pop().(*ListValue)
vm.Stack.Push(&ListValue{ vm.stack.Push(&ListValue{
append(l.Items, r.Items...), append(l.Items, r.Items...),
}) })
@ -967,26 +784,23 @@ func (vm *VM) Next() bool {
vm.ascend() vm.ascend()
case InstructionStringConversion: case InstructionStringConversion:
v := vm.Stack.Pop() v := vm.stack.Pop()
vm.Stack.Push(&StringValue{v.String()}) vm.stack.Push(&StringValue{v.String()})
case InstructionStringConcatenation: case InstructionStringConcatenation:
r := vm.Stack.Pop().(*StringValue).Text r := vm.stack.Pop().(*StringValue).Text
l := vm.Stack.Pop().(*StringValue).Text l := vm.stack.Pop().(*StringValue).Text
vm.Stack.Push(&StringValue{l + r}) vm.stack.Push(&StringValue{l + r})
case InstructionSwap: case InstructionSwap:
r := vm.Stack.Pop() r := vm.stack.Pop()
l := vm.Stack.Pop() l := vm.stack.Pop()
vm.Stack.Push(r, l) vm.stack.Push(r, l)
case InstructionDuplicate:
vm.Stack.Push(vm.Stack.Peek().Clone())
case InstructionAccessProperty: case InstructionAccessProperty:
source := vm.Stack.Pop() source := vm.stack.Pop()
property := vm.ReadConstant() property := vm.ReadConstant()
member, err := source.Get(property.(*StringValue).String()) member, err := source.Get(property.(*StringValue).String())
@ -1001,19 +815,9 @@ func (vm *VM) Next() bool {
member.(*BuiltinFunctionValue).Parent = source member.(*BuiltinFunctionValue).Parent = source
} }
vm.Stack.Push(member) vm.stack.Push(member)
case InstructionBreakpoint: case InstructionBreakpoint:
/*
// I'm keeping this
s := vm.scope
log.Printf("breakpoint %d", vm.ip)
for s != nil {
log.Printf("%s", s.current)
s = s.parent
}
*/
vm.Stack.Push(&NilValue{})
default: default:
panic("invalid byte code") panic("invalid byte code")
@ -1028,12 +832,11 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
vm.call.Push(Call{ vm.call.Push(Call{
chunk: vm.chunk, chunk: vm.chunk,
ip: vm.ip, ip: vm.ip,
stackEnd: vm.stack.Current,
variableEnd: vm.variableEnd,
scope: vm.scope, scope: vm.scope,
}) })
vm.scope = f.Scope
vm.descend()
for i := 0; i < len(f.Params); i++ { for i := 0; i < len(f.Params); i++ {
vm.addVar(f.Params[i].Name, args[i]) vm.addVar(f.Params[i].Name, args[i])
} }
@ -1042,6 +845,8 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
vm.addVar("this", f.Parent) vm.addVar("this", f.Parent)
} }
vm.variableEnd = vm.stack.Current
vm.chunk = f.Chunk vm.chunk = f.Chunk
vm.ip = 0 vm.ip = 0
@ -1050,7 +855,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
vm.Next() vm.Next()
return vm.Stack.Pop(), nil return vm.stack.Pop(), nil
case *BuiltinFunctionValue: case *BuiltinFunctionValue:
return f.F(vm, f.Parent, args) return f.F(vm, f.Parent, args)
@ -1068,18 +873,6 @@ func (vm *VM) TryNextByte() (Bytecode, error) {
return 0, errors.New("there are no more instructions") return 0, errors.New("there are no more instructions")
} }
for int(vm.ip) >= len(vm.chunk.Bytecode) && vm.call.Current > 0 {
c := vm.call.Pop()
vm.ip = c.ip
vm.chunk = c.chunk
vm.scope = c.scope
}
if int(vm.ip) >= len(vm.chunk.Bytecode) {
return 0, errors.New("there are no more instructions")
}
v := vm.chunk.Bytecode[vm.ip] v := vm.chunk.Bytecode[vm.ip]
vm.ip++ vm.ip++
@ -1097,59 +890,57 @@ func (vm *VM) NextByte() Bytecode {
} }
func (vm *VM) ascend() { func (vm *VM) ascend() {
if vm.scope.parent == nil { vm.scope--
if vm.scope < 0 {
panic("invalid scope") panic("invalid scope")
} }
vm.scope = vm.scope.parent vm.purgeVars()
}
// purgeVars remove all variables not within scope
func (vm *VM) purgeVars() {
for ; vm.variableEnd > 0 && vm.stack.items[vm.variableEnd-1].(*VariableValue).scope > vm.scope; vm.variableEnd-- {
vm.stack.Pop()
}
} }
func (vm *VM) descend() { func (vm *VM) descend() {
old := vm.scope vm.scope++
vm.scope = &Scope{
map[string]Value{},
old,
}
} }
func (vm *VM) addVar(name string, value Value) { func (vm *VM) addVar(name string, value Value) {
vm.scope.current[name] = value vm.variableEnd++
vm.stack.Push(&VariableValue{
name,
value,
vm.scope,
})
} }
func (vm *VM) getVar(name string) Value { func (vm *VM) getVar(name string) *VariableValue {
s := vm.scope for i := vm.variableEnd - 1; i >= 0; i-- {
v, ok := vm.stack.items[i].(*VariableValue)
for s != nil { if !ok {
if v, ok := s.current[name]; ok { continue
}
if v.name == name {
return v return v
} }
s = s.parent
} }
return nil return nil
} }
func (vm *VM) setVar(name string, v Value) {
s := vm.scope
for s != nil {
if _, ok := s.current[name]; ok {
s.current[name] = v
break
}
s = s.parent
}
}
func (vm *VM) HasNext() bool { func (vm *VM) HasNext() bool {
return vm.ip < Pos(len(vm.chunk.Bytecode)) || vm.call.Current > 0 return vm.ip < Pos(len(vm.chunk.Bytecode))
} }
func (vm *VM) GetConstant(id Bytecode) Value { func (vm *VM) GetConstant(id Bytecode) Value {
return vm.chunk.Constants[id].Clone() return vm.chunk.Constants[id]
} }
func (vm *VM) ReadConstant() Value { func (vm *VM) ReadConstant() Value {

View file

@ -49,7 +49,7 @@ func TestNewVM(t *testing.T) {
chunk := NewChunk([]Bytecode{ chunk := NewChunk([]Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
}, []Value{ }, []Value{
&FloatValue{0}, &NumberValue{0},
}) })
stackSize := Pos(256) stackSize := Pos(256)
callstackSize := Pos(256) callstackSize := Pos(256)
@ -76,8 +76,8 @@ func TestNewVM(t *testing.T) {
} }
// should have given stack size // should have given stack size
if vm.Stack.Capacity != stackSize { if vm.stack.Capacity != stackSize {
t.Errorf("vm.stack.Capacity = %d, want %d", vm.Stack.Capacity, stackSize) t.Errorf("vm.stack.Capacity = %d, want %d", vm.stack.Capacity, stackSize)
} }
// should have given call stack size // should have given call stack size
@ -95,26 +95,23 @@ func BenchmarkNewVM(b *testing.B) {
func GetExecutionTestData() map[string]struct { func GetExecutionTestData() map[string]struct {
chunk *Chunk chunk *Chunk
resultingStack []Value resultingStack []Value
resultingScope []map[string]Value
} { } {
return map[string]struct { return map[string]struct {
chunk *Chunk chunk *Chunk
resultingStack []Value resultingStack []Value
resultingScope []map[string]Value
}{ }{
"two_plus_one": { "two_plus_one": {
NewChunk([]Bytecode{ NewChunk([]Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionConstant, 1, InstructionConstant, 1,
InstructionAddFloat, InstructionAdd,
}, },
[]Value{ []Value{
&FloatValue{1}, &FloatValue{2}, &NumberValue{1}, &NumberValue{2},
}), }),
[]Value{ []Value{
&FloatValue{3}, &NumberValue{3},
}, },
[]map[string]Value{},
}, },
"push_constant": { "push_constant": {
NewChunk( NewChunk(
@ -122,13 +119,12 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 0, InstructionConstant, 0,
}, },
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
[]map[string]Value{},
}, },
"push_true": { "push_true": {
NewChunk( NewChunk(
@ -140,7 +136,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&BoolValue{true}, &BoolValue{true},
}, },
[]map[string]Value{},
}, },
"push_false": { "push_false": {
NewChunk( NewChunk(
@ -152,7 +147,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&BoolValue{false}, &BoolValue{false},
}, },
[]map[string]Value{},
}, },
"push_nil": { "push_nil": {
NewChunk( NewChunk(
@ -164,7 +158,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&NilValue{}, &NilValue{},
}, },
[]map[string]Value{},
}, },
"empty": { "empty": {
NewChunk( NewChunk(
@ -172,7 +165,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{}, []Value{},
), ),
[]Value{}, []Value{},
[]map[string]Value{},
}, },
// (2 + 1) * 5 / (6 - 2) // (2 + 1) * 5 / (6 - 2)
"full_arithmetic": { "full_arithmetic": {
@ -180,22 +172,21 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionConstant, 1, InstructionConstant, 1,
InstructionAddFloat, InstructionAdd,
InstructionConstant, 2, InstructionConstant, 2,
InstructionMulFloat, InstructionMul,
InstructionConstant, 3, InstructionConstant, 3,
InstructionConstant, 0, InstructionConstant, 0,
InstructionSubFloat, InstructionSub,
InstructionDivFloat, InstructionDiv,
}, },
[]Value{ []Value{
&FloatValue{2}, &FloatValue{1}, &FloatValue{5}, &FloatValue{6}, &NumberValue{2}, &NumberValue{1}, &NumberValue{5}, &NumberValue{6},
}, },
), ),
[]Value{ []Value{
&FloatValue{3.75}, &NumberValue{3.75},
}, },
[]map[string]Value{},
}, },
"equality_true": { "equality_true": {
NewChunk( NewChunk(
@ -205,13 +196,12 @@ func GetExecutionTestData() map[string]struct {
InstructionEquals, InstructionEquals,
}, },
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&BoolValue{true}, &BoolValue{true},
}, },
[]map[string]Value{},
}, },
"equality_false": { "equality_false": {
NewChunk( NewChunk(
@ -221,13 +211,12 @@ func GetExecutionTestData() map[string]struct {
InstructionEquals, InstructionEquals,
}, },
[]Value{ []Value{
&FloatValue{1}, &FloatValue{2}, &NumberValue{1}, &NumberValue{2},
}, },
), ),
[]Value{ []Value{
&BoolValue{false}, &BoolValue{false},
}, },
[]map[string]Value{},
}, },
"inequality_false": { "inequality_false": {
NewChunk( NewChunk(
@ -237,13 +226,12 @@ func GetExecutionTestData() map[string]struct {
InstructionNotEqual, InstructionNotEqual,
}, },
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&BoolValue{false}, &BoolValue{false},
}, },
[]map[string]Value{},
}, },
"inequality_true": { "inequality_true": {
NewChunk( NewChunk(
@ -253,13 +241,12 @@ func GetExecutionTestData() map[string]struct {
InstructionNotEqual, InstructionNotEqual,
}, },
[]Value{ []Value{
&FloatValue{1}, &FloatValue{2}, &NumberValue{1}, &NumberValue{2},
}, },
), ),
[]Value{ []Value{
&BoolValue{true}, &BoolValue{true},
}, },
[]map[string]Value{},
}, },
"not_true": { "not_true": {
NewChunk( NewChunk(
@ -272,7 +259,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&BoolValue{false}, &BoolValue{false},
}, },
[]map[string]Value{},
}, },
"not_false": { "not_false": {
NewChunk( NewChunk(
@ -285,7 +271,6 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&BoolValue{true}, &BoolValue{true},
}, },
[]map[string]Value{},
}, },
"jump": { "jump": {
NewChunk( NewChunk(
@ -295,13 +280,12 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &NumberValue{0}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
[]map[string]Value{},
}, },
"jump_false/false": { "jump_false/false": {
NewChunk( NewChunk(
@ -312,13 +296,12 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &NumberValue{0}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
}, },
[]map[string]Value{},
}, },
"jump_false/true": { "jump_false/true": {
NewChunk( NewChunk(
@ -329,13 +312,12 @@ func GetExecutionTestData() map[string]struct {
InstructionConstant, 1, // should execute InstructionConstant, 1, // should execute
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &NumberValue{0}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &NumberValue{0}, &NumberValue{1},
}, },
[]map[string]Value{},
}, },
"declare_local": { "declare_local": {
NewChunk( NewChunk(
@ -344,13 +326,14 @@ func GetExecutionTestData() map[string]struct {
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
}, },
[]Value{ []Value{
&FloatValue{0}, &StringValue{"a"}, &NumberValue{0}, &StringValue{"a"},
}, },
), ),
[]Value{&FloatValue{0}}, []Value{
[]map[string]Value{ &VariableValue{
{ "a",
"a": &FloatValue{0}, &NumberValue{0},
0,
}, },
}, },
}, },
@ -359,19 +342,18 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
InstructionPop,
InstructionConstant, 2, InstructionConstant, 2,
InstructionSetLocal, 1, // reassign InstructionSetLocal, 1, // reassign
InstructionPop,
}, },
[]Value{ []Value{
&FloatValue{0}, &StringValue{"a"}, &FloatValue{1}, &NumberValue{0}, &StringValue{"a"}, &NumberValue{1},
}, },
), ),
[]Value{}, []Value{
[]map[string]Value{ &VariableValue{
{ "a",
"a": &FloatValue{1}, &NumberValue{1},
0,
}, },
}, },
}, },
@ -380,18 +362,19 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
InstructionGetLocal, 1, // reassign
}, },
[]Value{ []Value{
&FloatValue{0}, &StringValue{"a"}, &NumberValue{0}, &StringValue{"a"},
}, },
), ),
[]Value{ []Value{
&FloatValue{0}, &VariableValue{
}, "a",
[]map[string]Value{ &NumberValue{0},
{ 0,
"a": &FloatValue{0},
}, },
&NumberValue{0},
}, },
}, },
"get_reassigned_local": { "get_reassigned_local": {
@ -399,25 +382,23 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
InstructionPop,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionConstant, 2, InstructionConstant, 2,
InstructionSetLocal, 1, // reassign InstructionSetLocal, 1, // reassign
InstructionPop,
InstructionGetLocal, 1, InstructionGetLocal, 1,
}, },
[]Value{ []Value{
&FloatValue{0}, &StringValue{"a"}, &FloatValue{1}, &NumberValue{0}, &StringValue{"a"}, &NumberValue{1},
}, },
), ),
[]Value{ []Value{
&FloatValue{0}, &VariableValue{
&FloatValue{1}, "a",
}, &NumberValue{1},
[]map[string]Value{ 0,
{
"a": &FloatValue{1},
}, },
&NumberValue{0},
&NumberValue{1},
}, },
}, },
"variable_scope": { "variable_scope": {
@ -425,28 +406,26 @@ func GetExecutionTestData() map[string]struct {
[]Bytecode{ []Bytecode{
InstructionConstant, 0, InstructionConstant, 0,
InstructionDeclareLocal, 1, InstructionDeclareLocal, 1,
InstructionPop,
InstructionDescend, InstructionDescend,
InstructionConstant, 2, InstructionConstant, 2,
InstructionDeclareLocal, 3, InstructionDeclareLocal, 3,
InstructionPop,
InstructionDescend, InstructionDescend,
InstructionConstant, 4, InstructionConstant, 4,
InstructionDeclareLocal, 5, InstructionDeclareLocal, 5,
InstructionPop,
InstructionAscend, InstructionAscend,
InstructionAscend, InstructionAscend,
}, },
[]Value{ []Value{
&FloatValue{0}, &StringValue{"a"}, &NumberValue{0}, &StringValue{"a"},
&FloatValue{1}, &StringValue{"b"}, &NumberValue{1}, &StringValue{"b"},
&FloatValue{2}, &StringValue{"c"}, &NumberValue{2}, &StringValue{"c"},
}, },
), ),
[]Value{}, []Value{
[]map[string]Value{ &VariableValue{
{ "a",
"a": &FloatValue{0}, &NumberValue{0},
0,
}, },
}, },
}, },
@ -459,25 +438,25 @@ func GetExecutionTestData() map[string]struct {
InstructionCall, InstructionCall,
}, },
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
&FunctionValue{ &FunctionValue{
Name: "sum", Name: "sum",
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 1, InstructionGetLocal, 1,
InstructionAddFloat, InstructionAdd,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -488,34 +467,32 @@ func GetExecutionTestData() map[string]struct {
}, },
), ),
[]Value{ []Value{
&FloatValue{3}, &NumberValue{3},
}, },
[]map[string]Value{},
}, },
"function_calling_function": { "function_calling_function": {
NewChunk( NewChunk(
[]Bytecode{ []Bytecode{
InstructionConstant, 3, InstructionConstant, 3,
InstructionDeclareLocal, 4, InstructionDeclareLocal, 4,
InstructionPop,
InstructionConstant, 0, InstructionConstant, 0,
InstructionConstant, 1, InstructionConstant, 1,
InstructionConstant, 2, InstructionConstant, 2,
InstructionCall, InstructionCall,
}, },
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
&FunctionValue{ &FunctionValue{
Name: "sum", Name: "sum",
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"a", "a",
&FloatSignature{}, &NumberSignature{},
}, },
{ {
"b", "b",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
@ -524,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
InstructionAddFloat, InstructionAdd,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -537,14 +514,14 @@ func GetExecutionTestData() map[string]struct {
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"n", "n",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionMulFloat, InstructionMul,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -556,23 +533,21 @@ func GetExecutionTestData() map[string]struct {
}, },
), ),
[]Value{ []Value{
&FloatValue{5}, &VariableValue{
}, "square",
[]map[string]Value{ &FunctionValue{
{
"square": &FunctionValue{
Name: "square", Name: "square",
Params: []FunctionParameter{ Params: []FunctionParameter{
{ {
"n", "n",
&FloatSignature{}, &NumberSignature{},
}, },
}, },
Chunk: NewChunk( Chunk: NewChunk(
[]Bytecode{ []Bytecode{
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionGetLocal, 0, InstructionGetLocal, 0,
InstructionMulFloat, InstructionMul,
InstructionReturn, InstructionReturn,
}, },
[]Value{ []Value{
@ -580,7 +555,9 @@ func GetExecutionTestData() map[string]struct {
}, },
), ),
}, },
0,
}, },
&NumberValue{5},
}, },
}, },
"list_concat": { "list_concat": {
@ -593,13 +570,13 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&ListValue{ &ListValue{
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
}, },
}, },
&ListValue{ &ListValue{
[]Value{ []Value{
&FloatValue{3}, &NumberValue{3},
}, },
}, },
}, },
@ -607,13 +584,12 @@ func GetExecutionTestData() map[string]struct {
[]Value{ []Value{
&ListValue{ &ListValue{
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
&FloatValue{3}, &NumberValue{3},
}, },
}, },
}, },
[]map[string]Value{},
}, },
} }
} }
@ -628,7 +604,7 @@ func TestVM_Execution(t *testing.T) {
for vm.Next() { for vm.Next() {
} }
CompareStacks(t, test.resultingStack, vm.Stack) CompareStacks(t, test.resultingStack, vm.stack)
}) })
} }
} }
@ -654,7 +630,7 @@ func TestVM_NextByte(t *testing.T) {
InstructionConstant, 0, InstructionConstant, 0,
}, },
[]Value{ []Value{
&FloatValue{0}, &NumberValue{0},
}, },
), ),
16, 16,
@ -765,7 +741,7 @@ func TestVM_Jump(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &FloatValue{2}, &NumberValue{0}, &NumberValue{1}, &NumberValue{2},
}, },
), ),
16, 16,
@ -790,7 +766,7 @@ func TestVM_JumpFalse(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &FloatValue{2}, &NumberValue{0}, &NumberValue{1}, &NumberValue{2},
}, },
), ),
16, 16,
@ -816,7 +792,7 @@ func TestVM_DontJumpFalse(t *testing.T) {
InstructionConstant, 2, InstructionConstant, 2,
}, },
[]Value{ []Value{
&FloatValue{0}, &FloatValue{1}, &FloatValue{2}, &NumberValue{0}, &NumberValue{1}, &NumberValue{2},
}, },
), ),
16, 16,

View file

@ -1,11 +0,0 @@
fn counter() -> (fn() -> int) {
i := 0
fn() -> int { i = i + 1 }
}
next := counter()
println(next())
println(next())
println(next())

View file

@ -7,19 +7,19 @@
terms := 1000000 terms := 1000000
# The running sum of terms # The running sum of terms
tot := 0.0 tot := 0
n := 1 n := 1
while n <= terms { while n <= terms {
tot = tot + 1.0/float(n*n) tot = tot + 1 / (n*n)
n = n + 1 n = n + 1
} }
tot = tot * 6.0 tot = tot * 6
# get the absolute value of a number # get the absolute value of a number
fn abs(x: float) -> float { func abs(x: number) number {
if x < 0.0 { if x < 0 {
return -x return -x
} }
return x return x
@ -30,19 +30,19 @@ fn abs(x: float) -> float {
# see: https://en.wikipedia.org/wiki/Newton's_method # see: https://en.wikipedia.org/wiki/Newton's_method
# The required accuracy # The required accuracy
SQRT_ACC := 0.00000001 SQRT_ACC := 0.00000001
fn sqrt(x: float) -> float { func sqrt(x: number) number {
pg := 0.0 # previous guess pg := 0 # previous guess
g := 1.0 # current guess g := 1 # current guess
while abs(pg - g) >= SQRT_ACC { while abs(pg - g) >= SQRT_ACC {
pg = g pg = g
g = (pg + x/pg)/2.0 g = (pg + tot/pg)/2
} }
return g return g
} }
pi := sqrt(tot) tot = sqrt(tot)
# output the result # output the result
println(pi) write(str(tot))

View file

@ -13,5 +13,3 @@ _slow_blink := CSI + "5m"
_rapid_blink := CSI + "6m" _rapid_blink := CSI + "6m"
_strike := CSI + "9m" _strike := CSI + "9m"
_primary_font := CSI + "10m" _primary_font := CSI + "10m"
fn red(s: string) -> string {}

View file

@ -1,5 +1,5 @@
fn map(list: [any], f: fn(any) -> any) -> [any] { func map(list: list[any], f: func(any)any) list[any] {
out := [] out := []
i := 0 i := 0

View file

@ -7,7 +7,7 @@ E := 2.718281828459045235360287471352
# Get the absolute value of a number. If x is negative, the returned # Get the absolute value of a number. If x is negative, the returned
# value is positive and equal to `-x`. If x is positive or zero, the # value is positive and equal to `-x`. If x is positive or zero, the
# returned value is x. # returned value is x.
fn absf(x: float) -> float { func abs(x: number) number {
# if the number is negative # if the number is negative
if x < 0 { if x < 0 {
# negate it so it's positive # negate it so it's positive
@ -17,23 +17,15 @@ fn absf(x: float) -> float {
return x return x
} }
fn absi(n: int) -> int {
if n < 0 {
-n
} else {
n
}
}
DERIVE_DX := 0.00000001 DERIVE_DX := 0.00000001
fn derive(f: fn(float) -> float, x: float) float { func derive(f: func(number)number, x: number) number {
return (f(x + DERIVE_DX) - f(x))/DERIVE_DX return (f(x + DERIVE_DX) - f(x))/DERIVE_DX
} }
NEWTONS_ACC := 0.000000000001 NEWTONS_ACC := 0.000000000001
fn newtons(f: fn(float) -> float) -> float { func newtons(f: func(number)number) number {
pg := 0.0 pg := 0
g := 1.0 g := 1
while abs(g - pg) > NEWTONS_ACC { while abs(g - pg) > NEWTONS_ACC {
pg = g pg = g
@ -49,9 +41,9 @@ MAX_SQRT_DX := 0.0000001
# x: number # x: number
# Calculate the approximate square root using newton's method until # Calculate the approximate square root using newton's method until
# the accuracy has increased by less than the variable `MAX_SQRT_DX`. # the accuracy has increased by less than the variable `MAX_SQRT_DX`.
fn sqrt(x: float) -> float { func sqrt(x: number) number {
ng := x ng := x
g := 1.0 g := 1
while abs(g - ng) > MAX_SQRT_DX { while abs(g - ng) > MAX_SQRT_DX {
g = ng g = ng
@ -59,6 +51,8 @@ fn sqrt(x: float) -> float {
# create new guess # create new guess
ng = (g + x / g) / 2 ng = (g + x / g) / 2
} }
return g
} }
# floor(x) # floor(x)
@ -78,7 +72,7 @@ fn sqrt(x: float) -> float {
# round(x) # round(x)
# x: number # x: number
# Return the closest whole number to the value x. # Return the closest whole number to the value x.
fn round(x: float) -> float { func round(x: number) number {
f := floor(x) f := floor(x)
if x - f > 0.5 { if x - f > 0.5 {
@ -92,7 +86,7 @@ fn round(x: float) -> float {
# x: number; any number # x: number; any number
# n: number; the number to divide by # n: number; the number to divide by
# Return the rest from a division of x by n. # Return the rest from a division of x by n.
fn mod(x: float, n: float) -> float { func mod(x: number, n: number) number {
if x == 0 { if x == 0 {
return 0 return 0
} }
@ -116,18 +110,17 @@ fn mod(x: float, n: float) -> float {
# This value is only reasonable if 0<x<1. # This value is only reasonable if 0<x<1.
# It is approximated using the taylor series of e**x. # It is approximated using the taylor series of e**x.
SM_EXP_ACC := 0.00000000001 SM_EXP_ACC := 0.00000000001
fn sm_exp(x: float) -> float { func sm_exp(x: number) number {
p_tot := 0.0 p_tot := 0
tot := 1.0 tot := 1
n := 1 n := 1
x_pow := x x_pow := x
f := 1.0 f := 1
while abs(tot - p_tot) > SM_EXP_ACC { while abs(tot - p_tot) > SM_EXP_ACC {
p_tot = tot p_tot = tot
t := x_pow / f t := x_pow / f
tot = tot + t tot = tot + t
f = f * float(n+1) f = f * (n+1)
x_pow = x_pow * x x_pow = x_pow * x
n = n + 1 n = n + 1
} }
@ -138,22 +131,22 @@ fn sm_exp(x: float) -> float {
# exp(x) # exp(x)
# x: number; any number # x: number; any number
# Get an approximate value of e raised to the power of x. # Get an approximate value of e raised to the power of x.
fn exp(x: float) -> float { func exp(x: number) number {
n := abs(x) n := abs(x)
tot := 1.0 tot := 1
while n >= 1 { while n >= 1 {
tot = tot * E tot = tot * E
n = n - 1 n = n - 1
} }
if n > 0.0 { if n > 0 {
tot = tot * sm_exp(n) tot = tot * sm_exp(n)
} }
if x < 0 { if x < 0 {
1.0/tot return 1/tot
} else { } else {
tot return tot
} }
} }
@ -162,9 +155,9 @@ fn exp(x: float) -> float {
# Get the approximate value of the natural logarithm # Get the approximate value of the natural logarithm
# This function uses newton's method to approximate. # This function uses newton's method to approximate.
LN_ACC := 0.0000000001 LN_ACC := 0.0000000001
fn ln(x: float) -> float { func ln(x: number) number {
pg := 0.0 pg := 0
g := 1.0 g := 1
while abs(pg - g) > LN_ACC { while abs(pg - g) > LN_ACC {
pg = g pg = g
@ -178,7 +171,7 @@ fn ln(x: float) -> float {
# x: number; any number. The base # x: number; any number. The base
# p: number; the value of the exponent # p: number; the value of the exponent
# Raise any number to any power (x^p) # Raise any number to any power (x^p)
fn pow(x: float, p: float) -> float { func pow(x: number, p: number) number {
return exp(p*ln(x)) return exp(p*ln(x))
} }
@ -188,15 +181,15 @@ fn pow(x: float, p: float) -> float {
# Calculate the approximate value of the logarithm # Calculate the approximate value of the logarithm
# of a with b as base. # of a with b as base.
LOG_ACC := 0.0000001 LOG_ACC := 0.0000001
fn log(a: float, b: float) -> float { func log(a: number, b: number) number {
ln_b := ln(b) ln_b := ln(b)
pg := 0.0 pg := 0
g := 1.0 g := 1
while abs(g - pg) > LOG_ACC { while abs(g - pg) > LOG_ACC {
pg = g pg = g
g = pg - 1.0/ln_b - a/(ln_b*pow(b, pg)) g = pg - 1/ln_b - a/(ln_b*pow(b, pg))
} }
return g return g
@ -206,9 +199,9 @@ fn log(a: float, b: float) -> float {
# x: number; an angle in radians # x: number; an angle in radians
# Get the sine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine # Get the sine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine
# TODO: use hashmap with precomputed values and linear interpolation # TODO: use hashmap with precomputed values and linear interpolation
fn sin(x: float) -> float { func sin(x: number) number {
f := 1.0 f := 1
x = mod(x, 2.0*PI) x = mod(x, 2*PI)
if x > PI { if x > PI {
x = PI - x x = PI - x
f = -1 f = -1
@ -216,13 +209,13 @@ fn sin(x: float) -> float {
# compute sine with a taylor series mock function of sine (valid between -pi and +pi) # compute sine with a taylor series mock function of sine (valid between -pi and +pi)
tot := x tot := x
l := 1.0 l := 1
i := 1.0 i := 1
s := -1.0 s := -1
while i <= 19 { while i <= 19 {
i = i + 2.0 i = i + 2
l = s * l * x / i / (i-1.0) l = s * l * x / i / (i-1)
tot = tot + l tot = tot + l

View file

@ -1,5 +1,5 @@
assertEq(1+1, 2) assertEq(1+1, 2)
assertEq(3*2, 6) assertEq(3*2, 6)
assertEq(3.0/4.0, 0.75) assertEq(3/4, 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([]int, []int) assertEq([]number, []number)
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 := []int list := []number
x := 1 x := 1
while x <= 1000 { while x <= 1000 {
list.append(x) list.append(x)
assertEq(list.reduce(fn(tot: int, a: int) -> int { assertEq(list.reduce(func(tot: number, a: number) number {
return tot + a return tot + a
}, 0), x*(x + 1)/2) }, 0), x*(x + 1)/2)
x = x + 1 x = x + 1
} }
fn sum(a: int, b: int) -> int { func sum(a: number, b: number) number {
return a + b return a + b
} }
list = []int list = []number
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
] ]
fn fib(n: int) -> int { func fib(n: number) number {
if n < 2 { if n < 2 {
return n return n
} }
@ -29,4 +29,4 @@ while x < fibonacci_numbers.length() {
x = x + 1 x = x + 1
} }
println("") write("")

View file

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

View file

@ -1,8 +1,12 @@
fn sum(a: int, b: int) -> int { func sum(a: number, b: number) number {
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), "int") assertEq(type(1), "number")
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[int]") assertEq(type([0, 1]), "list[number]")