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{}}, []Value{
[]map[string]Value{ &VariableValue{
{"a": &ListValue{ "a",
[]Value{ &ListValue{
&FloatValue{1}, []Value{
&FloatValue{2}, &NumberValue{1},
&NumberValue{2},
},
}, },
}}, 0,
},
}, },
}, },
"constant_list_concat": { "constant_list_concat": {
"a := [1, 2] + [3]\n{}", "a := [1, 2] + [3]",
[]Value{&NilValue{}}, []Value{
[]map[string]Value{ &VariableValue{
{"a": &ListValue{ "a",
[]Value{ &ListValue{
&IntegerValue{big.NewInt(1)}, []Value{
&IntegerValue{big.NewInt(2)}, &NumberValue{1},
&IntegerValue{big.NewInt(3)}, &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{}}, []Value{
[]map[string]Value{ &VariableValue{
{ "a",
"a": &ListValue{ &ListValue{
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
}, },
}, },
"b": &ListValue{ 0,
},
&VariableValue{
"b",
&ListValue{
[]Value{ []Value{
&FloatValue{1}, &NumberValue{1},
&FloatValue{2}, &NumberValue{2},
&FloatValue{3}, &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
// this would jump over the else/otherwise block in the code var jumpOverElse Pos
c.add(InstructionJump) if n.otherwise != nil {
jumpOverElse := c.ip // this would jump over the else/otherwise block in the code
c.advance(2) c.add(InstructionJump)
jumpOverElse = c.ip
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,12 +337,9 @@ func (c *Compiler) compile(tree Node) error {
if err != nil { if err != nil {
return err return err
} }
} else { c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
c.add(InstructionNil)
} }
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,47 +397,26 @@ func (c *Compiler) compile(tree Node) error {
case AssignNodeType: case AssignNodeType:
n := tree.(*AssignNode) n := tree.(*AssignNode)
switch n.dest.Type() { if n.name == "_" {
case ReferenceNodeType: // allow non-ish statements
d := n.dest.(*ReferenceNode) err := c.compile(n.value)
if err != nil {
if d.name == "_" { return err
return c.compile(n.value) }
} c.add(InstructionPop)
} else {
if n.declare && c.isVarDeclaredHere(d.name) { if n.declare && c.isVarDeclaredHere(n.name) {
return c.error(fmt.Sprintf("%s is already declared in this scope", d.name), n) return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
} }
if err := c.addSetVar(d.name, n.value, n.declare); err != nil { err := c.addSetVar(n.name, n.value, n.declare)
if err != nil {
return err return err
} }
default:
return c.error(fmt.Sprintf("cannot assign to %s", n.dest.Type()), n.dest)
} }
/* case CallNodeType:
if n.name == "_" { n := tree.(*CallNode)
// allow non-ish statements
err := c.compile(n.value)
if err != nil {
return err
}
c.add(InstructionPop)
} else {
if n.declare && c.isVarDeclaredHere(n.name) {
return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
}
err := c.addSetVar(n.name, n.value, n.declare)
if err != nil {
return err
}
}
*/
case InvokeNodeType:
n := tree.(*InvokeNode)
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
} }
res, err := c.deduceSignature(binary)
if err != nil {
return err
}
inType, err := c.deduceSignature(binary.Left) // type(left) == type(right) because res is valid
if err != nil {
return err
}
switch binary.BinaryOperation { switch binary.BinaryOperation {
case BinaryAddition: case BinaryAddition:
res, err := c.deduceSignature(binary)
if err != nil {
return err
}
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 return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
} }
if l.Type() == TypeFloat { return &NumberSignature{}, nil
return &FloatSignature{}, nil
}
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
case BinaryAddition: case BinaryAddition:
switch l.Type() { switch l.Type() {
case TypeString: case TypeString:
return &StringSignature{}, nil return &StringSignature{}, nil
case 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 return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
} else if sig.Type() == TypeInteger {
return &IntegerSignature{}, nil
} }
return &NumberSignature{}, nil
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
case UnaryNot: case UnaryNot:
if sig.Type() != TypeBoolean { if sig.Type() != TypeBoolean {
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n) return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n)
@ -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,37 +929,29 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
case AssignNodeType: case AssignNodeType:
n := tree.(*AssignNode) n := tree.(*AssignNode)
switch n.dest.Type() { if !n.declare {
case ReferenceNodeType: prev, err := c.getVarSignature(n.name, n)
name := n.dest.(*ReferenceNode).name if err != nil {
return err
if !n.declare {
prev, err := c.getVarSignature(name, n)
if err != nil {
return err
}
sig, err := c.deduceSignature(n.value)
if err != nil {
return err
}
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 nil
} }
sig, err := c.deduceSignature(n.value) sig, err := c.deduceSignature(n.value)
if err != nil { if err != nil {
return err return err
} }
c.registerVar(name, sig)
default: if !sig.Matches(prev) {
return c.error("can neither assign nor declare to", n.dest) 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
} }
sig, err := c.deduceSignature(n.value)
if err != nil {
return err
}
c.registerVar(n.name, sig)
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')
@ -76,13 +75,12 @@ func (p ParsingError) Format() string {
} }
type Parser struct { type Parser struct {
source string source string
trace []string trace []string
tokens []Token tokens []Token
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]
p.pos++
} else { } else {
p.curr = nil panic("no more tokens")
} }
p.pos++
} }
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 err != nil {
if p.accept(TokenArrow) { return nil, err
out, err = p.parseSignature()
if err != nil {
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)

File diff suppressed because it is too large Load diff

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