Compare commits
No commits in common. "2c354c4a967e64309a5719b4a3785eba2daa8b6a" and "10f55313b097c924de6c31e9ee2c3b80e6597c50" have entirely different histories.
2c354c4a96
...
10f55313b0
30 changed files with 967 additions and 1956 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
120
core/all_test.go
120
core/all_test.go
|
|
@ -1,73 +1,113 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AllTestCase struct {
|
type AllTestCase struct {
|
||||||
src string
|
src string
|
||||||
expectedStack []Value
|
expectedStack []Value
|
||||||
expectedScope []map[string]Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllTestCases() map[string]AllTestCase {
|
func GetAllTestCases() map[string]AllTestCase {
|
||||||
return map[string]AllTestCase{
|
return map[string]AllTestCase{
|
||||||
"constant_number": {
|
"constant_number": {
|
||||||
"a := 1",
|
"a := 1",
|
||||||
[]Value{&IntegerValue{new(big.Int).SetInt64(1)}},
|
[]Value{
|
||||||
[]map[string]Value{
|
&VariableValue{
|
||||||
{"a": &IntegerValue{new(big.Int).SetInt64(1)}},
|
"a",
|
||||||
|
&NumberValue{1},
|
||||||
|
0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"func": {
|
"func": {
|
||||||
"fn sum(a: int, b: int) -> int {\n\treturn a + b\n}\nres := sum(1, 2)",
|
"func sum(a: number, b: number) number {\n\treturn a + b\n}\n_ = sum(1, 2)",
|
||||||
[]Value{&IntegerValue{new(big.Int).SetInt64(3)}},
|
[]Value{
|
||||||
[]map[string]Value{},
|
&VariableValue{
|
||||||
|
"sum",
|
||||||
|
&FunctionValue{
|
||||||
|
Name: "sum",
|
||||||
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Chunk: &Chunk{
|
||||||
|
Bytecode: []Bytecode{
|
||||||
|
InstructionDescend,
|
||||||
|
InstructionGetLocal, 0,
|
||||||
|
InstructionGetLocal, 1,
|
||||||
|
InstructionAdd,
|
||||||
|
InstructionReturn,
|
||||||
|
InstructionAscend,
|
||||||
|
},
|
||||||
|
Constants: []Value{&StringValue{"a"}, &StringValue{"b"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"list": {
|
"list": {
|
||||||
"a := [1.0, 2.0]\n{}",
|
"a := [1, 2]",
|
||||||
[]Value{&NilValue{}},
|
|
||||||
[]map[string]Value{
|
|
||||||
{"a": &ListValue{
|
|
||||||
[]Value{
|
[]Value{
|
||||||
&FloatValue{1},
|
&VariableValue{
|
||||||
&FloatValue{2},
|
"a",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"constant_list_concat": {
|
"constant_list_concat": {
|
||||||
"a := [1, 2] + [3]\n{}",
|
"a := [1, 2] + [3]",
|
||||||
[]Value{&NilValue{}},
|
|
||||||
[]map[string]Value{
|
|
||||||
{"a": &ListValue{
|
|
||||||
[]Value{
|
[]Value{
|
||||||
&IntegerValue{big.NewInt(1)},
|
&VariableValue{
|
||||||
&IntegerValue{big.NewInt(2)},
|
"a",
|
||||||
&IntegerValue{big.NewInt(3)},
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"list_concat": {
|
"list_concat": {
|
||||||
"a := [1.0, 2.0]\nb := a + [3.0]\nnil",
|
"a := [1, 2]\nb := a + [3]",
|
||||||
[]Value{&NilValue{}},
|
|
||||||
[]map[string]Value{
|
|
||||||
{
|
|
||||||
"a": &ListValue{
|
|
||||||
[]Value{
|
[]Value{
|
||||||
&FloatValue{1},
|
&VariableValue{
|
||||||
&FloatValue{2},
|
"a",
|
||||||
},
|
&ListValue{
|
||||||
},
|
|
||||||
"b": &ListValue{
|
|
||||||
[]Value{
|
[]Value{
|
||||||
&FloatValue{1},
|
&NumberValue{1},
|
||||||
&FloatValue{2},
|
&NumberValue{2},
|
||||||
&FloatValue{3},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
&VariableValue{
|
||||||
|
"b",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -119,13 +159,7 @@ func TestAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Comparing stacks")
|
t.Log("Comparing stacks")
|
||||||
|
CompareStacks(t, tc.expectedStack, vm.stack)
|
||||||
CompareStacks(t, tc.expectedStack, vm.Stack)
|
|
||||||
|
|
||||||
// expected scope == nil => we don't care
|
|
||||||
if tc.expectedScope != nil {
|
|
||||||
CompareScope(t, tc.expectedScope, vm.scope)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
308
core/compiler.go
308
core/compiler.go
|
|
@ -2,7 +2,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,9 +17,6 @@ type Compiler struct {
|
||||||
source []rune
|
source []rune
|
||||||
Warnings []CompilerError
|
Warnings []CompilerError
|
||||||
|
|
||||||
// optimize Whether to attempt some optimization of the emitted bytecode
|
|
||||||
optimize bool
|
|
||||||
|
|
||||||
stack *Stack[LocalVariable]
|
stack *Stack[LocalVariable]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +128,6 @@ func NewCompiler(source []rune) *Compiler {
|
||||||
nil,
|
nil,
|
||||||
source,
|
source,
|
||||||
[]CompilerError{},
|
[]CompilerError{},
|
||||||
false,
|
|
||||||
NewStack[LocalVariable](256),
|
NewStack[LocalVariable](256),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,14 +168,10 @@ func (c *Compiler) Compile(p *Program) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, s := range p.Block.statements {
|
for _, s := range p.Block.statements {
|
||||||
if err := c.compile(s); err != nil {
|
if err := c.compile(s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != len(p.Block.statements)-1 {
|
|
||||||
c.add(InstructionPop)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.fileStack.Pop()
|
c.fileStack.Pop()
|
||||||
|
|
@ -200,20 +191,16 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
tree.(*StringNode).value,
|
tree.(*StringNode).value,
|
||||||
})
|
})
|
||||||
|
|
||||||
case FloatNodeType:
|
case NumberNodeType:
|
||||||
c.add(InstructionConstant)
|
c.add(InstructionConstant)
|
||||||
c.addConstant(&FloatValue{tree.(*FloatNode).value})
|
c.addConstant(&NumberValue{tree.(*NumberNode).value})
|
||||||
|
|
||||||
case IntegerNodeType:
|
|
||||||
c.add(InstructionConstant)
|
|
||||||
c.addConstant(&IntegerValue{tree.(*IntegerNode).value})
|
|
||||||
|
|
||||||
case ListNodeType:
|
case ListNodeType:
|
||||||
l := tree.(*ListNode)
|
l := tree.(*ListNode)
|
||||||
|
|
||||||
if len(l.items) == 0 {
|
if len(l.items) == 0 {
|
||||||
c.add(InstructionNewList)
|
c.add(InstructionNewList)
|
||||||
} else if c.optimize && c.isTreeConstant(l) {
|
} else if c.isTreeConstant(l) {
|
||||||
v, err := c.compute(l)
|
v, err := c.compute(l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // this shouldn't happen
|
panic(err) // this shouldn't happen
|
||||||
|
|
@ -242,7 +229,7 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
case UnaryNodeType:
|
case UnaryNodeType:
|
||||||
if c.optimize && c.isTreeConstant(tree.(*UnaryNode).value) {
|
if c.isTreeConstant(tree.(*UnaryNode).value) {
|
||||||
v, err := c.compute(tree)
|
v, err := c.compute(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -256,19 +243,9 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vt, err := c.deduceSignature(tree.(*UnaryNode).value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tree.(*UnaryNode).UnaryOperation {
|
switch tree.(*UnaryNode).UnaryOperation {
|
||||||
case UnaryNegate:
|
case UnaryNegate:
|
||||||
if vt.Type() == TypeInteger {
|
c.add(InstructionNegate)
|
||||||
c.add(InstructionNegateInt)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionNegateFloat)
|
|
||||||
}
|
|
||||||
|
|
||||||
case UnaryNot:
|
case UnaryNot:
|
||||||
c.add(InstructionNot)
|
c.add(InstructionNot)
|
||||||
}
|
}
|
||||||
|
|
@ -285,21 +262,12 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
c.add(InstructionNil)
|
c.add(InstructionNil)
|
||||||
|
|
||||||
case BlockNodeType:
|
case BlockNodeType:
|
||||||
if len(tree.(*BlockNode).statements) == 0 {
|
|
||||||
c.add(InstructionNil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.addDescend()
|
c.addDescend()
|
||||||
for i, n := range tree.(*BlockNode).statements {
|
for _, n := range tree.(*BlockNode).statements {
|
||||||
err := c.compile(n)
|
err := c.compile(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if i != len(tree.(*BlockNode).statements)-1 {
|
|
||||||
c.add(InstructionPop)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.addAscend()
|
c.addAscend()
|
||||||
|
|
||||||
|
|
@ -315,7 +283,7 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
return c.error(fmt.Sprintf("conditional requires boolean; cannot use non-boolean type %s", sig), n.condition)
|
return c.error(fmt.Sprintf("conditional requires boolean; cannot use non-boolean type %s", sig), n.condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.optimize && c.isTreeConstant(n.condition) {
|
if c.isTreeConstant(n.condition) {
|
||||||
v, err := c.compute(n.condition)
|
v, err := c.compute(n.condition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -353,10 +321,13 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we store the position of the jump over the else code here
|
// we store the position of the jump over the else code here
|
||||||
|
var jumpOverElse Pos
|
||||||
|
if n.otherwise != nil {
|
||||||
// this would jump over the else/otherwise block in the code
|
// this would jump over the else/otherwise block in the code
|
||||||
c.add(InstructionJump)
|
c.add(InstructionJump)
|
||||||
jumpOverElse := c.ip
|
jumpOverElse = c.ip
|
||||||
c.advance(2)
|
c.advance(2)
|
||||||
|
}
|
||||||
|
|
||||||
// put the u16 of where to jump if the condition was false
|
// put the u16 of where to jump if the condition was false
|
||||||
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
|
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
|
||||||
|
|
@ -366,11 +337,8 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
c.add(InstructionNil)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
|
c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
|
||||||
|
}
|
||||||
|
|
||||||
case LoopNodeType:
|
case LoopNodeType:
|
||||||
n := tree.(*LoopNode)
|
n := tree.(*LoopNode)
|
||||||
|
|
@ -385,7 +353,7 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
alwaysLoop := false
|
alwaysLoop := false
|
||||||
if c.optimize && c.isTreeConstant(n.condition) {
|
if c.isTreeConstant(n.condition) {
|
||||||
v, err := c.compute(n.condition)
|
v, err := c.compute(n.condition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -400,8 +368,6 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.add(InstructionNil)
|
|
||||||
|
|
||||||
conditionPos := c.ip
|
conditionPos := c.ip
|
||||||
jumpValuePos := Pos(0)
|
jumpValuePos := Pos(0)
|
||||||
if !alwaysLoop {
|
if !alwaysLoop {
|
||||||
|
|
@ -415,8 +381,6 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
c.advance(2)
|
c.advance(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.add(InstructionPop)
|
|
||||||
|
|
||||||
err = c.compile(n.do)
|
err = c.compile(n.do)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -433,26 +397,6 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
case AssignNodeType:
|
case AssignNodeType:
|
||||||
n := tree.(*AssignNode)
|
n := tree.(*AssignNode)
|
||||||
|
|
||||||
switch n.dest.Type() {
|
|
||||||
case ReferenceNodeType:
|
|
||||||
d := n.dest.(*ReferenceNode)
|
|
||||||
|
|
||||||
if d.name == "_" {
|
|
||||||
return c.compile(n.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.declare && c.isVarDeclaredHere(d.name) {
|
|
||||||
return c.error(fmt.Sprintf("%s is already declared in this scope", d.name), n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.addSetVar(d.name, n.value, n.declare); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return c.error(fmt.Sprintf("cannot assign to %s", n.dest.Type()), n.dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if n.name == "_" {
|
if n.name == "_" {
|
||||||
// allow non-ish statements
|
// allow non-ish statements
|
||||||
err := c.compile(n.value)
|
err := c.compile(n.value)
|
||||||
|
|
@ -470,10 +414,9 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
case InvokeNodeType:
|
case CallNodeType:
|
||||||
n := tree.(*InvokeNode)
|
n := tree.(*CallNode)
|
||||||
|
|
||||||
s, err := c.deduceSignature(n.source)
|
s, err := c.deduceSignature(n.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -485,6 +428,10 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !n.keep && f.Out.Type() != TypeNil {
|
||||||
|
c.warn(fmt.Sprintf("shouldn't void result of function call (output is non-nil %s)", f.Out), n)
|
||||||
|
}
|
||||||
|
|
||||||
if len(n.args) != len(f.In) {
|
if len(n.args) != len(f.In) {
|
||||||
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
|
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
|
||||||
}
|
}
|
||||||
|
|
@ -516,7 +463,7 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.optimize && c.isTreeConstant(arg) {
|
if c.isTreeConstant(arg) {
|
||||||
v, err := c.compute(arg)
|
v, err := c.compute(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -539,6 +486,10 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
|
|
||||||
c.add(InstructionCall)
|
c.add(InstructionCall)
|
||||||
|
|
||||||
|
if !n.keep {
|
||||||
|
c.add(InstructionPop)
|
||||||
|
}
|
||||||
|
|
||||||
case FunctionNodeType:
|
case FunctionNodeType:
|
||||||
n := tree.(*FunctionNode)
|
n := tree.(*FunctionNode)
|
||||||
|
|
||||||
|
|
@ -587,7 +538,6 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
n.yield,
|
n.yield,
|
||||||
c.Chunk,
|
c.Chunk,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore old chunk and ip
|
// restore old chunk and ip
|
||||||
|
|
@ -623,7 +573,7 @@ func (c *Compiler) compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||||
if c.optimize && c.isTreeConstant(binary) {
|
if c.isTreeConstant(binary) {
|
||||||
v, err := c.compute(binary)
|
v, err := c.compute(binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -643,77 +593,38 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch binary.BinaryOperation {
|
||||||
|
case BinaryAddition:
|
||||||
res, err := c.deduceSignature(binary)
|
res, err := c.deduceSignature(binary)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
inType, err := c.deduceSignature(binary.Left) // type(left) == type(right) because res is valid
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch binary.BinaryOperation {
|
|
||||||
case BinaryAddition:
|
|
||||||
if res.Type() == TypeString {
|
if res.Type() == TypeString {
|
||||||
c.add(InstructionStringConcatenation)
|
c.add(InstructionStringConcatenation)
|
||||||
} else if res.Type() == TypeList {
|
} else if res.Type() == TypeList {
|
||||||
c.add(InstructionConcatLists)
|
c.add(InstructionConcatLists)
|
||||||
} else if res.Type() == TypeFloat {
|
|
||||||
c.add(InstructionAddFloat)
|
|
||||||
} else if res.Type() == TypeInteger {
|
|
||||||
c.add(InstructionAddInt)
|
|
||||||
} else {
|
} else {
|
||||||
return c.error("unimplemented binary compilation", binary)
|
c.add(InstructionAdd)
|
||||||
}
|
}
|
||||||
case BinarySubtraction:
|
case BinarySubtraction:
|
||||||
if res.Type() == TypeFloat {
|
c.add(InstructionSub)
|
||||||
c.add(InstructionSubFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionSubInt)
|
|
||||||
}
|
|
||||||
case BinaryMultiplication:
|
case BinaryMultiplication:
|
||||||
if res.Type() == TypeFloat {
|
c.add(InstructionMul)
|
||||||
c.add(InstructionMulFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionMulInt)
|
|
||||||
}
|
|
||||||
case BinaryDivision:
|
case BinaryDivision:
|
||||||
if res.Type() == TypeFloat {
|
c.add(InstructionDiv)
|
||||||
c.add(InstructionDivFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionDivInt)
|
|
||||||
}
|
|
||||||
case BinaryEquality:
|
case BinaryEquality:
|
||||||
c.add(InstructionEquals)
|
c.add(InstructionEquals)
|
||||||
case BinaryInequality:
|
case BinaryInequality:
|
||||||
c.add(InstructionNotEqual)
|
c.add(InstructionNotEqual)
|
||||||
|
|
||||||
case BinaryLess:
|
case BinaryLess:
|
||||||
if inType.Type() == TypeFloat {
|
c.add(InstructionLess)
|
||||||
c.add(InstructionLessFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionLessInt)
|
|
||||||
}
|
|
||||||
case BinaryGreater:
|
case BinaryGreater:
|
||||||
if inType.Type() == TypeFloat {
|
c.add(InstructionGreater)
|
||||||
c.add(InstructionGreaterFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionGreaterInt)
|
|
||||||
}
|
|
||||||
case BinaryLessEqual:
|
case BinaryLessEqual:
|
||||||
if inType.Type() == TypeFloat {
|
c.add(InstructionLessOrEqual)
|
||||||
c.add(InstructionLessOrEqualFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionLessOrEqualInt)
|
|
||||||
}
|
|
||||||
case BinaryGreaterEqual:
|
case BinaryGreaterEqual:
|
||||||
if inType.Type() == TypeFloat {
|
c.add(InstructionGreaterOrEqual)
|
||||||
c.add(InstructionGreaterOrEqualFloat)
|
|
||||||
} else {
|
|
||||||
c.add(InstructionGreaterOrEqualInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryAnd:
|
case BinaryAnd:
|
||||||
c.add(InstructionAnd)
|
c.add(InstructionAnd)
|
||||||
case BinaryOr:
|
case BinaryOr:
|
||||||
|
|
@ -727,10 +638,8 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
switch tree.Type() {
|
switch tree.Type() {
|
||||||
case StringNodeType:
|
case StringNodeType:
|
||||||
return &StringSignature{}, nil
|
return &StringSignature{}, nil
|
||||||
case FloatNodeType:
|
case NumberNodeType:
|
||||||
return &FloatSignature{}, nil
|
return &NumberSignature{}, nil
|
||||||
case IntegerNodeType:
|
|
||||||
return &IntegerSignature{}, nil
|
|
||||||
case ReferenceNodeType:
|
case ReferenceNodeType:
|
||||||
n := tree.(*ReferenceNode)
|
n := tree.(*ReferenceNode)
|
||||||
sig, err := c.getVarSignature(n.name, n)
|
sig, err := c.getVarSignature(n.name, n)
|
||||||
|
|
@ -786,23 +695,17 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
|
|
||||||
switch n.BinaryOperation {
|
switch n.BinaryOperation {
|
||||||
case BinarySubtraction, BinaryMultiplication, BinaryDivision:
|
case BinarySubtraction, BinaryMultiplication, BinaryDivision:
|
||||||
if l.Type() == TypeInteger {
|
if l.Type() != TypeNumber {
|
||||||
return &IntegerSignature{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Type() == TypeFloat {
|
|
||||||
return &FloatSignature{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
|
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NumberSignature{}, nil
|
||||||
case BinaryAddition:
|
case BinaryAddition:
|
||||||
switch l.Type() {
|
switch l.Type() {
|
||||||
case TypeString:
|
case TypeString:
|
||||||
return &StringSignature{}, nil
|
return &StringSignature{}, nil
|
||||||
case TypeInteger:
|
case TypeNumber:
|
||||||
return &IntegerSignature{}, nil
|
return &NumberSignature{}, nil
|
||||||
case TypeFloat:
|
|
||||||
return &FloatSignature{}, nil
|
|
||||||
case TypeList:
|
case TypeList:
|
||||||
return &ListSignature{
|
return &ListSignature{
|
||||||
l.(*ListSignature).Contents,
|
l.(*ListSignature).Contents,
|
||||||
|
|
@ -819,7 +722,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
case BinaryEquality, BinaryInequality:
|
case BinaryEquality, BinaryInequality:
|
||||||
return &BooleanSignature{}, nil
|
return &BooleanSignature{}, nil
|
||||||
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
if l.Type() != TypeInteger && l.Type() != TypeFloat {
|
if l.Type() != TypeNumber {
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on non-number type %s", n.BinaryOperation, l), n)
|
return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on non-number type %s", n.BinaryOperation, l), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -865,8 +768,8 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
case InvokeNodeType:
|
case CallNodeType:
|
||||||
n := tree.(*InvokeNode)
|
n := tree.(*CallNode)
|
||||||
sig, err := c.deduceSignature(n.source)
|
sig, err := c.deduceSignature(n.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -949,13 +852,10 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
|
|
||||||
switch n.UnaryOperation {
|
switch n.UnaryOperation {
|
||||||
case UnaryNegate:
|
case UnaryNegate:
|
||||||
if sig.Type() == TypeFloat {
|
if sig.Type() != TypeNumber {
|
||||||
return &FloatSignature{}, nil
|
|
||||||
} else if sig.Type() == TypeInteger {
|
|
||||||
return &IntegerSignature{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
|
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be number)", n.UnaryOperation), n)
|
||||||
|
}
|
||||||
|
return &NumberSignature{}, nil
|
||||||
case UnaryNot:
|
case UnaryNot:
|
||||||
if sig.Type() != TypeBoolean {
|
if sig.Type() != TypeBoolean {
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n)
|
return nil, c.error(fmt.Sprintf("cannot perform negation on type %s (must be boolean)", n.UnaryOperation), n)
|
||||||
|
|
@ -964,15 +864,6 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("unimplemented result type deduction for unary %s", n.UnaryOperation), n)
|
return nil, c.error(fmt.Sprintf("unimplemented result type deduction for unary %s", n.UnaryOperation), n)
|
||||||
case BlockNodeType:
|
|
||||||
n := tree.(*BlockNode)
|
|
||||||
if len(n.statements) == 0 {
|
|
||||||
return &NilSignature{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.deduceSignature(n.statements[len(n.statements)-1])
|
|
||||||
case AssignNodeType:
|
|
||||||
return c.deduceSignature(tree.(*AssignNode).value)
|
|
||||||
default:
|
default:
|
||||||
return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree)
|
return nil, c.error(fmt.Sprintf("impossible to deduce signature of %s", tree.Type()), tree)
|
||||||
}
|
}
|
||||||
|
|
@ -1038,12 +929,8 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
case AssignNodeType:
|
case AssignNodeType:
|
||||||
n := tree.(*AssignNode)
|
n := tree.(*AssignNode)
|
||||||
|
|
||||||
switch n.dest.Type() {
|
|
||||||
case ReferenceNodeType:
|
|
||||||
name := n.dest.(*ReferenceNode).name
|
|
||||||
|
|
||||||
if !n.declare {
|
if !n.declare {
|
||||||
prev, err := c.getVarSignature(name, n)
|
prev, err := c.getVarSignature(n.name, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1054,7 +941,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sig.Matches(prev) {
|
if !sig.Matches(prev) {
|
||||||
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, name, prev), n.value)
|
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, n.name, prev), n.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -1064,11 +951,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.registerVar(name, sig)
|
c.registerVar(n.name, sig)
|
||||||
default:
|
|
||||||
return c.error("can neither assign nor declare to", n.dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1157,7 +1040,7 @@ func (c *Compiler) isLocal(name string) bool {
|
||||||
// isTreeConstant check if a node tree is constant (predictable)
|
// isTreeConstant check if a node tree is constant (predictable)
|
||||||
func (c *Compiler) isTreeConstant(tree Node) bool {
|
func (c *Compiler) isTreeConstant(tree Node) bool {
|
||||||
switch tree.Type() {
|
switch tree.Type() {
|
||||||
case StringNodeType, FloatNodeType, IntegerNodeType, BooleanNodeType, NilNodeType:
|
case StringNodeType, NumberNodeType, BooleanNodeType, NilNodeType:
|
||||||
return true
|
return true
|
||||||
case ListNodeType:
|
case ListNodeType:
|
||||||
for _, item := range tree.(*ListNode).items {
|
for _, item := range tree.(*ListNode).items {
|
||||||
|
|
@ -1171,13 +1054,13 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
|
||||||
return c.isTreeConstant(tree.(*UnaryNode).value)
|
return c.isTreeConstant(tree.(*UnaryNode).value)
|
||||||
case BinaryNodeType:
|
case BinaryNodeType:
|
||||||
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
||||||
case InvokeNodeType:
|
case CallNodeType:
|
||||||
for _, arg := range tree.(*InvokeNode).args {
|
for _, arg := range tree.(*CallNode).args {
|
||||||
if !c.isTreeConstant(arg) {
|
if !c.isTreeConstant(arg) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.isTreeConstant(tree.(*InvokeNode).source)
|
return c.isTreeConstant(tree.(*CallNode).source)
|
||||||
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
|
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
|
||||||
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
||||||
return false
|
return false
|
||||||
|
|
@ -1193,13 +1076,8 @@ func (c *Compiler) compute(tree Node) (Value, error) {
|
||||||
n.value,
|
n.value,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case *FloatNode:
|
case *NumberNode:
|
||||||
return &FloatValue{
|
return &NumberValue{
|
||||||
n.value,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case *IntegerNode:
|
|
||||||
return &IntegerValue{
|
|
||||||
n.value,
|
n.value,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|
@ -1236,17 +1114,13 @@ func (c *Compiler) compute(tree Node) (Value, error) {
|
||||||
|
|
||||||
switch n.UnaryOperation {
|
switch n.UnaryOperation {
|
||||||
case UnaryNegate:
|
case UnaryNegate:
|
||||||
if v.Type() == FloatValueType {
|
if v.Type() != NumberValueType {
|
||||||
return &FloatValue{
|
return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
|
||||||
-v.(*FloatValue).Number,
|
|
||||||
}, nil
|
|
||||||
} else if v.Type() == IntegerValueType {
|
|
||||||
return &IntegerValue{
|
|
||||||
new(big.Int).Neg(v.(*IntegerValue).Number),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("cannot negate %s value (not a number)", v.Type()), n)
|
return &NumberValue{
|
||||||
|
-v.(*NumberValue).Number,
|
||||||
|
}, nil
|
||||||
case UnaryNot:
|
case UnaryNot:
|
||||||
if v.Type() != BoolValueType {
|
if v.Type() != BoolValueType {
|
||||||
return nil, c.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n)
|
return nil, c.error(fmt.Sprintf("cannot invert %s value (not a boolean)", v.Type()), n)
|
||||||
|
|
@ -1259,7 +1133,7 @@ func (c *Compiler) compute(tree Node) (Value, error) {
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
|
return nil, c.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
|
||||||
|
|
||||||
case *InvokeNode:
|
case *CallNode:
|
||||||
source, err := c.compute(n.source)
|
source, err := c.compute(n.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1306,7 +1180,7 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
// perform type check
|
// perform type check
|
||||||
switch n.BinaryOperation {
|
switch n.BinaryOperation {
|
||||||
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
if l.Type() != FloatValueType && l.Type() != IntegerValueType {
|
if l.Type() != NumberValueType {
|
||||||
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n)
|
return nil, c.error(fmt.Sprintf("cannot %s values of non-number type %s", n.BinaryOperation, l.Type()), n)
|
||||||
}
|
}
|
||||||
case BinaryAnd, BinaryOr:
|
case BinaryAnd, BinaryOr:
|
||||||
|
|
@ -1322,67 +1196,37 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
switch n.BinaryOperation {
|
switch n.BinaryOperation {
|
||||||
case BinaryAddition:
|
case BinaryAddition:
|
||||||
switch l.Type() {
|
switch l.Type() {
|
||||||
case FloatValueType:
|
case NumberValueType:
|
||||||
v = l.(*FloatValue).Number + r.(*FloatValue).Number
|
v = l.(*NumberValue).Number + r.(*NumberValue).Number
|
||||||
case StringValueType:
|
case StringValueType:
|
||||||
v = l.(*StringValue).Text + r.(*StringValue).Text
|
v = l.(*StringValue).Text + r.(*StringValue).Text
|
||||||
case ListValueType:
|
case ListValueType:
|
||||||
v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
|
v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
|
||||||
case IntegerValueType:
|
|
||||||
v = new(big.Int).Add(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
|
||||||
default:
|
default:
|
||||||
return nil, c.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
|
return nil, c.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
|
||||||
}
|
}
|
||||||
case BinarySubtraction:
|
case BinarySubtraction:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number - r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number - r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = new(big.Int).Sub(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
|
||||||
}
|
|
||||||
case BinaryMultiplication:
|
case BinaryMultiplication:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number * r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number * r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = new(big.Int).Mul(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
|
||||||
}
|
|
||||||
case BinaryDivision:
|
case BinaryDivision:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number / r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number / r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = new(big.Int).Div(l.(*IntegerValue).Number, r.(*IntegerValue).Number)
|
|
||||||
}
|
|
||||||
case BinaryAnd:
|
case BinaryAnd:
|
||||||
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||||
case BinaryOr:
|
case BinaryOr:
|
||||||
v = l.(*BoolValue).Boolean || r.(*BoolValue).Boolean
|
v = l.(*BoolValue).Boolean && r.(*BoolValue).Boolean
|
||||||
case BinaryEquality:
|
case BinaryEquality:
|
||||||
v = l.Equals(r)
|
v = l.Equals(r)
|
||||||
case BinaryInequality:
|
case BinaryInequality:
|
||||||
v = !l.Equals(r)
|
v = !l.Equals(r)
|
||||||
case BinaryLess:
|
case BinaryLess:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number < r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number < r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == -1
|
|
||||||
}
|
|
||||||
case BinaryGreater:
|
case BinaryGreater:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number > r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number > r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) == 1
|
|
||||||
}
|
|
||||||
case BinaryLessEqual:
|
case BinaryLessEqual:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number <= r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number <= r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
|
||||||
}
|
|
||||||
case BinaryGreaterEqual:
|
case BinaryGreaterEqual:
|
||||||
if l.Type() == FloatValueType {
|
v = l.(*NumberValue).Number >= r.(*NumberValue).Number
|
||||||
v = l.(*FloatValue).Number >= r.(*FloatValue).Number
|
|
||||||
} else {
|
|
||||||
v = l.(*IntegerValue).Number.Cmp(r.(*IntegerValue).Number) != 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GoToValue(v), nil
|
return GoToValue(v), nil
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
core/nodes.go
119
core/nodes.go
|
|
@ -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) {
|
||||||
|
|
|
||||||
596
core/parser.go
596
core/parser.go
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -62,7 +61,7 @@ func (p ParsingError) Format() string {
|
||||||
b.WriteRune(' ')
|
b.WriteRune(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(p.Causer.Lexeme); i++ {
|
for i := 0; i < int(p.Causer.Length); i++ {
|
||||||
b.WriteRune('^')
|
b.WriteRune('^')
|
||||||
}
|
}
|
||||||
b.WriteRune('\n')
|
b.WriteRune('\n')
|
||||||
|
|
@ -82,7 +81,6 @@ type Parser struct {
|
||||||
prev *Token
|
prev *Token
|
||||||
curr *Token
|
curr *Token
|
||||||
pos Pos
|
pos Pos
|
||||||
ignoreNewLine bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParser(source string, trace []string, tokens []Token) *Parser {
|
func NewParser(source string, trace []string, tokens []Token) *Parser {
|
||||||
|
|
@ -144,19 +142,12 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
||||||
imports = append(imports, Import{
|
imports = append(imports, Import{
|
||||||
p.prev.Lexeme[1 : len(p.prev.Lexeme)-1],
|
p.prev.Lexeme[1 : len(p.prev.Lexeme)-1],
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for p.accept(TokenNewLine) {
|
b, err := p.block(true)
|
||||||
}
|
|
||||||
|
|
||||||
if p.curr.Type == TokenEOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := p.expression(false)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -172,7 +163,7 @@ func (p *Parser) Parse(path string) (*Program, error) {
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
statements,
|
statements,
|
||||||
0,
|
0,
|
||||||
p.curr.End,
|
p.curr.Start + p.curr.Length,
|
||||||
},
|
},
|
||||||
path,
|
path,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -184,12 +175,6 @@ func (p *Parser) accept(tokenType TokenType) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.ignoreNewLine && tokenType != TokenNewLine {
|
|
||||||
for p.curr.Type == TokenNewLine {
|
|
||||||
p.advance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*p.curr).Type == tokenType {
|
if (*p.curr).Type == tokenType {
|
||||||
p.advance()
|
p.advance()
|
||||||
return true
|
return true
|
||||||
|
|
@ -218,11 +203,10 @@ func (p *Parser) advance() {
|
||||||
|
|
||||||
if p.pos < Pos(len(p.tokens)) {
|
if p.pos < Pos(len(p.tokens)) {
|
||||||
p.curr = &p.tokens[p.pos]
|
p.curr = &p.tokens[p.pos]
|
||||||
} else {
|
|
||||||
p.curr = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pos++
|
p.pos++
|
||||||
|
} else {
|
||||||
|
panic("no more tokens")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) error(error string, causer *Token) error {
|
func (p *Parser) error(error string, causer *Token) error {
|
||||||
|
|
@ -234,370 +218,6 @@ func (p *Parser) error(error string, causer *Token) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expression(mustBeBlock bool) (Node, error) {
|
|
||||||
if mustBeBlock || p.accept(TokenOpenBrace) {
|
|
||||||
if mustBeBlock {
|
|
||||||
if err := p.expect(TokenOpenBrace, "expected block"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oldIgnoreNewline := p.ignoreNewLine
|
|
||||||
p.ignoreNewLine = false
|
|
||||||
|
|
||||||
start := p.prev.Start
|
|
||||||
|
|
||||||
var statements []Node
|
|
||||||
for !p.accept(TokenCloseBrace) {
|
|
||||||
if p.accept(TokenNewLine) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
statements = append(statements, s)
|
|
||||||
|
|
||||||
if !p.accept(TokenNewLine) {
|
|
||||||
if err := p.expect(TokenCloseBrace, "blocks must be closed"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.ignoreNewLine = oldIgnoreNewline
|
|
||||||
|
|
||||||
return &BlockNode{statements, start, p.prev.End}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
t := p.curr
|
|
||||||
switch t.Type {
|
|
||||||
case TokenIf:
|
|
||||||
p.advance()
|
|
||||||
|
|
||||||
cond, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
do, err := p.expression(true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var otherwise Node
|
|
||||||
if p.accept(TokenElse) {
|
|
||||||
otherwise, err = p.expression(p.curr.Type != TokenIf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ConditionalNode{
|
|
||||||
cond,
|
|
||||||
do,
|
|
||||||
otherwise,
|
|
||||||
t.Start,
|
|
||||||
t.End,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TokenFunc:
|
|
||||||
p.advance()
|
|
||||||
start := p.prev.Start
|
|
||||||
|
|
||||||
var name *Token
|
|
||||||
if p.accept(TokenName) { // can be unnamed, but accept name if it is named
|
|
||||||
name = p.prev
|
|
||||||
}
|
|
||||||
|
|
||||||
params, err := p.parseParams()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var yield TypeSignature
|
|
||||||
if p.accept(TokenArrow) {
|
|
||||||
yield, err = p.parseSignature()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logic, err := p.expression(true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
names := "*"
|
|
||||||
if name != nil {
|
|
||||||
names = name.Lexeme
|
|
||||||
}
|
|
||||||
|
|
||||||
fn := &FunctionNode{
|
|
||||||
names,
|
|
||||||
params,
|
|
||||||
yield,
|
|
||||||
logic,
|
|
||||||
start,
|
|
||||||
p.prev.End,
|
|
||||||
}
|
|
||||||
|
|
||||||
if name != nil {
|
|
||||||
return &AssignNode{
|
|
||||||
&ReferenceNode{name.Lexeme, name.Start, name.End},
|
|
||||||
fn,
|
|
||||||
true,
|
|
||||||
start,
|
|
||||||
p.prev.End,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn, nil
|
|
||||||
|
|
||||||
case TokenReturn:
|
|
||||||
p.advance()
|
|
||||||
start := p.prev.Start
|
|
||||||
|
|
||||||
v, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ReturnNode{
|
|
||||||
v,
|
|
||||||
start,
|
|
||||||
p.prev.End,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TokenWhile:
|
|
||||||
p.advance()
|
|
||||||
start := p.prev.Start
|
|
||||||
|
|
||||||
cond, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
logic, err := p.expression(true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &LoopNode{
|
|
||||||
cond,
|
|
||||||
logic,
|
|
||||||
start,
|
|
||||||
p.prev.End,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
s, err := p.binary()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.accept(TokenDeclare) || p.accept(TokenAssign) {
|
|
||||||
isDeclaration := p.prev.Type == TokenDeclare
|
|
||||||
|
|
||||||
// possibly assign tuples; not implemented yet
|
|
||||||
v, err := p.expression(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
start, _ := s.Bounds()
|
|
||||||
_, end := v.Bounds()
|
|
||||||
|
|
||||||
return &AssignNode{
|
|
||||||
s,
|
|
||||||
v,
|
|
||||||
isDeclaration,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOperator(tokenType TokenType) bool {
|
|
||||||
switch tokenType {
|
|
||||||
case TokenPlus, TokenMinus, TokenStar, TokenSlash, TokenPipe, TokenDoubleAmpersand, TokenDoublePipe, TokenEquals, TokenBangEquals, TokenLessThan, TokenLessThanOrEqual, TokenGreaterThan, TokenGreaterThanOrEqual:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryPrecedence(op TokenType) int {
|
|
||||||
switch op {
|
|
||||||
case TokenDoubleAmpersand, TokenDoublePipe:
|
|
||||||
return 1
|
|
||||||
case TokenEquals, TokenBangEquals, TokenLessThan, TokenGreaterThan, TokenLessThanOrEqual, TokenGreaterThanOrEqual:
|
|
||||||
return 2
|
|
||||||
case TokenPlus, TokenMinus, TokenPipe:
|
|
||||||
return 3
|
|
||||||
case TokenStar, TokenSlash:
|
|
||||||
return 5
|
|
||||||
default:
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenToBinaryOperation(tokenType TokenType) BinaryOperation {
|
|
||||||
switch tokenType {
|
|
||||||
case TokenPlus:
|
|
||||||
return BinaryAddition
|
|
||||||
case TokenMinus:
|
|
||||||
return BinarySubtraction
|
|
||||||
case TokenStar:
|
|
||||||
return BinaryMultiplication
|
|
||||||
case TokenSlash:
|
|
||||||
return BinaryDivision
|
|
||||||
case TokenPipe:
|
|
||||||
panic("unimplemented bitwise ops")
|
|
||||||
case TokenDoubleAmpersand:
|
|
||||||
return BinaryAnd
|
|
||||||
case TokenDoublePipe:
|
|
||||||
return BinaryOr
|
|
||||||
|
|
||||||
case TokenEquals:
|
|
||||||
return BinaryEquality
|
|
||||||
case TokenBangEquals:
|
|
||||||
return BinaryInequality
|
|
||||||
case TokenLessThan:
|
|
||||||
return BinaryLess
|
|
||||||
case TokenLessThanOrEqual:
|
|
||||||
return BinaryLessEqual
|
|
||||||
case TokenGreaterThan:
|
|
||||||
return BinaryGreater
|
|
||||||
case TokenGreaterThanOrEqual:
|
|
||||||
return BinaryGreaterEqual
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) binary() (Node, error) {
|
|
||||||
t, err := p.chain()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ops := NewStack[*Token](128)
|
|
||||||
values := NewStack[Node](256)
|
|
||||||
values.pushItem(t)
|
|
||||||
|
|
||||||
for isBinaryOperator(p.curr.Type) {
|
|
||||||
for ops.Current > 0 && binaryPrecedence(p.curr.Type) <= binaryPrecedence(ops.Peek().Type) {
|
|
||||||
r := values.Pop()
|
|
||||||
l := values.Pop()
|
|
||||||
op := tokenToBinaryOperation(ops.Pop().Type)
|
|
||||||
|
|
||||||
start, _ := l.Bounds()
|
|
||||||
_, end := r.Bounds()
|
|
||||||
|
|
||||||
values.Push(&BinaryNode{
|
|
||||||
op,
|
|
||||||
l,
|
|
||||||
r,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ops.Push(p.curr)
|
|
||||||
p.advance()
|
|
||||||
|
|
||||||
v, err := p.chain()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
values.Push(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for ops.Current > 0 {
|
|
||||||
r := values.Pop()
|
|
||||||
l := values.Pop()
|
|
||||||
op := tokenToBinaryOperation(ops.Pop().Type)
|
|
||||||
|
|
||||||
start, _ := l.Bounds()
|
|
||||||
_, end := l.Bounds()
|
|
||||||
|
|
||||||
values.Push(&BinaryNode{
|
|
||||||
op,
|
|
||||||
l,
|
|
||||||
r,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.Pop(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) chain() (Node, error) {
|
|
||||||
f, err := p.factor()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if p.accept(TokenDot) {
|
|
||||||
if err = p.expect(TokenName, "can only access properties by name"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
name := p.prev
|
|
||||||
|
|
||||||
f = &AccessNode{
|
|
||||||
f,
|
|
||||||
p.prev.Lexeme,
|
|
||||||
name.Start,
|
|
||||||
name.End,
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.curr.Type == TokenOpenParenthesis {
|
|
||||||
args, err := p.parseArgs()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f = &InvokeNode{
|
|
||||||
f,
|
|
||||||
args,
|
|
||||||
name.Start,
|
|
||||||
p.prev.End,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if p.curr.Type == TokenOpenParenthesis {
|
|
||||||
start := p.curr.Start
|
|
||||||
args, err := p.parseArgs()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f = &InvokeNode{
|
|
||||||
f,
|
|
||||||
args,
|
|
||||||
|
|
||||||
start,
|
|
||||||
p.prev.End,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) factor() (Node, error) {
|
func (p *Parser) factor() (Node, error) {
|
||||||
switch (*p.curr).Type {
|
switch (*p.curr).Type {
|
||||||
case TokenString:
|
case TokenString:
|
||||||
|
|
@ -606,49 +226,35 @@ func (p *Parser) factor() (Node, error) {
|
||||||
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
||||||
(*p.prev).Lexeme,
|
(*p.prev).Lexeme,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenInteger:
|
case TokenNumber:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
num, err := strconv.ParseFloat((*p.prev).Lexeme, NumberSize)
|
||||||
num, success := new(big.Int).SetString(p.prev.Lexeme, 10)
|
|
||||||
if !success {
|
|
||||||
return nil, p.error(fmt.Sprintf("cannot parse integer base 10: %s", p.prev.Lexeme), p.prev)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &IntegerNode{
|
|
||||||
num,
|
|
||||||
p.prev.Start,
|
|
||||||
p.prev.End,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TokenFloat:
|
|
||||||
p.advance()
|
|
||||||
num, err := strconv.ParseFloat((*p.prev).Lexeme, FloatSize)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev)
|
return nil, p.error(fmt.Sprintf("Error parsing number: %v", err), p.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FloatNode{
|
return &NumberNode{
|
||||||
num,
|
num,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenHexadecimal:
|
case TokenHexadecimal:
|
||||||
p.advance()
|
p.advance()
|
||||||
start := (*p.prev).Start
|
start := (*p.prev).Start
|
||||||
num, ok := new(big.Int).SetString(p.prev.Lexeme[2:], 16)
|
num, err := strconv.ParseUint((*p.prev).Lexeme[2:], 16, NumberSize)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, p.error(fmt.Sprintf("cannot parse hexadecimal: %v", p.prev.Lexeme), p.prev)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IntegerNode{
|
return &NumberNode{
|
||||||
num,
|
float64(num),
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenTrue:
|
case TokenTrue:
|
||||||
|
|
@ -656,14 +262,14 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
true,
|
true,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
case TokenFalse:
|
case TokenFalse:
|
||||||
p.advance()
|
p.advance()
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
false,
|
false,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNil:
|
case TokenNil:
|
||||||
|
|
@ -674,8 +280,6 @@ func (p *Parser) factor() (Node, error) {
|
||||||
p.advance()
|
p.advance()
|
||||||
start := p.prev.Start
|
start := p.prev.Start
|
||||||
|
|
||||||
// TODO: find better solution; current one is messy
|
|
||||||
// Maybe perform better analysis to determine the kind of the list...
|
|
||||||
if p.accept(TokenCloseBracket) {
|
if p.accept(TokenCloseBracket) {
|
||||||
s, err := p.parseSignature()
|
s, err := p.parseSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -686,13 +290,10 @@ func (p *Parser) factor() (Node, error) {
|
||||||
[]Node{},
|
[]Node{},
|
||||||
s,
|
s,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
oldIgnoreNewline := p.ignoreNewLine
|
|
||||||
p.ignoreNewLine = true
|
|
||||||
|
|
||||||
var values []Node
|
var values []Node
|
||||||
for !p.accept(TokenCloseBracket) {
|
for !p.accept(TokenCloseBracket) {
|
||||||
if len(values) > 0 {
|
if len(values) > 0 {
|
||||||
|
|
@ -709,13 +310,11 @@ func (p *Parser) factor() (Node, error) {
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.ignoreNewLine = oldIgnoreNewline
|
|
||||||
|
|
||||||
return &ListNode{
|
return &ListNode{
|
||||||
values,
|
values,
|
||||||
nil,
|
nil,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
// unary minus
|
// unary minus
|
||||||
|
|
@ -731,7 +330,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
UnaryNegate,
|
UnaryNegate,
|
||||||
f,
|
f,
|
||||||
first.Start,
|
first.Start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenBang:
|
case TokenBang:
|
||||||
|
|
@ -747,14 +346,14 @@ func (p *Parser) factor() (Node, error) {
|
||||||
UnaryNot,
|
UnaryNot,
|
||||||
v,
|
v,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenName:
|
case TokenName:
|
||||||
p.advance()
|
p.advance()
|
||||||
name := (*p.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
start := p.prev.Start
|
start := p.prev.Start
|
||||||
nameEnd := p.prev.End
|
nameEnd := start + p.prev.Length
|
||||||
|
|
||||||
if p.curr.Type == TokenOpenParenthesis {
|
if p.curr.Type == TokenOpenParenthesis {
|
||||||
args, err := p.parseArgs()
|
args, err := p.parseArgs()
|
||||||
|
|
@ -762,15 +361,16 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InvokeNode{
|
return &CallNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
name,
|
name,
|
||||||
start,
|
start,
|
||||||
nameEnd,
|
nameEnd,
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
|
true,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -790,7 +390,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sig TypeSignature = &NilSignature{}
|
var sig TypeSignature = &NilSignature{}
|
||||||
if p.accept(TokenArrow) {
|
if p.curr.Type != TokenOpenBrace {
|
||||||
sig, err = p.parseSignature()
|
sig, err = p.parseSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -808,7 +408,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
sig,
|
sig,
|
||||||
b,
|
b,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenOpenParenthesis:
|
case TokenOpenParenthesis:
|
||||||
|
|
@ -823,16 +423,8 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
||||||
case TokenBreakpoint:
|
|
||||||
p.advance()
|
|
||||||
|
|
||||||
return &BreakpointNode{
|
|
||||||
p.prev.Start,
|
|
||||||
p.prev.End,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, p.error(fmt.Sprintf("invalid factor %s", p.curr), p.curr)
|
return nil, p.error("invalid factor", p.curr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -855,7 +447,7 @@ func (p *Parser) prop() (Node, error) {
|
||||||
v,
|
v,
|
||||||
property,
|
property,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if called, also add
|
// if called, also add
|
||||||
|
|
@ -865,11 +457,12 @@ func (p *Parser) prop() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
v = &InvokeNode{
|
v = &CallNode{
|
||||||
v,
|
v,
|
||||||
args,
|
args,
|
||||||
|
true,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -901,7 +494,7 @@ func (p *Parser) product() (Node, error) {
|
||||||
left,
|
left,
|
||||||
f,
|
f,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -933,7 +526,7 @@ func (p *Parser) term() (Node, error) {
|
||||||
left,
|
left,
|
||||||
pr,
|
pr,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -980,7 +573,7 @@ func (p *Parser) comparison() (Node, error) {
|
||||||
left,
|
left,
|
||||||
t,
|
t,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1014,7 +607,7 @@ func (p *Parser) condition() (Node, error) {
|
||||||
left,
|
left,
|
||||||
c,
|
c,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1053,18 +646,19 @@ func (p *Parser) statement() (Node, error) {
|
||||||
then,
|
then,
|
||||||
otherwise,
|
otherwise,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenName:
|
case TokenName:
|
||||||
p.advance()
|
p.advance()
|
||||||
name := p.prev
|
start := p.prev.Start
|
||||||
|
name := (*p.prev).Lexeme
|
||||||
|
|
||||||
if (*p.curr).Type == TokenDot {
|
if (*p.curr).Type == TokenDot {
|
||||||
var v Node = &ReferenceNode{
|
var v Node = &ReferenceNode{
|
||||||
name.Lexeme,
|
name,
|
||||||
name.Start,
|
start,
|
||||||
name.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse chains of prop-getting ( "".split().join().length.round() )
|
// parse chains of prop-getting ( "".split().join().length.round() )
|
||||||
|
|
@ -1077,8 +671,8 @@ func (p *Parser) statement() (Node, error) {
|
||||||
v = &AccessNode{
|
v = &AccessNode{
|
||||||
v,
|
v,
|
||||||
property,
|
property,
|
||||||
name.Start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if called, also add
|
// if called, also add
|
||||||
|
|
@ -1088,11 +682,12 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
v = &InvokeNode{
|
v = &CallNode{
|
||||||
v,
|
v,
|
||||||
args,
|
args,
|
||||||
name.Start,
|
(*p.curr).Type == TokenDot, // if the chain is continued, keep the value.
|
||||||
p.prev.End,
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1104,15 +699,16 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InvokeNode{
|
return &CallNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
name.Lexeme,
|
name,
|
||||||
name.Start,
|
start,
|
||||||
name.End,
|
start + Pos(len(name)),
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
name.Start,
|
false,
|
||||||
p.prev.End,
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
} else if p.accept(TokenAssign) || p.accept(TokenDeclare) {
|
} else if p.accept(TokenAssign) || p.accept(TokenDeclare) {
|
||||||
isDeclaration := p.prev.Type == TokenDeclare
|
isDeclaration := p.prev.Type == TokenDeclare
|
||||||
|
|
@ -1121,16 +717,12 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AssignNode{ // THIS COULD BE MORE PERMISSIVE; its a new system
|
return &AssignNode{
|
||||||
&ReferenceNode{
|
name,
|
||||||
name.Lexeme,
|
|
||||||
name.Start,
|
|
||||||
name.End,
|
|
||||||
},
|
|
||||||
c,
|
c,
|
||||||
isDeclaration,
|
isDeclaration,
|
||||||
name.Start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1144,7 +736,7 @@ func (p *Parser) statement() (Node, error) {
|
||||||
if err := p.expect(TokenName, "function must have a name"); err != nil {
|
if err := p.expect(TokenName, "function must have a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := p.prev
|
name := p.prev.Lexeme
|
||||||
|
|
||||||
params, err := p.parseParams()
|
params, err := p.parseParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1152,7 +744,7 @@ func (p *Parser) statement() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var yield TypeSignature = &NilSignature{}
|
var yield TypeSignature = &NilSignature{}
|
||||||
if p.accept(TokenArrow) {
|
if p.curr.Type != TokenOpenBrace {
|
||||||
yield, err = p.parseSignature()
|
yield, err = p.parseSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1165,22 +757,18 @@ func (p *Parser) statement() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AssignNode{
|
return &AssignNode{
|
||||||
&ReferenceNode{
|
name,
|
||||||
name.Lexeme,
|
|
||||||
name.Start,
|
|
||||||
name.End,
|
|
||||||
},
|
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
name.Lexeme,
|
name,
|
||||||
params,
|
params,
|
||||||
yield,
|
yield,
|
||||||
b,
|
b,
|
||||||
funcStart,
|
funcStart,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
funcStart,
|
funcStart,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenWhile:
|
case TokenWhile:
|
||||||
|
|
@ -1201,7 +789,7 @@ func (p *Parser) statement() (Node, error) {
|
||||||
c,
|
c,
|
||||||
b,
|
b,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenReturn:
|
case TokenReturn:
|
||||||
|
|
@ -1216,7 +804,7 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return &ReturnNode{
|
return &ReturnNode{
|
||||||
c,
|
c,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenBreakpoint:
|
case TokenBreakpoint:
|
||||||
|
|
@ -1266,7 +854,7 @@ func (p *Parser) block(canBeStatement bool) (Node, error) {
|
||||||
return &BlockNode{
|
return &BlockNode{
|
||||||
statements,
|
statements,
|
||||||
start,
|
start,
|
||||||
p.prev.End,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1278,7 +866,7 @@ func (p *Parser) parseArgs() ([]Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.accept(TokenCloseParenthesis) {
|
if !p.accept(TokenCloseParenthesis) {
|
||||||
c, err := p.expression(false)
|
c, err := p.condition()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1287,7 +875,7 @@ func (p *Parser) parseArgs() ([]Node, error) {
|
||||||
if err := p.expect(TokenComma, "arguments must be separated by comma"); err != nil {
|
if err := p.expect(TokenComma, "arguments must be separated by comma"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c, err = p.expression(false)
|
c, err = p.condition()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1354,17 +942,7 @@ func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
||||||
func (p *Parser) parseSignature() (TypeSignature, error) {
|
func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
var s TypeSignature
|
var s TypeSignature
|
||||||
|
|
||||||
if p.accept(TokenOpenParenthesis) {
|
if p.accept(TokenFunc) {
|
||||||
is, err := p.parseSignature()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := p.expect(TokenCloseParenthesis, "expected closing parenthesis"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s = is
|
|
||||||
} else if p.accept(TokenFunc) {
|
|
||||||
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1387,31 +965,15 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
in = append(in, sig)
|
in = append(in, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
var out TypeSignature
|
out, err := p.parseSignature()
|
||||||
var err error
|
|
||||||
if p.accept(TokenArrow) {
|
|
||||||
out, err = p.parseSignature()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
out = &NilSignature{}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = &FunctionSignature{
|
s = &FunctionSignature{
|
||||||
in,
|
in,
|
||||||
out,
|
out,
|
||||||
}
|
}
|
||||||
} else if p.accept(TokenOpenBracket) {
|
|
||||||
inner, err := p.parseSignature()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := p.expect(TokenCloseBracket, "list type must be enclosed in brackets"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ListSignature{inner}, nil
|
|
||||||
} else {
|
} else {
|
||||||
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1421,10 +983,8 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
switch name {
|
switch name {
|
||||||
case "string":
|
case "string":
|
||||||
s = &StringSignature{}
|
s = &StringSignature{}
|
||||||
case "int":
|
case "number":
|
||||||
s = &IntegerSignature{}
|
s = &NumberSignature{}
|
||||||
case "float":
|
|
||||||
s = &FloatSignature{}
|
|
||||||
case "boolean":
|
case "boolean":
|
||||||
s = &BooleanSignature{}
|
s = &BooleanSignature{}
|
||||||
case "list":
|
case "list":
|
||||||
|
|
@ -1437,7 +997,7 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.expect(TokenCloseBracket, "square brackets enclose list content signature"); err != nil {
|
if err := p.expect(TokenCloseBracket, "list must close parameter list"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
||||||
|
|
|
||||||
139
core/values.go
139
core/values.go
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
623
core/vm.go
623
core/vm.go
|
|
@ -7,9 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -22,52 +20,30 @@ const (
|
||||||
// InstructionPop pop and delete the first item on the stack
|
// InstructionPop pop and delete the first item on the stack
|
||||||
InstructionPop
|
InstructionPop
|
||||||
|
|
||||||
// InstructionAddFloat pop two floats and add them
|
// InstructionAdd pop two and add them
|
||||||
InstructionAddFloat
|
InstructionAdd
|
||||||
// InstructionSubFloat pop two floats and subtract the second from the first
|
// InstructionSub pop two and subtract the second from the first
|
||||||
InstructionSubFloat
|
InstructionSub
|
||||||
// InstructionMulFloat pop two floats and multiply them
|
// InstructionMul pop two and multiply them
|
||||||
InstructionMulFloat
|
InstructionMul
|
||||||
// InstructionDivFloat pop two floats and divide the second by the first
|
// InstructionDiv pop two and divide the second by the first
|
||||||
InstructionDivFloat
|
InstructionDiv
|
||||||
// InstructionNegateFloat negate the float; if it was positive, make it negative, and vice versa.
|
// InstructionNegate negate the value; if it was positive, make it negative, and vice versa.
|
||||||
InstructionNegateFloat
|
InstructionNegate
|
||||||
|
|
||||||
// InstructionAddInt pop two ints and add them
|
|
||||||
InstructionAddInt
|
|
||||||
// InstructionSubInt pop two ints and subtract the second from the first
|
|
||||||
InstructionSubInt
|
|
||||||
// InstructionMulInt pop two ints and multiply them
|
|
||||||
InstructionMulInt
|
|
||||||
// InstructionDivInt pop two ints and divide the second by the first
|
|
||||||
InstructionDivInt
|
|
||||||
// InstructionNegateInt negate the int; if it was positive, make it negative, and vice versa.
|
|
||||||
InstructionNegateInt
|
|
||||||
|
|
||||||
// InstructionEquals whether the two top values on the stack are equal
|
// InstructionEquals whether the two top values on the stack are equal
|
||||||
InstructionEquals
|
InstructionEquals
|
||||||
// InstructionNotEqual whether the two top values on the stack are not equal
|
// InstructionNotEqual whether the two top values on the stack are not equal
|
||||||
InstructionNotEqual
|
InstructionNotEqual
|
||||||
// InstructionNot inverts boolean (true => false, false => true)
|
// InstructionNot inverts boolean (true => false, false => true)
|
||||||
InstructionNot
|
InstructionNot
|
||||||
|
// InstructionLess pops two from stack, pushes whether the lowest is less than the highest
|
||||||
// InstructionLessFloat pops two from stack, pushes whether the lowest is less than the highest
|
InstructionLess
|
||||||
InstructionLessFloat
|
// InstructionLessOrEqual pops two from stack, pushes whether the lowest is less or equal than the highest
|
||||||
// InstructionLessOrEqualFloat pops two from stack, pushes whether the lowest is less or equal than the highest
|
InstructionLessOrEqual
|
||||||
InstructionLessOrEqualFloat
|
// InstructionGreater pops two from stack, pushes whether the lowest is greater than the highest
|
||||||
// InstructionGreaterFloat pops two from stack, pushes whether the lowest is greater than the highest
|
InstructionGreater
|
||||||
InstructionGreaterFloat
|
// InstructionGreaterOrEqual pops two from stack, pushes whether the lowest is greater or equal than the highest
|
||||||
// InstructionGreaterOrEqualFloat pops two from stack, pushes whether the lowest is greater or equal than the highest
|
InstructionGreaterOrEqual
|
||||||
InstructionGreaterOrEqualFloat
|
|
||||||
|
|
||||||
// InstructionLessInt pops two from stack, pushes whether the lowest is less than the highest
|
|
||||||
InstructionLessInt
|
|
||||||
// InstructionLessOrEqualInt pops two from stack, pushes whether the lowest is less or equal than the highest
|
|
||||||
InstructionLessOrEqualInt
|
|
||||||
// InstructionGreaterInt pops two from stack, pushes whether the lowest is greater than the highest
|
|
||||||
InstructionGreaterInt
|
|
||||||
// InstructionGreaterOrEqualInt pops two from stack, pushes whether the lowest is greater or equal than the highest
|
|
||||||
InstructionGreaterOrEqualInt
|
|
||||||
|
|
||||||
// InstructionAccessProperty gets a property from a value, and pops it onto the stack
|
// InstructionAccessProperty gets a property from a value, and pops it onto the stack
|
||||||
InstructionAccessProperty
|
InstructionAccessProperty
|
||||||
|
|
@ -104,8 +80,6 @@ const (
|
||||||
|
|
||||||
// InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1)
|
// InstructionSwap swap the two top items on the stack (1, 2 -> 2, 1)
|
||||||
InstructionSwap
|
InstructionSwap
|
||||||
// InstructionDuplicate push a copy of the item on top of the stack (1 -> 1, 1)
|
|
||||||
InstructionDuplicate
|
|
||||||
|
|
||||||
// InstructionAnd pop two booleans and push true if both are true
|
// InstructionAnd pop two booleans and push true if both are true
|
||||||
InstructionAnd
|
InstructionAnd
|
||||||
|
|
@ -142,48 +116,30 @@ func (b Bytecode) String() string {
|
||||||
return "RETURN"
|
return "RETURN"
|
||||||
case InstructionPop:
|
case InstructionPop:
|
||||||
return "POP"
|
return "POP"
|
||||||
case InstructionAddFloat:
|
case InstructionAdd:
|
||||||
return "ADD_FLOAT"
|
return "ADD"
|
||||||
case InstructionSubFloat:
|
case InstructionSub:
|
||||||
return "SUB_FLOAT"
|
return "SUB"
|
||||||
case InstructionMulFloat:
|
case InstructionMul:
|
||||||
return "MUL_FLOAT"
|
return "MUL"
|
||||||
case InstructionDivFloat:
|
case InstructionDiv:
|
||||||
return "DIV_FLOAT"
|
return "DIV"
|
||||||
case InstructionNegateFloat:
|
case InstructionNegate:
|
||||||
return "NEGATE_FLOAT"
|
return "NEGATE"
|
||||||
case InstructionAddInt:
|
|
||||||
return "ADD_INT"
|
|
||||||
case InstructionSubInt:
|
|
||||||
return "SUB_INT"
|
|
||||||
case InstructionMulInt:
|
|
||||||
return "MUL_INT"
|
|
||||||
case InstructionDivInt:
|
|
||||||
return "DIV_INT"
|
|
||||||
case InstructionNegateInt:
|
|
||||||
return "NEGATE_INT"
|
|
||||||
case InstructionEquals:
|
case InstructionEquals:
|
||||||
return "EQUALS"
|
return "EQUALS"
|
||||||
case InstructionNotEqual:
|
case InstructionNotEqual:
|
||||||
return "NOT_EQUALS"
|
return "NOT_EQUALS"
|
||||||
case InstructionNot:
|
case InstructionNot:
|
||||||
return "NOT"
|
return "NOT"
|
||||||
case InstructionLessFloat:
|
case InstructionLess:
|
||||||
return "LESS_FLOAT"
|
return "LESS"
|
||||||
case InstructionLessOrEqualFloat:
|
case InstructionLessOrEqual:
|
||||||
return "LESS_OR_EQUAL_FLOAT"
|
return "LESS_OR_EQUAL"
|
||||||
case InstructionGreaterFloat:
|
case InstructionGreater:
|
||||||
return "GREATER_FLOAT"
|
return "GREATER_OR_EQUAL"
|
||||||
case InstructionGreaterOrEqualFloat:
|
case InstructionGreaterOrEqual:
|
||||||
return "GREATER_OR_EQUAL_FLOAT"
|
return "GREATER_OR_EQUAL"
|
||||||
case InstructionLessInt:
|
|
||||||
return "LESS_INT"
|
|
||||||
case InstructionLessOrEqualInt:
|
|
||||||
return "LESS_OR_EQUAL_INT"
|
|
||||||
case InstructionGreaterInt:
|
|
||||||
return "GREATER_INT"
|
|
||||||
case InstructionGreaterOrEqualInt:
|
|
||||||
return "GREATER_OR_EQUAL_INT"
|
|
||||||
case InstructionJump:
|
case InstructionJump:
|
||||||
return "JUMP"
|
return "JUMP"
|
||||||
case InstructionJumpFalse:
|
case InstructionJumpFalse:
|
||||||
|
|
@ -236,8 +192,6 @@ func (b Bytecode) String() string {
|
||||||
return "ACCESS_PROPERTY"
|
return "ACCESS_PROPERTY"
|
||||||
case InstructionConcatLists:
|
case InstructionConcatLists:
|
||||||
return "CONCAT_LISTS"
|
return "CONCAT_LISTS"
|
||||||
case InstructionDuplicate:
|
|
||||||
return "DUPLICATE"
|
|
||||||
}
|
}
|
||||||
return "UNDEFINED"
|
return "UNDEFINED"
|
||||||
}
|
}
|
||||||
|
|
@ -271,30 +225,6 @@ func (c Chunk) String() string {
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Chunk) Equals(other *Chunk) bool {
|
|
||||||
if len(c.Bytecode) != len(other.Bytecode) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, bc := range c.Bytecode {
|
|
||||||
if other.Bytecode[i] != bc {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Constants) != len(other.Constants) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(c.Constants); i++ {
|
|
||||||
if other.Constants[i] != c.Constants[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
|
func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
|
||||||
return &Chunk{bytecode, constants}
|
return &Chunk{bytecode, constants}
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +232,7 @@ func NewChunk(bytecode []Bytecode, constants []Value) *Chunk {
|
||||||
func RegisterGOBTypes() {
|
func RegisterGOBTypes() {
|
||||||
gob.Register(&StringValue{""})
|
gob.Register(&StringValue{""})
|
||||||
gob.Register(&BoolValue{false})
|
gob.Register(&BoolValue{false})
|
||||||
gob.Register(&FloatValue{0})
|
gob.Register(&NumberValue{0})
|
||||||
gob.Register(&FunctionValue{
|
gob.Register(&FunctionValue{
|
||||||
Name: "",
|
Name: "",
|
||||||
Params: nil,
|
Params: nil,
|
||||||
|
|
@ -311,7 +241,7 @@ func RegisterGOBTypes() {
|
||||||
|
|
||||||
// Signatures
|
// Signatures
|
||||||
gob.Register(&NilSignature{})
|
gob.Register(&NilSignature{})
|
||||||
gob.Register(&FloatSignature{})
|
gob.Register(&NumberSignature{})
|
||||||
gob.Register(&StringSignature{})
|
gob.Register(&StringSignature{})
|
||||||
gob.Register(&FunctionSignature{})
|
gob.Register(&FunctionSignature{})
|
||||||
gob.Register(&ListSignature{})
|
gob.Register(&ListSignature{})
|
||||||
|
|
@ -356,29 +286,26 @@ type VM struct {
|
||||||
|
|
||||||
// instruction pointer
|
// instruction pointer
|
||||||
ip Pos
|
ip Pos
|
||||||
|
scope Pos
|
||||||
|
|
||||||
// global variable storage
|
// global variable storage
|
||||||
globals map[string]Value
|
globals map[string]Value
|
||||||
// local variable storage
|
variableEnd Pos
|
||||||
scope *Scope
|
|
||||||
|
|
||||||
Stack *Stack[Value]
|
stack *Stack[Value]
|
||||||
call *Stack[Call]
|
call *Stack[Call]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scope struct {
|
|
||||||
current map[string]Value
|
|
||||||
parent *Scope
|
|
||||||
}
|
|
||||||
|
|
||||||
type Call struct {
|
type Call struct {
|
||||||
chunk *Chunk
|
chunk *Chunk
|
||||||
ip Pos
|
ip Pos
|
||||||
scope *Scope
|
stackEnd Pos
|
||||||
|
variableEnd Pos
|
||||||
|
scope Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultGlobals = map[string]Value{
|
var DefaultGlobals = map[string]Value{
|
||||||
"println": &BuiltinFunctionValue{
|
"write": &BuiltinFunctionValue{
|
||||||
"write", // always remember where you come from...
|
"write", // always remember where you come from...
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&AnySignature{}},
|
[]TypeSignature{&AnySignature{}},
|
||||||
|
|
@ -386,7 +313,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
},
|
},
|
||||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||||
println(v[0].String())
|
println(v[0].String())
|
||||||
return &NilValue{}, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -399,7 +326,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
},
|
},
|
||||||
func(_ *VM, this Value, v []Value) (Value, error) {
|
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||||
print(v[0].String())
|
print(v[0].String())
|
||||||
return &NilValue{}, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
|
@ -441,12 +368,12 @@ var DefaultGlobals = map[string]Value{
|
||||||
"char": &BuiltinFunctionValue{
|
"char": &BuiltinFunctionValue{
|
||||||
"char",
|
"char",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&IntegerSignature{}},
|
[]TypeSignature{&NumberSignature{}},
|
||||||
&StringSignature{},
|
&StringSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
n := args[0].(*IntegerValue).Number
|
n := args[0].(*NumberValue).Number
|
||||||
b := n.Bytes()[0]
|
b := byte(n)
|
||||||
|
|
||||||
return &StringValue{
|
return &StringValue{
|
||||||
string([]byte{b}),
|
string([]byte{b}),
|
||||||
|
|
@ -456,16 +383,16 @@ var DefaultGlobals = map[string]Value{
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
"byte": &BuiltinFunctionValue{
|
"byte": &BuiltinFunctionValue{
|
||||||
"byte",
|
"char",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&StringSignature{}},
|
[]TypeSignature{&StringSignature{}},
|
||||||
&IntegerSignature{},
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
s := args[0].(*StringValue).Text
|
s := args[0].(*StringValue).Text
|
||||||
n := new(big.Int).SetBytes([]byte(s))
|
b := []byte(s)[0]
|
||||||
|
|
||||||
return &IntegerValue{n}, nil
|
return &NumberValue{float64(b)}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
|
@ -526,66 +453,6 @@ var DefaultGlobals = map[string]Value{
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
"int": &BuiltinFunctionValue{
|
|
||||||
"int",
|
|
||||||
&FunctionSignature{
|
|
||||||
[]TypeSignature{&AnySignature{}},
|
|
||||||
&CompositeSignature{
|
|
||||||
&IntegerSignature{},
|
|
||||||
&NilSignature{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
func(vm *VM, _ Value, args []Value) (Value, error) {
|
|
||||||
switch v := args[0].(type) {
|
|
||||||
case *IntegerValue:
|
|
||||||
return &IntegerValue{v.Number}, nil // this might need to clone the value instead
|
|
||||||
case *FloatValue:
|
|
||||||
n := new(big.Int).SetInt64(int64(v.Number))
|
|
||||||
return &IntegerValue{n}, nil
|
|
||||||
case *StringValue:
|
|
||||||
n, success := new(big.Int).SetString(v.Text, 0) // determine base
|
|
||||||
if !success {
|
|
||||||
return &NilValue{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &IntegerValue{n}, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
"float": &BuiltinFunctionValue{
|
|
||||||
"float",
|
|
||||||
&FunctionSignature{
|
|
||||||
[]TypeSignature{&AnySignature{}},
|
|
||||||
&CompositeSignature{
|
|
||||||
&FloatSignature{},
|
|
||||||
&NilSignature{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
func(vm *VM, _ Value, args []Value) (Value, error) {
|
|
||||||
switch v := args[0].(type) {
|
|
||||||
case *IntegerValue:
|
|
||||||
n, _ := v.Number.Float64()
|
|
||||||
return &FloatValue{n}, nil // this might need to clone the value instead
|
|
||||||
case *FloatValue:
|
|
||||||
return &FloatValue{v.Number}, nil
|
|
||||||
case *StringValue:
|
|
||||||
num, err := strconv.ParseFloat(v.Text, FloatSize)
|
|
||||||
if err != nil {
|
|
||||||
return &NilValue{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FloatValue{num}, nil
|
|
||||||
default:
|
|
||||||
return nil, errors.New(fmt.Sprintf("%s cannot become an integer", v))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
"type": &BuiltinFunctionValue{
|
"type": &BuiltinFunctionValue{
|
||||||
Name: "type",
|
Name: "type",
|
||||||
Signature: &FunctionSignature{
|
Signature: &FunctionSignature{
|
||||||
|
|
@ -603,11 +470,11 @@ var DefaultGlobals = map[string]Value{
|
||||||
"exit": &BuiltinFunctionValue{
|
"exit": &BuiltinFunctionValue{
|
||||||
"exit",
|
"exit",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&FloatSignature{}},
|
[]TypeSignature{&NumberSignature{}},
|
||||||
&NilSignature{},
|
&NilSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
os.Exit(int(args[0].(*FloatValue).Number))
|
os.Exit(int(args[0].(*NumberValue).Number))
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
|
@ -616,11 +483,11 @@ var DefaultGlobals = map[string]Value{
|
||||||
"floor": &BuiltinFunctionValue{
|
"floor": &BuiltinFunctionValue{
|
||||||
"floor",
|
"floor",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&FloatSignature{}},
|
[]TypeSignature{&NumberSignature{}},
|
||||||
&FloatSignature{},
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
return &FloatValue{math.Floor(args[0].(*FloatValue).Number)}, nil
|
return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
|
@ -628,11 +495,11 @@ var DefaultGlobals = map[string]Value{
|
||||||
"ceil": &BuiltinFunctionValue{
|
"ceil": &BuiltinFunctionValue{
|
||||||
"ceil",
|
"ceil",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&FloatSignature{}},
|
[]TypeSignature{&NumberSignature{}},
|
||||||
&FloatSignature{},
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
return &FloatValue{math.Ceil(args[0].(*FloatValue).Number)}, nil
|
return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
|
@ -640,14 +507,14 @@ var DefaultGlobals = map[string]Value{
|
||||||
"roundd": &BuiltinFunctionValue{
|
"roundd": &BuiltinFunctionValue{
|
||||||
"roundd",
|
"roundd",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&FloatSignature{}, &FloatSignature{}},
|
[]TypeSignature{&NumberSignature{}, &NumberSignature{}},
|
||||||
&FloatSignature{},
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, args []Value) (Value, error) {
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
x := args[0].(*FloatValue).Number
|
x := args[0].(*NumberValue).Number
|
||||||
decimals := args[1].(*FloatValue).Number
|
decimals := args[1].(*NumberValue).Number
|
||||||
multiplier := math.Pow(10, decimals)
|
multiplier := math.Pow(10, decimals)
|
||||||
return &FloatValue{math.Round(x*multiplier) / multiplier}, nil
|
return &NumberValue{math.Round(x*multiplier) / multiplier}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
|
@ -657,13 +524,10 @@ var DefaultGlobals = map[string]Value{
|
||||||
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
|
func NewVM(chunk *Chunk, stackSize Pos, callstackSize Pos) *VM {
|
||||||
vm := &VM{
|
vm := &VM{
|
||||||
chunk: chunk,
|
chunk: chunk,
|
||||||
Stack: NewStack[Value](stackSize),
|
stack: NewStack[Value](stackSize),
|
||||||
call: NewStack[Call](callstackSize),
|
call: NewStack[Call](callstackSize),
|
||||||
|
|
||||||
globals: DefaultGlobals,
|
globals: DefaultGlobals,
|
||||||
scope: &Scope{
|
|
||||||
current: map[string]Value{},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm
|
return vm
|
||||||
|
|
@ -680,190 +544,141 @@ func (vm *VM) Next() bool {
|
||||||
case InstructionReturn:
|
case InstructionReturn:
|
||||||
if vm.call.Current == 0 {
|
if vm.call.Current == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
} else {
|
||||||
|
v := vm.stack.Pop()
|
||||||
v := vm.Stack.Pop()
|
|
||||||
c := vm.call.Pop()
|
c := vm.call.Pop()
|
||||||
|
|
||||||
// reset stack current and variable end and scope
|
// reset stack current and variable end and scope
|
||||||
|
vm.variableEnd = c.variableEnd
|
||||||
|
vm.stack.Current = c.stackEnd
|
||||||
vm.scope = c.scope
|
vm.scope = c.scope
|
||||||
|
|
||||||
// reset to calling position
|
// reset to calling position
|
||||||
vm.ip = c.ip
|
vm.ip = c.ip
|
||||||
vm.chunk = c.chunk
|
vm.chunk = c.chunk
|
||||||
|
|
||||||
vm.Stack.Push(v)
|
vm.purgeVars()
|
||||||
|
|
||||||
case InstructionPop:
|
vm.stack.Push(v)
|
||||||
vm.Stack.Pop()
|
|
||||||
|
|
||||||
case InstructionConstant:
|
|
||||||
c := vm.ReadConstant()
|
|
||||||
|
|
||||||
if c, ok := c.(*FunctionValue); ok {
|
|
||||||
c.Scope = vm.scope
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(c)
|
case InstructionPop:
|
||||||
|
vm.stack.Pop()
|
||||||
|
|
||||||
case InstructionAddFloat:
|
case InstructionConstant:
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
vm.stack.Push(vm.ReadConstant())
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&FloatValue{l + r})
|
case InstructionAdd:
|
||||||
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
case InstructionSubFloat:
|
vm.stack.Push(&NumberValue{l + r})
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&FloatValue{l - r})
|
case InstructionSub:
|
||||||
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
case InstructionMulFloat:
|
vm.stack.Push(&NumberValue{l - r})
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&FloatValue{l * r})
|
case InstructionMul:
|
||||||
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
case InstructionDivFloat:
|
vm.stack.Push(&NumberValue{l * r})
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&FloatValue{l / r})
|
case InstructionDiv:
|
||||||
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
case InstructionNegateFloat:
|
vm.stack.Push(&NumberValue{l / r})
|
||||||
v := vm.Stack.Pop().(*FloatValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&FloatValue{-v})
|
case InstructionNegate:
|
||||||
|
v := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
case InstructionAddInt:
|
vm.stack.Push(&NumberValue{-v})
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&IntegerValue{new(big.Int).Add(l, r)})
|
|
||||||
|
|
||||||
case InstructionSubInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&IntegerValue{new(big.Int).Sub(l, r)})
|
|
||||||
|
|
||||||
case InstructionMulInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&IntegerValue{new(big.Int).Mul(l, r)})
|
|
||||||
|
|
||||||
case InstructionDivInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&IntegerValue{new(big.Int).Div(l, r)})
|
|
||||||
|
|
||||||
case InstructionNegateInt:
|
|
||||||
v := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&IntegerValue{new(big.Int).Neg(v)})
|
|
||||||
|
|
||||||
case InstructionEquals:
|
case InstructionEquals:
|
||||||
vm.Stack.Push(
|
vm.stack.Push(
|
||||||
&BoolValue{vm.Stack.Pop().Equals(vm.Stack.Pop())},
|
&BoolValue{vm.stack.Pop().Equals(vm.stack.Pop())},
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionNotEqual:
|
case InstructionNotEqual:
|
||||||
vm.Stack.Push(
|
vm.stack.Push(
|
||||||
&BoolValue{!vm.Stack.Pop().Equals(vm.Stack.Pop())},
|
&BoolValue{!vm.stack.Pop().Equals(vm.stack.Pop())},
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionNot:
|
case InstructionNot:
|
||||||
b := vm.Stack.Pop().(*BoolValue).Boolean
|
b := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
vm.Stack.Push(&BoolValue{!b})
|
vm.stack.Push(&BoolValue{!b})
|
||||||
|
|
||||||
case InstructionAnd:
|
case InstructionAnd:
|
||||||
r := vm.Stack.Pop().(*BoolValue).Boolean
|
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
l := vm.Stack.Pop().(*BoolValue).Boolean
|
l := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
vm.Stack.Push(&BoolValue{l && r})
|
vm.stack.Push(&BoolValue{l && r})
|
||||||
|
|
||||||
case InstructionOr:
|
case InstructionOr:
|
||||||
r := vm.Stack.Pop().(*BoolValue).Boolean
|
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
l := vm.Stack.Pop().(*BoolValue).Boolean
|
l := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
vm.Stack.Push(&BoolValue{l || r})
|
vm.stack.Push(&BoolValue{l || r})
|
||||||
|
|
||||||
case InstructionLessFloat:
|
case InstructionLess:
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l < r})
|
vm.stack.Push(&BoolValue{l < r})
|
||||||
|
|
||||||
case InstructionLessOrEqualFloat:
|
case InstructionLessOrEqual:
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l <= r})
|
vm.stack.Push(&BoolValue{l <= r})
|
||||||
|
|
||||||
case InstructionGreaterFloat:
|
case InstructionGreater:
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l > r})
|
vm.stack.Push(&BoolValue{l > r})
|
||||||
|
|
||||||
case InstructionGreaterOrEqualFloat:
|
case InstructionGreaterOrEqual:
|
||||||
r := vm.Stack.Pop().(*FloatValue).Number
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.Stack.Pop().(*FloatValue).Number
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l >= r})
|
vm.stack.Push(&BoolValue{l >= r})
|
||||||
|
|
||||||
case InstructionLessInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l.Cmp(r) == -1})
|
|
||||||
|
|
||||||
case InstructionLessOrEqualInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l.Cmp(r) != 1})
|
|
||||||
|
|
||||||
case InstructionGreaterInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l.Cmp(r) == 1})
|
|
||||||
|
|
||||||
case InstructionGreaterOrEqualInt:
|
|
||||||
r := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
l := vm.Stack.Pop().(*IntegerValue).Number
|
|
||||||
|
|
||||||
vm.Stack.Push(&BoolValue{l.Cmp(r) != -1})
|
|
||||||
|
|
||||||
case InstructionCall:
|
case InstructionCall:
|
||||||
v := vm.Stack.Pop()
|
v := vm.stack.Pop()
|
||||||
switch f := v.(type) {
|
switch f := v.(type) {
|
||||||
case *FunctionValue:
|
case *FunctionValue:
|
||||||
vm.call.Push(Call{
|
vm.call.Push(Call{
|
||||||
chunk: vm.chunk,
|
chunk: vm.chunk,
|
||||||
ip: vm.ip,
|
ip: vm.ip,
|
||||||
|
stackEnd: vm.stack.Current - Pos(len(f.Params)),
|
||||||
|
variableEnd: vm.variableEnd,
|
||||||
scope: vm.scope,
|
scope: vm.scope,
|
||||||
})
|
})
|
||||||
|
|
||||||
vm.scope = f.Scope
|
|
||||||
vm.descend()
|
|
||||||
|
|
||||||
for i := len(f.Params) - 1; i >= 0; i-- {
|
for i := len(f.Params) - 1; i >= 0; i-- {
|
||||||
vm.addVar(f.Params[i].Name, vm.Stack.Pop())
|
p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
|
||||||
|
vm.stack.items[p] = &VariableValue{
|
||||||
|
f.Params[i].Name,
|
||||||
|
vm.stack.items[p],
|
||||||
|
vm.scope,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Parent != nil {
|
if f.Parent != nil {
|
||||||
vm.addVar("this", f.Parent)
|
vm.addVar("this", f.Parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vm.variableEnd = vm.stack.Current
|
||||||
|
|
||||||
vm.chunk = f.Chunk
|
vm.chunk = f.Chunk
|
||||||
vm.ip = 0
|
vm.ip = 0
|
||||||
case *BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
args := make([]Value, len(f.Signature.In))
|
args := make([]Value, len(f.Signature.In))
|
||||||
|
|
||||||
for i := len(f.Signature.In) - 1; i >= 0; i-- {
|
for i := len(f.Signature.In) - 1; i >= 0; i-- {
|
||||||
args[i] = vm.Stack.Pop()
|
args[i] = vm.stack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := f.F(vm, f.Parent, args)
|
v, err := f.F(vm, f.Parent, args)
|
||||||
|
|
@ -871,13 +686,9 @@ func (vm *VM) Next() bool {
|
||||||
vm.error(err.Error())
|
vm.error(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if v == nil {
|
vm.stack.Push(v)
|
||||||
v = &NilValue{}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.Stack.Push(v)
|
|
||||||
default:
|
default:
|
||||||
vm.error(fmt.Sprintf("%s (%s) is not callable ", v.DebugString(), v.Type()))
|
vm.error(fmt.Sprintf("value called is not a function (%s, type %T)", v.DebugString(), v))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -889,7 +700,7 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionJumpFalse:
|
case InstructionJumpFalse:
|
||||||
n := vm.NextU16()
|
n := vm.NextU16()
|
||||||
if !vm.Stack.Pop().(*BoolValue).Boolean {
|
if !vm.stack.Pop().(*BoolValue).Boolean {
|
||||||
vm.ip += Pos(n)
|
vm.ip += Pos(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -902,61 +713,67 @@ func (vm *VM) Next() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(v)
|
vm.stack.Push(v.value)
|
||||||
|
|
||||||
case InstructionSetLocal:
|
case InstructionSetLocal:
|
||||||
value := vm.Stack.Peek().Clone()
|
value := vm.stack.Pop().(Value)
|
||||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||||
|
|
||||||
vm.setVar(name, value)
|
v := vm.getVar(name)
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
v.value = value.Clone()
|
||||||
|
|
||||||
case InstructionDeclareLocal:
|
case InstructionDeclareLocal:
|
||||||
vm.addVar(
|
vm.addVar(
|
||||||
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
||||||
vm.Stack.Peek().Clone(),
|
vm.stack.Pop().Clone(),
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionGetGlobal:
|
case InstructionGetGlobal:
|
||||||
vm.Stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
|
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
|
||||||
|
|
||||||
case InstructionSetGlobal:
|
case InstructionSetGlobal:
|
||||||
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.Stack.Pop()
|
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text] = vm.stack.Pop()
|
||||||
|
|
||||||
case InstructionTrue:
|
case InstructionTrue:
|
||||||
vm.Stack.Push(&BoolValue{true})
|
vm.stack.Push(&BoolValue{true})
|
||||||
|
|
||||||
case InstructionFalse:
|
case InstructionFalse:
|
||||||
vm.Stack.Push(&BoolValue{false})
|
vm.stack.Push(&BoolValue{false})
|
||||||
|
|
||||||
case InstructionNil:
|
case InstructionNil:
|
||||||
vm.Stack.Push(&NilValue{})
|
vm.stack.Push(&NilValue{})
|
||||||
|
|
||||||
case InstructionFormList:
|
case InstructionFormList:
|
||||||
n := int(vm.NextU16())
|
n := int(vm.NextU16())
|
||||||
|
|
||||||
items := make([]Value, n)
|
items := make([]Value, n)
|
||||||
for i := n - 1; i >= 0; i-- {
|
for i := n - 1; i >= 0; i-- {
|
||||||
items[i] = vm.Stack.Pop()
|
items[i] = vm.stack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(&ListValue{
|
vm.stack.Push(&ListValue{
|
||||||
items,
|
items,
|
||||||
})
|
})
|
||||||
|
|
||||||
case InstructionNewList:
|
case InstructionNewList:
|
||||||
vm.Stack.Push(&ListValue{[]Value{}})
|
vm.stack.Push(&ListValue{[]Value{}})
|
||||||
|
|
||||||
case InstructionAppend:
|
case InstructionAppend:
|
||||||
value := vm.Stack.Pop()
|
value := vm.stack.Pop()
|
||||||
list := vm.Stack.Pop().(*ListValue)
|
list := vm.stack.Pop().(*ListValue)
|
||||||
list.Items = append(list.Items, value)
|
list.Items = append(list.Items, value)
|
||||||
vm.Stack.Push(list)
|
vm.stack.Push(list)
|
||||||
|
|
||||||
case InstructionConcatLists:
|
case InstructionConcatLists:
|
||||||
r := vm.Stack.Pop().(*ListValue)
|
r := vm.stack.Pop().(*ListValue)
|
||||||
l := vm.Stack.Pop().(*ListValue)
|
l := vm.stack.Pop().(*ListValue)
|
||||||
|
|
||||||
vm.Stack.Push(&ListValue{
|
vm.stack.Push(&ListValue{
|
||||||
append(l.Items, r.Items...),
|
append(l.Items, r.Items...),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -967,26 +784,23 @@ func (vm *VM) Next() bool {
|
||||||
vm.ascend()
|
vm.ascend()
|
||||||
|
|
||||||
case InstructionStringConversion:
|
case InstructionStringConversion:
|
||||||
v := vm.Stack.Pop()
|
v := vm.stack.Pop()
|
||||||
vm.Stack.Push(&StringValue{v.String()})
|
vm.stack.Push(&StringValue{v.String()})
|
||||||
|
|
||||||
case InstructionStringConcatenation:
|
case InstructionStringConcatenation:
|
||||||
r := vm.Stack.Pop().(*StringValue).Text
|
r := vm.stack.Pop().(*StringValue).Text
|
||||||
l := vm.Stack.Pop().(*StringValue).Text
|
l := vm.stack.Pop().(*StringValue).Text
|
||||||
|
|
||||||
vm.Stack.Push(&StringValue{l + r})
|
vm.stack.Push(&StringValue{l + r})
|
||||||
|
|
||||||
case InstructionSwap:
|
case InstructionSwap:
|
||||||
r := vm.Stack.Pop()
|
r := vm.stack.Pop()
|
||||||
l := vm.Stack.Pop()
|
l := vm.stack.Pop()
|
||||||
|
|
||||||
vm.Stack.Push(r, l)
|
vm.stack.Push(r, l)
|
||||||
|
|
||||||
case InstructionDuplicate:
|
|
||||||
vm.Stack.Push(vm.Stack.Peek().Clone())
|
|
||||||
|
|
||||||
case InstructionAccessProperty:
|
case InstructionAccessProperty:
|
||||||
source := vm.Stack.Pop()
|
source := vm.stack.Pop()
|
||||||
property := vm.ReadConstant()
|
property := vm.ReadConstant()
|
||||||
|
|
||||||
member, err := source.Get(property.(*StringValue).String())
|
member, err := source.Get(property.(*StringValue).String())
|
||||||
|
|
@ -1001,19 +815,9 @@ func (vm *VM) Next() bool {
|
||||||
member.(*BuiltinFunctionValue).Parent = source
|
member.(*BuiltinFunctionValue).Parent = source
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.Stack.Push(member)
|
vm.stack.Push(member)
|
||||||
|
|
||||||
case InstructionBreakpoint:
|
case InstructionBreakpoint:
|
||||||
/*
|
|
||||||
// I'm keeping this
|
|
||||||
s := vm.scope
|
|
||||||
log.Printf("breakpoint %d", vm.ip)
|
|
||||||
for s != nil {
|
|
||||||
log.Printf("%s", s.current)
|
|
||||||
s = s.parent
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
vm.Stack.Push(&NilValue{})
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("invalid byte code")
|
panic("invalid byte code")
|
||||||
|
|
@ -1028,12 +832,11 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
vm.call.Push(Call{
|
vm.call.Push(Call{
|
||||||
chunk: vm.chunk,
|
chunk: vm.chunk,
|
||||||
ip: vm.ip,
|
ip: vm.ip,
|
||||||
|
stackEnd: vm.stack.Current,
|
||||||
|
variableEnd: vm.variableEnd,
|
||||||
scope: vm.scope,
|
scope: vm.scope,
|
||||||
})
|
})
|
||||||
|
|
||||||
vm.scope = f.Scope
|
|
||||||
vm.descend()
|
|
||||||
|
|
||||||
for i := 0; i < len(f.Params); i++ {
|
for i := 0; i < len(f.Params); i++ {
|
||||||
vm.addVar(f.Params[i].Name, args[i])
|
vm.addVar(f.Params[i].Name, args[i])
|
||||||
}
|
}
|
||||||
|
|
@ -1042,6 +845,8 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
vm.addVar("this", f.Parent)
|
vm.addVar("this", f.Parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vm.variableEnd = vm.stack.Current
|
||||||
|
|
||||||
vm.chunk = f.Chunk
|
vm.chunk = f.Chunk
|
||||||
vm.ip = 0
|
vm.ip = 0
|
||||||
|
|
||||||
|
|
@ -1050,7 +855,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
|
|
||||||
vm.Next()
|
vm.Next()
|
||||||
|
|
||||||
return vm.Stack.Pop(), nil
|
return vm.stack.Pop(), nil
|
||||||
|
|
||||||
case *BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
return f.F(vm, f.Parent, args)
|
return f.F(vm, f.Parent, args)
|
||||||
|
|
@ -1068,18 +873,6 @@ func (vm *VM) TryNextByte() (Bytecode, error) {
|
||||||
return 0, errors.New("there are no more instructions")
|
return 0, errors.New("there are no more instructions")
|
||||||
}
|
}
|
||||||
|
|
||||||
for int(vm.ip) >= len(vm.chunk.Bytecode) && vm.call.Current > 0 {
|
|
||||||
c := vm.call.Pop()
|
|
||||||
|
|
||||||
vm.ip = c.ip
|
|
||||||
vm.chunk = c.chunk
|
|
||||||
vm.scope = c.scope
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(vm.ip) >= len(vm.chunk.Bytecode) {
|
|
||||||
return 0, errors.New("there are no more instructions")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := vm.chunk.Bytecode[vm.ip]
|
v := vm.chunk.Bytecode[vm.ip]
|
||||||
vm.ip++
|
vm.ip++
|
||||||
|
|
||||||
|
|
@ -1097,59 +890,57 @@ func (vm *VM) NextByte() Bytecode {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) ascend() {
|
func (vm *VM) ascend() {
|
||||||
if vm.scope.parent == nil {
|
vm.scope--
|
||||||
|
|
||||||
|
if vm.scope < 0 {
|
||||||
panic("invalid scope")
|
panic("invalid scope")
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.scope = vm.scope.parent
|
vm.purgeVars()
|
||||||
|
}
|
||||||
|
|
||||||
|
// purgeVars remove all variables not within scope
|
||||||
|
func (vm *VM) purgeVars() {
|
||||||
|
for ; vm.variableEnd > 0 && vm.stack.items[vm.variableEnd-1].(*VariableValue).scope > vm.scope; vm.variableEnd-- {
|
||||||
|
vm.stack.Pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) descend() {
|
func (vm *VM) descend() {
|
||||||
old := vm.scope
|
vm.scope++
|
||||||
|
|
||||||
vm.scope = &Scope{
|
|
||||||
map[string]Value{},
|
|
||||||
old,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) addVar(name string, value Value) {
|
func (vm *VM) addVar(name string, value Value) {
|
||||||
vm.scope.current[name] = value
|
vm.variableEnd++
|
||||||
|
vm.stack.Push(&VariableValue{
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
vm.scope,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) getVar(name string) Value {
|
func (vm *VM) getVar(name string) *VariableValue {
|
||||||
s := vm.scope
|
for i := vm.variableEnd - 1; i >= 0; i-- {
|
||||||
|
v, ok := vm.stack.items[i].(*VariableValue)
|
||||||
|
|
||||||
for s != nil {
|
if !ok {
|
||||||
if v, ok := s.current[name]; ok {
|
continue
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s.parent
|
if v.name == name {
|
||||||
|
return v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) setVar(name string, v Value) {
|
|
||||||
s := vm.scope
|
|
||||||
|
|
||||||
for s != nil {
|
|
||||||
if _, ok := s.current[name]; ok {
|
|
||||||
s.current[name] = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s.parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *VM) HasNext() bool {
|
func (vm *VM) HasNext() bool {
|
||||||
return vm.ip < Pos(len(vm.chunk.Bytecode)) || vm.call.Current > 0
|
return vm.ip < Pos(len(vm.chunk.Bytecode))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) GetConstant(id Bytecode) Value {
|
func (vm *VM) GetConstant(id Bytecode) Value {
|
||||||
return vm.chunk.Constants[id].Clone()
|
return vm.chunk.Constants[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) ReadConstant() Value {
|
func (vm *VM) ReadConstant() Value {
|
||||||
|
|
|
||||||
202
core/vm_test.go
202
core/vm_test.go
|
|
@ -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,
|
||||||
|
|
|
||||||
11
era3.ang
11
era3.ang
|
|
@ -1,11 +0,0 @@
|
||||||
fn counter() -> (fn() -> int) {
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
fn() -> int { i = i + 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
next := counter()
|
|
||||||
|
|
||||||
println(next())
|
|
||||||
println(next())
|
|
||||||
println(next())
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
81
lib/math.ang
81
lib/math.ang
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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("")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
list := []int
|
list := []number
|
||||||
|
|
||||||
list.append(1)
|
list.append(1)
|
||||||
list.append(2)
|
list.append(2)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue