This commit is contained in:
commit
f53ab30dbe
42 changed files with 3635 additions and 904 deletions
40
bad.ang
Normal file
40
bad.ang
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import "lib/math.ang"
|
||||||
|
|
||||||
|
primes := [2]
|
||||||
|
|
||||||
|
func is_prime(x: number) boolean {
|
||||||
|
i := 0
|
||||||
|
while i < primes.length() && primes.at(i)*primes.at(i) < x {
|
||||||
|
if mod(x, primes.at(i)) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
n := 1
|
||||||
|
max := 100000
|
||||||
|
|
||||||
|
while n < max {
|
||||||
|
n = n + 2
|
||||||
|
|
||||||
|
if is_prime(n) {
|
||||||
|
primes.append(n)
|
||||||
|
|
||||||
|
# Update counter
|
||||||
|
print(char(0x0D))
|
||||||
|
print(str(n))
|
||||||
|
print("/")
|
||||||
|
print(str(max))
|
||||||
|
print(char(0x09))
|
||||||
|
print(str(roundd(n/max*100, 2)))
|
||||||
|
print("%")
|
||||||
|
print(char(0x09))
|
||||||
|
print(str(primes.length()))
|
||||||
|
print(" primes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(str(primes))
|
||||||
15
chars.ang
Normal file
15
chars.ang
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
MAX_WIDTH := 16
|
||||||
|
|
||||||
|
print(" ")
|
||||||
|
w := 1
|
||||||
|
n := 0x21
|
||||||
|
while n < 0xA0 {
|
||||||
|
print(char(n))
|
||||||
|
n = n + 1
|
||||||
|
w = w + 1
|
||||||
|
if w >= MAX_WIDTH {
|
||||||
|
write("")
|
||||||
|
w = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
215
cli/main.go
215
cli/main.go
|
|
@ -1,10 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"log"
|
"log"
|
||||||
"neemek.com/anglais/core"
|
"neemek.com/anglais/core"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -13,6 +16,7 @@ type Context struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunCmd struct {
|
type RunCmd struct {
|
||||||
|
IgnoreWarnings bool `name:"ignore-warnings" help:"Ignore warning messages"`
|
||||||
Bytecode bool `name:"bytecode" short:"c" help:"Run file as if it's bytecode"`
|
Bytecode bool `name:"bytecode" short:"c" help:"Run file as if it's bytecode"`
|
||||||
File string `arg:"" name:"file" help:"File to read program from" type:"existingfile"`
|
File string `arg:"" name:"file" help:"File to read program from" type:"existingfile"`
|
||||||
}
|
}
|
||||||
|
|
@ -22,45 +26,34 @@ type WorkingDirectoryResolver struct {
|
||||||
workingDirectory string
|
workingDirectory string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *WorkingDirectoryResolver) Resolve(path string) (core.Node, error) {
|
func (r *WorkingDirectoryResolver) Resolve(path string) (string, error) {
|
||||||
pth := filepath.Join(r.workingDirectory, path)
|
pth := filepath.Join(r.workingDirectory, path)
|
||||||
f, err := os.ReadFile(pth)
|
f, err := os.ReadFile(pth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
str := string(f)
|
return string(f), nil
|
||||||
|
|
||||||
l := core.NewLexer(str)
|
|
||||||
|
|
||||||
tokens, err := l.Tokenize()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := core.NewParser(tokens)
|
|
||||||
|
|
||||||
tree, err := p.Parse()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *RunCmd) Run(ctx *Context) error {
|
func (r *WorkingDirectoryResolver) IsSame(a, b string) bool {
|
||||||
|
apath := filepath.Clean(filepath.Join(r.workingDirectory, a))
|
||||||
|
bpath := filepath.Clean(filepath.Join(r.workingDirectory, b))
|
||||||
|
|
||||||
|
return apath == bpath
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeChunk(ctx *Context, fpath string, ignoreWarnings bool) (*core.Chunk, error) {
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Reading file")
|
log.Println("Reading file")
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.ReadFile(cmd.File)
|
f, err := os.ReadFile(fpath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var chunk *core.Chunk
|
|
||||||
if !cmd.Bytecode {
|
|
||||||
src := string(f)
|
src := string(f)
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
|
|
@ -74,41 +67,50 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
tokens, err := l.Tokenize()
|
tokens, err := l.Tokenize()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tokens) <= 1 {
|
if len(tokens) <= 1 {
|
||||||
log.Fatal("Empty file")
|
return nil, errors.New("empty file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Printf("Lexed %d tokens", len(tokens))
|
log.Printf("Lexed %d tokens", len(tokens))
|
||||||
|
|
||||||
}
|
}
|
||||||
p := core.NewParser(tokens)
|
p := core.NewParser(src, tokens)
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Initialized parser")
|
log.Println("Initialized parser")
|
||||||
}
|
}
|
||||||
|
|
||||||
tree, err := p.Parse()
|
pathat, err := filepath.Abs(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := p.Parse(pathat)
|
||||||
|
|
||||||
|
if ctx.Debug {
|
||||||
|
log.Printf("Parsed tree, meaning:\n%s", tree)
|
||||||
|
}
|
||||||
|
|
||||||
// if there were parsing errors, print them out
|
// if there were parsing errors, print them out
|
||||||
if err != nil {
|
if err != nil {
|
||||||
print(err.(*core.ParsingError).Format([]rune(src)))
|
print(err.(core.ParsingError).Format())
|
||||||
log.Fatal("Parsing had errors")
|
log.Fatal("Parsing had errors")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Initialized compiler")
|
log.Println("Initialized compiler")
|
||||||
}
|
}
|
||||||
c := core.NewCompiler()
|
c := core.NewCompiler([]rune(src))
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Setting imports resolver")
|
log.Println("Setting imports resolver")
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := filepath.Split(cmd.File)
|
dir, _ := path.Split(fpath)
|
||||||
c.SetImportsResolver(&WorkingDirectoryResolver{
|
c.SetImportsResolver(&WorkingDirectoryResolver{
|
||||||
dir,
|
dir,
|
||||||
})
|
})
|
||||||
|
|
@ -117,12 +119,44 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
log.Println("Compiling parse tree")
|
log.Println("Compiling parse tree")
|
||||||
}
|
}
|
||||||
err = c.Compile(tree)
|
err = c.Compile(tree)
|
||||||
|
if err != nil {
|
||||||
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
log.Fatal(e.Format())
|
||||||
|
}
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there were non-critical warnings, report them
|
||||||
|
if !ignoreWarnings && len(c.Warnings) != 0 {
|
||||||
|
for _, warning := range c.Warnings {
|
||||||
|
log.Println(warning.Format())
|
||||||
|
}
|
||||||
|
log.Fatal("compiler reported warning(s) (ignore warnings with the --ignore-warnings option)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Chunk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
|
var chunk *core.Chunk
|
||||||
|
if !cmd.Bytecode {
|
||||||
|
c, err := makeChunk(ctx, cmd.File, cmd.IgnoreWarnings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
chunk = c
|
||||||
|
} else {
|
||||||
|
if ctx.Debug {
|
||||||
|
log.Println("Reading file")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.ReadFile(cmd.File)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = c.Chunk
|
|
||||||
} else {
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Registering GOB types")
|
log.Println("Registering GOB types")
|
||||||
}
|
}
|
||||||
|
|
@ -159,70 +193,11 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
type CompileCmd struct {
|
type CompileCmd struct {
|
||||||
File string `arg:"" name:"file" help:"File to compile program from" type:"existingfile"`
|
File string `arg:"" name:"file" help:"File to compile program from" type:"existingfile"`
|
||||||
Output string `arg:"" name:"output" help:"File path to output bytecode to" type:"path"`
|
Output string `arg:"" name:"output" help:"File path to output bytecode to" type:"path"`
|
||||||
|
IgnoreWarnings bool `name:"ignore-warnings" help:"Ignore warning messages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *CompileCmd) Run(ctx *Context) error {
|
func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
if ctx.Debug {
|
c, err := makeChunk(ctx, cmd.File, cmd.IgnoreWarnings)
|
||||||
log.Println("Reading file")
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.ReadFile(cmd.File)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
src := string(f)
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Initializing lexer")
|
|
||||||
}
|
|
||||||
l := core.NewLexer(src)
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Lexing all tokens")
|
|
||||||
}
|
|
||||||
tokens, err := l.Tokenize()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Initializing parser")
|
|
||||||
}
|
|
||||||
p := core.NewParser(tokens)
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Parsing tree")
|
|
||||||
}
|
|
||||||
|
|
||||||
tree, err := p.Parse()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
print(err.(*core.ParsingError).Format([]rune(src)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Initialized compiler")
|
|
||||||
}
|
|
||||||
|
|
||||||
c := core.NewCompiler()
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Setting import resolver")
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, _ := filepath.Split(cmd.File)
|
|
||||||
c.SetImportsResolver(&WorkingDirectoryResolver{
|
|
||||||
dir,
|
|
||||||
})
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Compiling parse tree")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.Compile(tree)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +212,7 @@ func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
log.Println("Serializing chunk")
|
log.Println("Serializing chunk")
|
||||||
}
|
}
|
||||||
|
|
||||||
serialized := c.Chunk.Serialize()
|
serialized := c.Serialize()
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Writing file")
|
log.Println("Writing file")
|
||||||
|
|
@ -252,11 +227,59 @@ func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReplCmd struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *ReplCmd) Run(ctx *Context) error {
|
||||||
|
c := core.NewCompiler([]rune(""))
|
||||||
|
vm := core.NewVM(core.NewChunk([]core.Bytecode{}, []core.Value{}), 256, 256)
|
||||||
|
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
for {
|
||||||
|
print("> ")
|
||||||
|
src, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := core.NewLexer(src)
|
||||||
|
tokens, err := l.Tokenize()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p := core.NewParser(src, tokens)
|
||||||
|
prog, err := p.Parse("REPL")
|
||||||
|
if err != nil {
|
||||||
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
log.Print(e.Format())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetSource(src)
|
||||||
|
if err = c.Compile(prog); err != nil {
|
||||||
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
log.Print(e.Format())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.SetChunk(c.Chunk)
|
||||||
|
for vm.Next() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cli struct {
|
var cli struct {
|
||||||
Debug bool `short:"D" name:"debug" help:"Enable debug mode."`
|
Debug bool `short:"D" name:"debug" help:"Enable debug mode."`
|
||||||
|
|
||||||
Run RunCmd `cmd:"" name:"run" help:"Run program."`
|
Run RunCmd `cmd:"" name:"run" help:"Run program."`
|
||||||
CompileCmd CompileCmd `cmd:"" name:"compile" help:"Compile program to bytecode."`
|
Compile CompileCmd `cmd:"" name:"compile" help:"Compile program to bytecode."`
|
||||||
|
Repl ReplCmd `cmd:"" name:"repl" help:"Start a REPL loop."`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
24
codegen.ang
Normal file
24
codegen.ang
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
passphrase := "Hello world!".split("")
|
||||||
|
start := [0, 0, 0]
|
||||||
|
modulus := 10
|
||||||
|
base := byte("!")
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
n := 0
|
||||||
|
while n < passphrase.length() {
|
||||||
|
b := byte(passphrase.at(n))
|
||||||
|
|
||||||
|
v = start.at(i) + b - base
|
||||||
|
while v >= modulus {
|
||||||
|
v = v - modulus
|
||||||
|
}
|
||||||
|
|
||||||
|
start.put(i, v)
|
||||||
|
|
||||||
|
if i >= 3 {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
n = n + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -22,13 +22,22 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"func": {
|
"func": {
|
||||||
"func sum(a, b) {\n\treturn a + b\n}\nsum(1, 2)",
|
"func sum(a: number, b: number) number {\n\treturn a + b\n}\n_ = sum(1, 2)",
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"sum",
|
"sum",
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
Chunk: &Chunk{
|
Chunk: &Chunk{
|
||||||
Bytecode: []Bytecode{
|
Bytecode: []Bytecode{
|
||||||
InstructionDescend,
|
InstructionDescend,
|
||||||
|
|
@ -45,6 +54,63 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"list": {
|
||||||
|
"a := [1, 2]",
|
||||||
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"constant_list_concat": {
|
||||||
|
"a := [1, 2] + [3]",
|
||||||
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"list_concat": {
|
||||||
|
"a := [1, 2]\nb := a + [3]",
|
||||||
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
&VariableValue{
|
||||||
|
"b",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,18 +130,18 @@ func TestAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Initializing parser")
|
t.Log("Initializing parser")
|
||||||
p := NewParser(tokens)
|
p := NewParser(tc.src, tokens)
|
||||||
|
|
||||||
t.Log("Parsing tokens")
|
t.Log("Parsing tokens")
|
||||||
tree, err := p.Parse()
|
tree, err := p.Parse(tc.src)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
print(err.(*ParsingError).Format([]rune(tc.src)))
|
print(err.(ParsingError).Format())
|
||||||
t.Fatalf("parser had an error")
|
t.Fatalf("parser had an error")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Initializing compiler")
|
t.Log("Initializing compiler")
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune(tc.src))
|
||||||
|
|
||||||
t.Log("Compiling parse tree")
|
t.Log("Compiling parse tree")
|
||||||
err = c.Compile(tree)
|
err = c.Compile(tree)
|
||||||
|
|
@ -107,10 +173,10 @@ func BenchmarkAll(b *testing.B) {
|
||||||
l := NewLexer(tc.src)
|
l := NewLexer(tc.src)
|
||||||
tokens, _ := l.Tokenize()
|
tokens, _ := l.Tokenize()
|
||||||
|
|
||||||
p := NewParser(tokens)
|
p := NewParser(tc.src, tokens)
|
||||||
tree, _ := p.Parse()
|
tree, _ := p.Parse(tc.src)
|
||||||
|
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune(tc.src))
|
||||||
_ = c.Compile(tree)
|
_ = c.Compile(tree)
|
||||||
|
|
||||||
vm := NewVM(c.Chunk, 256, 256)
|
vm := NewVM(c.Chunk, 256, 256)
|
||||||
|
|
|
||||||
836
core/compiler.go
836
core/compiler.go
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewCompiler(t *testing.T) {
|
func TestNewCompiler(t *testing.T) {
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune{})
|
||||||
|
|
||||||
if c == nil {
|
if c == nil {
|
||||||
t.Fatal("NewCompiler returned nil")
|
t.Fatal("NewCompiler returned nil")
|
||||||
|
|
@ -23,39 +23,63 @@ func TestNewCompiler(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkNewCompiler(b *testing.B) {
|
func BenchmarkNewCompiler(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = NewCompiler()
|
_ = NewCompiler([]rune{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompileTestData struct {
|
type CompileTestData struct {
|
||||||
tree Node
|
program *Program
|
||||||
expectedStack []Value
|
expectedStack []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCompileTestData() map[string]CompileTestData {
|
func GetCompileTestData() map[string]CompileTestData {
|
||||||
return map[string]CompileTestData{
|
return map[string]CompileTestData{
|
||||||
"constant_string": {
|
"constant_string": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
&StringNode{
|
&StringNode{
|
||||||
"Hello world!",
|
"Hello world!",
|
||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
&StringValue{"Hello world!"},
|
&StringValue{"Hello world!"},
|
||||||
|
0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_false": {
|
"conditional_false": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ConditionalNode{
|
&ConditionalNode{
|
||||||
&BooleanNode{
|
&BooleanNode{
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -63,14 +87,21 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -81,18 +112,23 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_true": {
|
"conditional_true": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ConditionalNode{
|
&ConditionalNode{
|
||||||
&BooleanNode{
|
&BooleanNode{
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -100,14 +136,21 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -118,18 +161,23 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_else_false": {
|
"conditional_else_false": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ConditionalNode{
|
&ConditionalNode{
|
||||||
&BooleanNode{
|
&BooleanNode{
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -137,10 +185,13 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -148,13 +199,20 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
2,
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -165,18 +223,23 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conditional_else_true": {
|
"conditional_else_true": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ConditionalNode{
|
&ConditionalNode{
|
||||||
&BooleanNode{
|
&BooleanNode{
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -184,10 +247,13 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -195,13 +261,20 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
"a",
|
"a",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
2,
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -212,41 +285,89 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"addition": {
|
"addition": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
|
&BlockNode{
|
||||||
|
[]Node{
|
||||||
|
&AssignNode{
|
||||||
|
"a",
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
2,
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
&NumberValue{3},
|
&NumberValue{3},
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sum_function": {&BlockNode{
|
},
|
||||||
|
"sum_function": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"sum",
|
"sum",
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
"sum",
|
"sum",
|
||||||
[]string{"a", "b"},
|
[]FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&NumberSignature{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&ReturnNode{
|
&ReturnNode{
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&ReferenceNode{"a"},
|
&ReferenceNode{
|
||||||
&ReferenceNode{"b"},
|
"a",
|
||||||
},
|
0, 0,
|
||||||
|
},
|
||||||
|
&ReferenceNode{
|
||||||
|
"b",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
|
|
@ -254,7 +375,17 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
|
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
"sum",
|
"sum",
|
||||||
[]string{"a", "b"},
|
[]FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&NumberSignature{},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionDescend,
|
InstructionDescend,
|
||||||
|
|
@ -275,43 +406,63 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"remove_func_vars": {
|
"remove_func_vars": {
|
||||||
|
&Program{
|
||||||
|
[]string{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"a",
|
"a",
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
"a",
|
"a",
|
||||||
[]string{},
|
[]FunctionParameter{},
|
||||||
|
&NumberSignature{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{1},
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ReturnNode{
|
&ReturnNode{
|
||||||
&ReferenceNode{"b"},
|
&ReferenceNode{
|
||||||
|
"b",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&CallNode{
|
&CallNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
[]Node{},
|
[]Node{},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
[]Value{
|
[]Value{
|
||||||
&VariableValue{
|
&VariableValue{
|
||||||
"a",
|
"a",
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
"a",
|
"a",
|
||||||
[]string{},
|
[]FunctionParameter{},
|
||||||
|
&NumberSignature{},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionDescend,
|
InstructionDescend,
|
||||||
|
|
@ -331,6 +482,58 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"two_lists": {
|
||||||
|
program: &Program{
|
||||||
|
[]string{},
|
||||||
|
&BlockNode{
|
||||||
|
statements: []Node{
|
||||||
|
&AssignNode{
|
||||||
|
name: "a",
|
||||||
|
value: &ListNode{
|
||||||
|
items: []Node{
|
||||||
|
&NumberNode{value: 1},
|
||||||
|
&NumberNode{value: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
declare: true,
|
||||||
|
},
|
||||||
|
&AssignNode{
|
||||||
|
name: "b",
|
||||||
|
value: &ListNode{
|
||||||
|
items: []Node{
|
||||||
|
&StringNode{value: "Hello"},
|
||||||
|
&StringNode{value: "world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
declare: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
expectedStack: []Value{
|
||||||
|
&VariableValue{
|
||||||
|
name: "a",
|
||||||
|
value: &ListValue{
|
||||||
|
Items: []Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scope: 0,
|
||||||
|
},
|
||||||
|
&VariableValue{
|
||||||
|
name: "b",
|
||||||
|
value: &ListValue{
|
||||||
|
Items: []Value{
|
||||||
|
&StringValue{"Hello"},
|
||||||
|
&StringValue{"world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scope: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,7 +546,7 @@ func printChunk(t *testing.T, name string, chunk *Chunk) {
|
||||||
t.Logf("=-= constants =-=")
|
t.Logf("=-= constants =-=")
|
||||||
|
|
||||||
for i, ct := range chunk.Constants {
|
for i, ct := range chunk.Constants {
|
||||||
t.Logf("c=%d \t%s", i, ct)
|
t.Logf("c=%d \t%s", i, ct.DebugString())
|
||||||
|
|
||||||
f, ok := ct.(*FunctionValue)
|
f, ok := ct.(*FunctionValue)
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -360,10 +563,10 @@ func TestCompile(t *testing.T) {
|
||||||
for name, testCase := range data {
|
for name, testCase := range data {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Log("Initializing compiler")
|
t.Log("Initializing compiler")
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune(testCase.program.String()))
|
||||||
|
|
||||||
t.Log("Compiling node tree")
|
t.Log("Compiling node tree")
|
||||||
err := c.Compile(testCase.tree)
|
err := c.Compile(testCase.program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -389,8 +592,8 @@ func BenchmarkCompile(b *testing.B) {
|
||||||
for name, testCase := range data {
|
for name, testCase := range data {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune{})
|
||||||
_ = c.Compile(testCase.tree)
|
_ = c.Compile(testCase.program)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +602,7 @@ func BenchmarkCompile(b *testing.B) {
|
||||||
func TestCompiler_AddU16(t *testing.T) {
|
func TestCompiler_AddU16(t *testing.T) {
|
||||||
for i := 0; i <= 0xffff; i++ {
|
for i := 0; i <= 0xffff; i++ {
|
||||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune{})
|
||||||
c.addU16(uint16(i))
|
c.addU16(uint16(i))
|
||||||
|
|
||||||
if c.Chunk.Bytecode[0] != Bytecode(i>>8) {
|
if c.Chunk.Bytecode[0] != Bytecode(i>>8) {
|
||||||
|
|
@ -417,24 +620,9 @@ func TestCompiler_CleanStack(t *testing.T) {
|
||||||
cases := GetCompileTestData()
|
cases := GetCompileTestData()
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
switch tc.tree.Type() {
|
|
||||||
// skip all expected unclean nodes
|
|
||||||
case StringNodeType, NumberNodeType, ReferenceNodeType, BooleanNodeType, NilNodeType, BinaryNodeType, ReturnNodeType:
|
|
||||||
continue
|
|
||||||
|
|
||||||
case CallNodeType:
|
|
||||||
if tc.tree.(*CallNode).keep {
|
|
||||||
// if we know it should be unclean, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean statements
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
c := NewCompiler()
|
c := NewCompiler([]rune(tc.program.String()))
|
||||||
err := c.Compile(tc.tree)
|
err := c.Compile(tc.program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ const (
|
||||||
TokenSemicolon
|
TokenSemicolon
|
||||||
|
|
||||||
TokenNumber
|
TokenNumber
|
||||||
|
TokenHexadecimal
|
||||||
TokenString
|
TokenString
|
||||||
TokenName
|
TokenName
|
||||||
|
|
||||||
|
|
@ -53,6 +54,7 @@ const (
|
||||||
|
|
||||||
TokenComma
|
TokenComma
|
||||||
TokenDot
|
TokenDot
|
||||||
|
TokenColon
|
||||||
|
|
||||||
TokenAssign
|
TokenAssign
|
||||||
TokenDeclare
|
TokenDeclare
|
||||||
|
|
@ -64,6 +66,7 @@ const (
|
||||||
TokenLessThanOrEqual
|
TokenLessThanOrEqual
|
||||||
|
|
||||||
TokenDoubleAmpersand
|
TokenDoubleAmpersand
|
||||||
|
TokenPipe
|
||||||
TokenDoublePipe
|
TokenDoublePipe
|
||||||
|
|
||||||
TokenBreakpoint
|
TokenBreakpoint
|
||||||
|
|
@ -153,6 +156,12 @@ func (t TokenType) String() string {
|
||||||
return "close bracket"
|
return "close bracket"
|
||||||
case TokenImport:
|
case TokenImport:
|
||||||
return "import"
|
return "import"
|
||||||
|
case TokenColon:
|
||||||
|
return "colon"
|
||||||
|
case TokenPipe:
|
||||||
|
return "pipe"
|
||||||
|
case TokenHexadecimal:
|
||||||
|
return "hexadecimal"
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
return "UNDEFINED TOKENTYPE STRING CONVERSION"
|
||||||
|
|
@ -234,11 +243,11 @@ func (l *Lexer) NextToken() (Token, error) {
|
||||||
case '.':
|
case '.':
|
||||||
return l.makeToken(TokenDot), nil
|
return l.makeToken(TokenDot), nil
|
||||||
case ':':
|
case ':':
|
||||||
if !l.accept('=') {
|
if l.accept('=') {
|
||||||
return l.makeToken(TokenError), errors.New("malformed token (got ':', expected '=' to follow)")
|
return l.makeToken(TokenDeclare), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.makeToken(TokenDeclare), nil
|
return l.makeToken(TokenColon), nil
|
||||||
case '!':
|
case '!':
|
||||||
if l.accept('=') {
|
if l.accept('=') {
|
||||||
return l.makeToken(TokenBangEquals), nil
|
return l.makeToken(TokenBangEquals), nil
|
||||||
|
|
@ -276,7 +285,7 @@ func (l *Lexer) NextToken() (Token, error) {
|
||||||
return l.makeToken(TokenDoublePipe), nil
|
return l.makeToken(TokenDoublePipe), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.makeToken(TokenError), errors.New("malformed token (got '|', expected '|' to follow)")
|
return l.makeToken(TokenPipe), nil
|
||||||
|
|
||||||
case '"':
|
case '"':
|
||||||
// include ending quote
|
// include ending quote
|
||||||
|
|
@ -327,6 +336,19 @@ func (l *Lexer) NextToken() (Token, error) {
|
||||||
default:
|
default:
|
||||||
return l.makeToken(TokenName), nil
|
return l.makeToken(TokenName), nil
|
||||||
}
|
}
|
||||||
|
} else if c == '0' && l.peek() != '.' {
|
||||||
|
if l.peek() == 'x' {
|
||||||
|
l.advance()
|
||||||
|
|
||||||
|
// hex
|
||||||
|
for unicode.In(l.peek(), unicode.Hex_Digit) {
|
||||||
|
l.advance()
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.makeToken(TokenHexadecimal), 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()
|
||||||
|
|
|
||||||
227
core/nodes.go
227
core/nodes.go
|
|
@ -11,6 +11,8 @@ type NodeType int
|
||||||
type Node interface {
|
type Node interface {
|
||||||
Type() NodeType
|
Type() NodeType
|
||||||
String() string
|
String() string
|
||||||
|
|
||||||
|
Bounds() (Pos, Pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -21,6 +23,7 @@ const (
|
||||||
NilNodeType
|
NilNodeType
|
||||||
ListNodeType
|
ListNodeType
|
||||||
BinaryNodeType
|
BinaryNodeType
|
||||||
|
UnaryNodeType
|
||||||
BlockNodeType
|
BlockNodeType
|
||||||
ConditionalNodeType
|
ConditionalNodeType
|
||||||
LoopNodeType
|
LoopNodeType
|
||||||
|
|
@ -29,7 +32,6 @@ const (
|
||||||
FunctionNodeType
|
FunctionNodeType
|
||||||
ReturnNodeType
|
ReturnNodeType
|
||||||
AccessNodeType
|
AccessNodeType
|
||||||
ImportNodeType
|
|
||||||
BreakpointNodeType
|
BreakpointNodeType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -67,8 +69,8 @@ func (n NodeType) String() string {
|
||||||
return "Access"
|
return "Access"
|
||||||
case BreakpointNodeType:
|
case BreakpointNodeType:
|
||||||
return "Breakpoint"
|
return "Breakpoint"
|
||||||
case ImportNodeType:
|
case UnaryNodeType:
|
||||||
return "Import"
|
return "Unary"
|
||||||
}
|
}
|
||||||
return "Invalid Node Type"
|
return "Invalid Node Type"
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +78,9 @@ func (n NodeType) String() string {
|
||||||
// ReferenceNode a reference to a variable on the stack
|
// ReferenceNode a reference to a variable on the stack
|
||||||
type ReferenceNode struct {
|
type ReferenceNode struct {
|
||||||
name string
|
name string
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ReferenceNode) Type() NodeType {
|
func (n ReferenceNode) Type() NodeType {
|
||||||
|
|
@ -86,10 +91,17 @@ func (n ReferenceNode) String() string {
|
||||||
return n.name
|
return n.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n ReferenceNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// StringNode string/text values
|
// StringNode string/text values
|
||||||
type StringNode struct {
|
type StringNode struct {
|
||||||
value string
|
value string
|
||||||
quoted string
|
quoted string
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n StringNode) Type() NodeType {
|
func (n StringNode) Type() NodeType {
|
||||||
|
|
@ -100,8 +112,15 @@ func (n StringNode) String() string {
|
||||||
return n.quoted
|
return n.quoted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n StringNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
type NumberNode struct {
|
type NumberNode struct {
|
||||||
value float64
|
value float64
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NumberNode) Type() NodeType {
|
func (n NumberNode) Type() NodeType {
|
||||||
|
|
@ -112,9 +131,17 @@ func (n NumberNode) String() string {
|
||||||
return strconv.FormatFloat(n.value, 'g', -1, NumberSize)
|
return strconv.FormatFloat(n.value, 'g', -1, NumberSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n NumberNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// ListNode a list or sequence of values (items)
|
// ListNode a list or sequence of values (items)
|
||||||
type ListNode struct {
|
type ListNode struct {
|
||||||
items []Node
|
items []Node
|
||||||
|
content TypeSignature
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ListNode) Type() NodeType {
|
func (n ListNode) Type() NodeType {
|
||||||
|
|
@ -125,18 +152,25 @@ func (n ListNode) String() string {
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
sb.WriteString("[")
|
sb.WriteString("[")
|
||||||
for i, item := range n.items {
|
for i, item := range n.items {
|
||||||
sb.WriteString(item.String())
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
|
sb.WriteString(item.String())
|
||||||
}
|
}
|
||||||
sb.WriteString("]")
|
sb.WriteString("]")
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n ListNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
type AccessNode struct {
|
type AccessNode struct {
|
||||||
source Node
|
source Node
|
||||||
property string
|
property string
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n AccessNode) Type() NodeType {
|
func (n AccessNode) Type() NodeType {
|
||||||
|
|
@ -147,6 +181,10 @@ func (n AccessNode) String() string {
|
||||||
return fmt.Sprintf("(%s from %s)", n.property, n.source)
|
return fmt.Sprintf("(%s from %s)", n.property, n.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n AccessNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
type BinaryOperation uint
|
type BinaryOperation uint
|
||||||
|
|
||||||
func (n BinaryOperation) String() string {
|
func (n BinaryOperation) String() string {
|
||||||
|
|
@ -198,11 +236,45 @@ const (
|
||||||
BinaryGreaterEqual
|
BinaryGreaterEqual
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (n BinaryOperation) Symbol() string {
|
||||||
|
switch n {
|
||||||
|
case BinaryAddition:
|
||||||
|
return "+"
|
||||||
|
case BinarySubtraction:
|
||||||
|
return "-"
|
||||||
|
case BinaryMultiplication:
|
||||||
|
return "*"
|
||||||
|
case BinaryDivision:
|
||||||
|
return "/"
|
||||||
|
case BinaryEquality:
|
||||||
|
return "=="
|
||||||
|
case BinaryInequality:
|
||||||
|
return "!="
|
||||||
|
case BinaryLess:
|
||||||
|
return "<"
|
||||||
|
case BinaryGreater:
|
||||||
|
return ">"
|
||||||
|
case BinaryAnd:
|
||||||
|
return "&&"
|
||||||
|
case BinaryOr:
|
||||||
|
return "||"
|
||||||
|
case BinaryLessEqual:
|
||||||
|
return "<="
|
||||||
|
case BinaryGreaterEqual:
|
||||||
|
return ">="
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unsupported binary operation to symbol conversion for " + n.String())
|
||||||
|
}
|
||||||
|
|
||||||
// BinaryNode All operations which take 2 variables
|
// BinaryNode All operations which take 2 variables
|
||||||
type BinaryNode struct {
|
type BinaryNode struct {
|
||||||
BinaryOperation
|
BinaryOperation
|
||||||
Left Node
|
Left Node
|
||||||
Right Node
|
Right Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n BinaryNode) Type() NodeType {
|
func (n BinaryNode) Type() NodeType {
|
||||||
|
|
@ -213,9 +285,65 @@ func (n BinaryNode) String() string {
|
||||||
return fmt.Sprintf("%s %s %s", n.Left.String(), n.BinaryOperation.String(), n.Right.String())
|
return fmt.Sprintf("%s %s %s", n.Left.String(), n.BinaryOperation.String(), n.Right.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n BinaryNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnaryOperation int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnaryNegate UnaryOperation = iota
|
||||||
|
UnaryNot
|
||||||
|
)
|
||||||
|
|
||||||
|
func (op UnaryOperation) String() string {
|
||||||
|
switch op {
|
||||||
|
case UnaryNegate:
|
||||||
|
return "negate"
|
||||||
|
case UnaryNot:
|
||||||
|
return "not"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unimplemented unary operation to string conversion")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op UnaryOperation) Symbol() string {
|
||||||
|
switch op {
|
||||||
|
case UnaryNegate:
|
||||||
|
return "-"
|
||||||
|
case UnaryNot:
|
||||||
|
return "!"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unimplemented unary operation to symbol conversion")
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnaryNode struct {
|
||||||
|
UnaryOperation
|
||||||
|
value Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n UnaryNode) Type() NodeType {
|
||||||
|
return UnaryNodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n UnaryNode) String() string {
|
||||||
|
return fmt.Sprintf("%s %s", n.UnaryOperation.String(), n.value.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n UnaryNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// BooleanNode boolean value
|
// BooleanNode boolean value
|
||||||
type BooleanNode struct {
|
type BooleanNode struct {
|
||||||
value bool
|
value bool
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n BooleanNode) Type() NodeType {
|
func (n BooleanNode) Type() NodeType {
|
||||||
|
|
@ -226,8 +354,15 @@ func (n BooleanNode) String() string {
|
||||||
return strconv.FormatBool(n.value)
|
return strconv.FormatBool(n.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n BooleanNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// NilNode nil value
|
// NilNode nil value
|
||||||
type NilNode struct{}
|
type NilNode struct {
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
|
}
|
||||||
|
|
||||||
func (n NilNode) Type() NodeType {
|
func (n NilNode) Type() NodeType {
|
||||||
return NilNodeType
|
return NilNodeType
|
||||||
|
|
@ -237,9 +372,16 @@ func (n NilNode) String() string {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n NilNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// BlockNode block node with statements
|
// BlockNode block node with statements
|
||||||
type BlockNode struct {
|
type BlockNode struct {
|
||||||
statements []Node
|
statements []Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n BlockNode) Type() NodeType {
|
func (n BlockNode) Type() NodeType {
|
||||||
|
|
@ -257,16 +399,8 @@ func (n BlockNode) String() string {
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImportNode struct {
|
func (n BlockNode) Bounds() (Pos, Pos) {
|
||||||
path string
|
return n.start, n.end
|
||||||
}
|
|
||||||
|
|
||||||
func (n ImportNode) Type() NodeType {
|
|
||||||
return ImportNodeType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n ImportNode) String() string {
|
|
||||||
return fmt.Sprintf("import %s", n.path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConditionalNode conditionals (if statements)
|
// ConditionalNode conditionals (if statements)
|
||||||
|
|
@ -274,6 +408,9 @@ type ConditionalNode struct {
|
||||||
condition Node
|
condition Node
|
||||||
do Node
|
do Node
|
||||||
otherwise Node
|
otherwise Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ConditionalNode) Type() NodeType {
|
func (n ConditionalNode) Type() NodeType {
|
||||||
|
|
@ -281,13 +418,24 @@ func (n ConditionalNode) Type() NodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ConditionalNode) String() string {
|
func (n ConditionalNode) String() string {
|
||||||
|
if n.otherwise == nil {
|
||||||
|
return fmt.Sprintf("if %s then %s", n.condition.String(), n.do.String())
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("if %s then %s otheriwise %s", n.condition.String(), n.do.String(), n.otherwise.String())
|
return fmt.Sprintf("if %s then %s otheriwise %s", n.condition.String(), n.do.String(), n.otherwise.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n ConditionalNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// LoopNode Loops (for/while)
|
// LoopNode Loops (for/while)
|
||||||
type LoopNode struct {
|
type LoopNode struct {
|
||||||
condition Node
|
condition Node
|
||||||
do Node
|
do Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n LoopNode) Type() NodeType {
|
func (n LoopNode) Type() NodeType {
|
||||||
|
|
@ -298,11 +446,18 @@ func (n LoopNode) String() string {
|
||||||
return fmt.Sprintf("while %s loop %s", n.condition.String(), n.do.String())
|
return fmt.Sprintf("while %s loop %s", n.condition.String(), n.do.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n LoopNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// AssignNode assignment
|
// AssignNode assignment
|
||||||
type AssignNode struct {
|
type AssignNode struct {
|
||||||
name string
|
name string
|
||||||
value Node
|
value Node
|
||||||
declare bool
|
declare bool
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n AssignNode) Type() NodeType {
|
func (n AssignNode) Type() NodeType {
|
||||||
|
|
@ -313,11 +468,18 @@ func (n AssignNode) String() string {
|
||||||
return fmt.Sprintf("set %s to %s", n.name, n.value)
|
return fmt.Sprintf("set %s to %s", n.name, n.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n AssignNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// CallNode function call
|
// CallNode function call
|
||||||
type CallNode struct {
|
type CallNode struct {
|
||||||
source Node
|
source Node
|
||||||
args []Node
|
args []Node
|
||||||
keep bool
|
keep bool
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n CallNode) Type() NodeType {
|
func (n CallNode) Type() NodeType {
|
||||||
|
|
@ -328,11 +490,24 @@ func (n CallNode) String() string {
|
||||||
return fmt.Sprintf("call %s with args (%s)", n.source.String(), n.args)
|
return fmt.Sprintf("call %s with args (%s)", n.source.String(), n.args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n CallNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// FunctionNode definition of function
|
// FunctionNode definition of function
|
||||||
type FunctionNode struct {
|
type FunctionNode struct {
|
||||||
name string
|
name string
|
||||||
params []string
|
parameters []FunctionParameter
|
||||||
|
yield TypeSignature
|
||||||
logic Node
|
logic Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionParameter struct {
|
||||||
|
Name string
|
||||||
|
Signature TypeSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n FunctionNode) Type() NodeType {
|
func (n FunctionNode) Type() NodeType {
|
||||||
|
|
@ -343,9 +518,16 @@ func (n FunctionNode) String() string {
|
||||||
return fmt.Sprintf("definition of %s, do %s", n.name, n.logic.String())
|
return fmt.Sprintf("definition of %s, do %s", n.name, n.logic.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n FunctionNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
// ReturnNode return a value out of this context
|
// ReturnNode return a value out of this context
|
||||||
type ReturnNode struct {
|
type ReturnNode struct {
|
||||||
value Node
|
value Node
|
||||||
|
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n ReturnNode) Type() NodeType {
|
func (n ReturnNode) Type() NodeType {
|
||||||
|
|
@ -356,7 +538,14 @@ func (n ReturnNode) String() string {
|
||||||
return fmt.Sprintf("return %s", n.value)
|
return fmt.Sprintf("return %s", n.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BreakpointNode struct{}
|
func (n ReturnNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
||||||
|
type BreakpointNode struct {
|
||||||
|
start Pos
|
||||||
|
end Pos
|
||||||
|
}
|
||||||
|
|
||||||
func (n BreakpointNode) Type() NodeType {
|
func (n BreakpointNode) Type() NodeType {
|
||||||
return BreakpointNodeType
|
return BreakpointNodeType
|
||||||
|
|
@ -365,3 +554,7 @@ func (n BreakpointNode) Type() NodeType {
|
||||||
func (n BreakpointNode) String() string {
|
func (n BreakpointNode) String() string {
|
||||||
return "breakpoint"
|
return "breakpoint"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n BreakpointNode) Bounds() (Pos, Pos) {
|
||||||
|
return n.start, n.end
|
||||||
|
}
|
||||||
|
|
|
||||||
400
core/parser.go
400
core/parser.go
|
|
@ -8,17 +8,24 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FormatedError interface {
|
||||||
|
Error() string
|
||||||
|
Format() string
|
||||||
|
}
|
||||||
|
|
||||||
type ParsingError struct {
|
type ParsingError struct {
|
||||||
Description string
|
Description string
|
||||||
Causer *Token
|
Causer *Token
|
||||||
|
Source string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ParsingError) Error() string {
|
func (p ParsingError) Error() string {
|
||||||
return p.Description
|
return p.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format Print a rich and informative error
|
// Format Print a rich and informative error
|
||||||
func (p *ParsingError) Format(src []rune) string {
|
func (p ParsingError) Format() string {
|
||||||
|
src := []rune(p.Source)
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
|
|
||||||
lineNumber := 1
|
lineNumber := 1
|
||||||
|
|
@ -38,13 +45,17 @@ func (p *ParsingError) Format(src []rune) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.WriteString(" \t v ")
|
descriptor := fmt.Sprintf("%d:%d", lineNumber, int(p.Causer.Start)-lineBeginning+1)
|
||||||
builder.WriteString(p.Description)
|
builder.WriteString(p.Description)
|
||||||
builder.WriteRune('\n')
|
builder.WriteRune('\n')
|
||||||
|
|
||||||
builder.WriteString(fmt.Sprintf(" %d:%d\t | %s", lineNumber, int(p.Causer.Start)-lineBeginning+1, string(src[lineBeginning:lineEnd])))
|
builder.WriteString(descriptor)
|
||||||
|
builder.WriteString(" | ")
|
||||||
|
builder.WriteString(string(src[lineBeginning:lineEnd]))
|
||||||
|
|
||||||
builder.WriteString("\n\t ^")
|
builder.WriteString("\n")
|
||||||
|
builder.WriteString(strings.Repeat(" ", len(descriptor)))
|
||||||
|
builder.WriteString(" ")
|
||||||
for i := lineBeginning; i <= int(p.Causer.Start); i++ {
|
for i := lineBeginning; i <= int(p.Causer.Start); i++ {
|
||||||
builder.WriteRune(' ')
|
builder.WriteRune(' ')
|
||||||
}
|
}
|
||||||
|
|
@ -58,20 +69,45 @@ func (p *ParsingError) Format(src []rune) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
|
source string
|
||||||
tokens []Token
|
tokens []Token
|
||||||
prev *Token
|
prev *Token
|
||||||
curr *Token
|
curr *Token
|
||||||
pos Pos
|
pos Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParser(tokens []Token) *Parser {
|
func NewParser(source string, tokens []Token) *Parser {
|
||||||
return &Parser{
|
return &Parser{
|
||||||
|
source: source,
|
||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) Parse() (Node, error) {
|
type Program struct {
|
||||||
|
Imports []string
|
||||||
|
Block *BlockNode
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Program) String() string {
|
||||||
|
builder := strings.Builder{}
|
||||||
|
|
||||||
|
builder.WriteString("=== Imports ===\n")
|
||||||
|
for _, i := range p.Imports {
|
||||||
|
builder.WriteString(i)
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
|
builder.WriteString("===============\n")
|
||||||
|
|
||||||
|
builder.WriteString(p.Block.String())
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Parse(path string) (*Program, error) {
|
||||||
|
imports := make([]string, 0)
|
||||||
|
|
||||||
// top level statements
|
// top level statements
|
||||||
statements := make([]Node, 0)
|
statements := make([]Node, 0)
|
||||||
|
|
||||||
|
|
@ -79,17 +115,33 @@ func (p *Parser) Parse() (Node, error) {
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
for int(p.pos) < len(p.tokens) && p.curr.Type != TokenEOF {
|
for int(p.pos) < len(p.tokens) && p.curr.Type != TokenEOF {
|
||||||
|
if p.accept(TokenImport) {
|
||||||
|
if err := p.expect(TokenString, "import requires a path/name to import"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imports = append(imports, p.prev.Lexeme[1:len(p.prev.Lexeme)-1])
|
||||||
|
}
|
||||||
|
|
||||||
b, err := p.block(true)
|
b, err := p.block(true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b != nil {
|
||||||
statements = append(statements, b)
|
statements = append(statements, b)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &BlockNode{
|
return &Program{
|
||||||
statements: statements,
|
imports,
|
||||||
|
&BlockNode{
|
||||||
|
statements,
|
||||||
|
0,
|
||||||
|
p.curr.Start + p.curr.Length,
|
||||||
|
},
|
||||||
|
path,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,9 +159,9 @@ func (p *Parser) accept(tokenType TokenType) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expect(tokenType TokenType) error {
|
func (p *Parser) expect(tokenType TokenType, reason string) error {
|
||||||
if !p.accept(tokenType) {
|
if !p.accept(tokenType) {
|
||||||
return p.error("Expected token "+tokenType.String()+", got "+p.curr.Type.String(), p.curr)
|
return p.error(fmt.Sprintf("Expected token %s, got %s; %s", tokenType, p.curr.Type, reason), p.curr)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -134,9 +186,10 @@ func (p *Parser) advance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) error(error string, causer *Token) error {
|
func (p *Parser) error(error string, causer *Token) error {
|
||||||
return &ParsingError{
|
return ParsingError{
|
||||||
Description: error,
|
Description: error,
|
||||||
Causer: causer,
|
Causer: causer,
|
||||||
|
Source: p.source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,6 +200,8 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &StringNode{
|
return &StringNode{
|
||||||
(*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.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNumber:
|
case TokenNumber:
|
||||||
|
|
@ -159,17 +214,37 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
return &NumberNode{
|
return &NumberNode{
|
||||||
num,
|
num,
|
||||||
|
p.prev.Start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case TokenHexadecimal:
|
||||||
|
p.advance()
|
||||||
|
start := (*p.prev).Start
|
||||||
|
num, err := strconv.ParseUint((*p.prev).Lexeme[2:], 16, NumberSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NumberNode{
|
||||||
|
float64(num),
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenTrue:
|
case TokenTrue:
|
||||||
p.advance()
|
p.advance()
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
true,
|
true,
|
||||||
|
p.prev.Start,
|
||||||
|
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.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNil:
|
case TokenNil:
|
||||||
|
|
@ -178,11 +253,25 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
case TokenOpenBracket:
|
case TokenOpenBracket:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
|
if p.accept(TokenCloseBracket) {
|
||||||
|
s, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ListNode{
|
||||||
|
[]Node{},
|
||||||
|
s,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
var values []Node
|
var values []Node
|
||||||
for !p.accept(TokenCloseBracket) {
|
for !p.accept(TokenCloseBracket) {
|
||||||
if len(values) > 0 {
|
if len(values) > 0 {
|
||||||
if err := p.expect(TokenComma); err != nil {
|
if err := p.expect(TokenComma, "list values must be separated by a comma"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,24 +287,48 @@ func (p *Parser) factor() (Node, error) {
|
||||||
|
|
||||||
return &ListNode{
|
return &ListNode{
|
||||||
values,
|
values,
|
||||||
|
nil,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
// unary minus
|
// unary minus
|
||||||
case TokenMinus:
|
case TokenMinus:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
first := p.prev
|
||||||
|
|
||||||
f, err := p.factor()
|
f, err := p.factor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &BinaryNode{
|
return &UnaryNode{
|
||||||
BinarySubtraction,
|
UnaryNegate,
|
||||||
&NumberNode{0},
|
|
||||||
f,
|
f,
|
||||||
|
first.Start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case TokenBang:
|
||||||
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
|
v, err := p.factor()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UnaryNode{
|
||||||
|
UnaryNot,
|
||||||
|
v,
|
||||||
|
start,
|
||||||
|
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
|
||||||
|
nameEnd := start + p.prev.Length
|
||||||
|
|
||||||
if p.curr.Type == TokenOpenParenthesis {
|
if p.curr.Type == TokenOpenParenthesis {
|
||||||
args, err := p.parseArgs()
|
args, err := p.parseArgs()
|
||||||
|
|
@ -226,23 +339,39 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &CallNode{
|
return &CallNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
name,
|
name,
|
||||||
|
start,
|
||||||
|
nameEnd,
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
true,
|
true,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ReferenceNode{
|
return &ReferenceNode{
|
||||||
name,
|
name,
|
||||||
|
start,
|
||||||
|
nameEnd,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenFunc:
|
case TokenFunc:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
params, err := p.parseParams()
|
params, err := p.parseParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sig TypeSignature = &NilSignature{}
|
||||||
|
if p.curr.Type != TokenOpenBrace {
|
||||||
|
sig, err = p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b, err := p.block(false)
|
b, err := p.block(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -251,7 +380,10 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &FunctionNode{
|
return &FunctionNode{
|
||||||
"*",
|
"*",
|
||||||
params,
|
params,
|
||||||
|
sig,
|
||||||
b,
|
b,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenOpenParenthesis:
|
case TokenOpenParenthesis:
|
||||||
|
|
@ -260,20 +392,20 @@ func (p *Parser) factor() (Node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
if err := p.expect(TokenCloseParenthesis, "an opened parenthesis must be closed"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err := p.error("invalid factor", p.curr)
|
return nil, p.error("invalid factor", p.curr)
|
||||||
p.advance()
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) prop() (Node, error) {
|
func (p *Parser) prop() (Node, error) {
|
||||||
|
start := p.curr.Start
|
||||||
|
|
||||||
v, err := p.factor()
|
v, err := p.factor()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -281,7 +413,7 @@ func (p *Parser) prop() (Node, error) {
|
||||||
|
|
||||||
// parse chains of prop-getting ( "".split().join().length.round() )
|
// parse chains of prop-getting ( "".split().join().length.round() )
|
||||||
for p.accept(TokenDot) {
|
for p.accept(TokenDot) {
|
||||||
if err := p.expect(TokenName); err != nil {
|
if err := p.expect(TokenName, "property must be a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
property := (*p.prev).Lexeme
|
property := (*p.prev).Lexeme
|
||||||
|
|
@ -289,6 +421,8 @@ func (p *Parser) prop() (Node, error) {
|
||||||
v = &AccessNode{
|
v = &AccessNode{
|
||||||
v,
|
v,
|
||||||
property,
|
property,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if called, also add
|
// if called, also add
|
||||||
|
|
@ -302,6 +436,8 @@ func (p *Parser) prop() (Node, error) {
|
||||||
v,
|
v,
|
||||||
args,
|
args,
|
||||||
true,
|
true,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,6 +446,7 @@ func (p *Parser) prop() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) product() (Node, error) {
|
func (p *Parser) product() (Node, error) {
|
||||||
|
start := p.curr.Start
|
||||||
left, err := p.prop()
|
left, err := p.prop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -331,6 +468,8 @@ func (p *Parser) product() (Node, error) {
|
||||||
op,
|
op,
|
||||||
left,
|
left,
|
||||||
f,
|
f,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,6 +477,8 @@ func (p *Parser) product() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) term() (Node, error) {
|
func (p *Parser) term() (Node, error) {
|
||||||
|
start := p.curr.Start
|
||||||
|
|
||||||
left, err := p.product()
|
left, err := p.product()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -359,6 +500,8 @@ func (p *Parser) term() (Node, error) {
|
||||||
op,
|
op,
|
||||||
left,
|
left,
|
||||||
pr,
|
pr,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,6 +509,7 @@ func (p *Parser) term() (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) comparison() (Node, error) {
|
func (p *Parser) comparison() (Node, error) {
|
||||||
|
start := p.curr.Start
|
||||||
left, err := p.term()
|
left, err := p.term()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -403,10 +547,13 @@ func (p *Parser) comparison() (Node, error) {
|
||||||
op,
|
op,
|
||||||
left,
|
left,
|
||||||
t,
|
t,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) condition() (Node, error) {
|
func (p *Parser) condition() (Node, error) {
|
||||||
|
start := p.curr.Start
|
||||||
left, err := p.comparison()
|
left, err := p.comparison()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -434,12 +581,15 @@ func (p *Parser) condition() (Node, error) {
|
||||||
op,
|
op,
|
||||||
left,
|
left,
|
||||||
c,
|
c,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) statement() (Node, error) {
|
func (p *Parser) statement() (Node, error) {
|
||||||
switch (*p.curr).Type {
|
switch (*p.curr).Type {
|
||||||
case TokenIf:
|
case TokenIf:
|
||||||
|
start := p.curr.Start
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
condition, err := p.condition()
|
condition, err := p.condition()
|
||||||
|
|
@ -470,20 +620,25 @@ func (p *Parser) statement() (Node, error) {
|
||||||
condition,
|
condition,
|
||||||
then,
|
then,
|
||||||
otherwise,
|
otherwise,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenName:
|
case TokenName:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
name := (*p.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
|
|
||||||
if (*p.curr).Type == TokenDot {
|
if (*p.curr).Type == TokenDot {
|
||||||
var v Node = &ReferenceNode{
|
var v Node = &ReferenceNode{
|
||||||
name,
|
name,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse chains of prop-getting ( "".split().join().length.round() )
|
// parse chains of prop-getting ( "".split().join().length.round() )
|
||||||
for p.accept(TokenDot) {
|
for p.accept(TokenDot) {
|
||||||
if err := p.expect(TokenName); err != nil {
|
if err := p.expect(TokenName, "property must be name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
property := (*p.prev).Lexeme
|
property := (*p.prev).Lexeme
|
||||||
|
|
@ -491,6 +646,8 @@ func (p *Parser) statement() (Node, error) {
|
||||||
v = &AccessNode{
|
v = &AccessNode{
|
||||||
v,
|
v,
|
||||||
property,
|
property,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if called, also add
|
// if called, also add
|
||||||
|
|
@ -504,6 +661,8 @@ func (p *Parser) statement() (Node, error) {
|
||||||
v,
|
v,
|
||||||
args,
|
args,
|
||||||
(*p.curr).Type == TokenDot, // if the chain is continued, keep the value.
|
(*p.curr).Type == TokenDot, // if the chain is continued, keep the value.
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -518,9 +677,13 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return &CallNode{
|
return &CallNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
name,
|
name,
|
||||||
|
start,
|
||||||
|
start + Pos(len(name)),
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
false,
|
false,
|
||||||
|
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
|
||||||
|
|
@ -533,28 +696,19 @@ func (p *Parser) statement() (Node, error) {
|
||||||
name,
|
name,
|
||||||
c,
|
c,
|
||||||
isDeclaration,
|
isDeclaration,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
|
||||||
return p.condition()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case TokenImport:
|
return nil, p.error("invalid statement", p.curr)
|
||||||
p.advance()
|
|
||||||
|
|
||||||
if err := p.expect(TokenString); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
path := p.prev.Lexeme[1 : len(p.prev.Lexeme)-1]
|
|
||||||
|
|
||||||
return &ImportNode{
|
|
||||||
path,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TokenFunc:
|
case TokenFunc:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
if err := p.expect(TokenName); err != nil {
|
funcStart := p.prev.Start
|
||||||
|
|
||||||
|
if err := p.expect(TokenName, "function must have a name"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := p.prev.Lexeme
|
name := p.prev.Lexeme
|
||||||
|
|
@ -564,6 +718,14 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var yield TypeSignature = &NilSignature{}
|
||||||
|
if p.curr.Type != TokenOpenBrace {
|
||||||
|
yield, err = p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b, err := p.block(false)
|
b, err := p.block(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -574,13 +736,19 @@ func (p *Parser) statement() (Node, error) {
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
|
yield,
|
||||||
b,
|
b,
|
||||||
|
funcStart,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
funcStart,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenWhile:
|
case TokenWhile:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
c, err := p.condition()
|
c, err := p.condition()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -595,10 +763,13 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return &LoopNode{
|
return &LoopNode{
|
||||||
c,
|
c,
|
||||||
b,
|
b,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenReturn:
|
case TokenReturn:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
c, err := p.condition()
|
c, err := p.condition()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -607,6 +778,8 @@ func (p *Parser) statement() (Node, error) {
|
||||||
|
|
||||||
return &ReturnNode{
|
return &ReturnNode{
|
||||||
c,
|
c,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenBreakpoint:
|
case TokenBreakpoint:
|
||||||
|
|
@ -614,24 +787,33 @@ func (p *Parser) statement() (Node, error) {
|
||||||
|
|
||||||
return &BreakpointNode{}, nil
|
return &BreakpointNode{}, nil
|
||||||
|
|
||||||
|
case TokenImport:
|
||||||
|
defer p.advance()
|
||||||
|
return nil, p.error("import statements must be top-level", p.curr)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err := p.error("invalid statement", p.curr)
|
defer p.advance()
|
||||||
p.advance()
|
return nil, p.error("invalid statement", p.curr)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) block(canBeStatement bool) (Node, error) {
|
func (p *Parser) block(canBeStatement bool) (Node, error) {
|
||||||
if canBeStatement {
|
if canBeStatement {
|
||||||
if !p.accept(TokenOpenBrace) {
|
if !p.accept(TokenOpenBrace) {
|
||||||
|
if p.curr.Type == TokenEOF {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
return p.statement()
|
return p.statement()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := p.expect(TokenOpenBrace); err != nil {
|
if err := p.expect(TokenOpenBrace, "a block is required"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start := p.prev.Start
|
||||||
|
|
||||||
statements := make([]Node, 0)
|
statements := make([]Node, 0)
|
||||||
|
|
||||||
for !p.accept(TokenCloseBrace) {
|
for !p.accept(TokenCloseBrace) {
|
||||||
|
|
@ -646,13 +828,15 @@ func (p *Parser) block(canBeStatement bool) (Node, error) {
|
||||||
|
|
||||||
return &BlockNode{
|
return &BlockNode{
|
||||||
statements,
|
statements,
|
||||||
|
start,
|
||||||
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseArgs() ([]Node, error) {
|
func (p *Parser) parseArgs() ([]Node, error) {
|
||||||
args := make([]Node, 0)
|
args := make([]Node, 0)
|
||||||
|
|
||||||
if err := p.expect(TokenOpenParenthesis); err != nil {
|
if err := p.expect(TokenOpenParenthesis, "arguments must be contained in parenthesis"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -663,7 +847,7 @@ func (p *Parser) parseArgs() ([]Node, error) {
|
||||||
}
|
}
|
||||||
args = append(args, c)
|
args = append(args, c)
|
||||||
for !p.accept(TokenCloseParenthesis) {
|
for !p.accept(TokenCloseParenthesis) {
|
||||||
if err := p.expect(TokenComma); err != nil {
|
if err := p.expect(TokenComma, "arguments must be separated by comma"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c, err = p.condition()
|
c, err = p.condition()
|
||||||
|
|
@ -678,30 +862,142 @@ func (p *Parser) parseArgs() ([]Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseParams parse parameters and parentheses
|
// parseParams parse parameters and parentheses
|
||||||
func (p *Parser) parseParams() ([]string, error) {
|
func (p *Parser) parseParams() ([]FunctionParameter, error) {
|
||||||
if err := p.expect(TokenOpenParenthesis); err != nil {
|
if err := p.expect(TokenOpenParenthesis, "parameters must be in parentheses"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
params := make([]string, 0)
|
params := make([]FunctionParameter, 0)
|
||||||
|
|
||||||
if p.accept(TokenName) {
|
if p.accept(TokenName) {
|
||||||
name := (*p.prev).Lexeme
|
name := (*p.prev).Lexeme
|
||||||
params = append(params, name)
|
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||||
for !p.accept(TokenCloseParenthesis) {
|
|
||||||
if err := p.expect(TokenComma); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := p.expect(TokenName); err != nil {
|
|
||||||
|
t, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params = append(params, FunctionParameter{
|
||||||
|
name,
|
||||||
|
t,
|
||||||
|
})
|
||||||
|
for !p.accept(TokenCloseParenthesis) {
|
||||||
|
if err := p.expect(TokenComma, "parameters must be separated by comma"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := p.expect(TokenName, "parameters must have a name (cannot have trailing comma)"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name = (*p.prev).Lexeme
|
name = (*p.prev).Lexeme
|
||||||
params = append(params, name)
|
if err := p.expect(TokenColon, "parameters must have a type separated by a colon"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params = append(params, FunctionParameter{
|
||||||
|
name,
|
||||||
|
t,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
if err := p.expect(TokenCloseParenthesis, "must close parameter list"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return params, nil
|
return params, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
|
var s TypeSignature
|
||||||
|
|
||||||
|
if p.accept(TokenFunc) {
|
||||||
|
if err := p.expect(TokenOpenParenthesis, "func signature must have parentheses for parameters"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var in []TypeSignature
|
||||||
|
|
||||||
|
for !p.accept(TokenCloseParenthesis) {
|
||||||
|
if len(in) > 0 {
|
||||||
|
if err := p.expect(TokenComma, "parameter types must be separated by a comma"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := p.parseSignature()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
in = append(in, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s = &FunctionSignature{
|
||||||
|
in,
|
||||||
|
out,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := p.expect(TokenName, "type must be a name"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
name := (*p.prev).Lexeme
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case "string":
|
||||||
|
s = &StringSignature{}
|
||||||
|
case "number":
|
||||||
|
s = &NumberSignature{}
|
||||||
|
case "boolean":
|
||||||
|
s = &BooleanSignature{}
|
||||||
|
case "list":
|
||||||
|
if err := p.expect(TokenOpenBracket, "list must have content typed"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.expect(TokenCloseBracket, "list must close parameter list"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s = &ListSignature{
|
||||||
|
contents,
|
||||||
|
}
|
||||||
|
|
||||||
|
case "any":
|
||||||
|
s = &AnySignature{}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, p.error("unsupported type: "+name, p.prev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.accept(TokenPipe) {
|
||||||
|
other, err := p.parseSignature()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CompositeSignature{
|
||||||
|
s,
|
||||||
|
other,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewParser(t *testing.T) {
|
func TestNewParser(t *testing.T) {
|
||||||
tokens := make([]Token, 0)
|
tokens := make([]Token, 0)
|
||||||
|
|
||||||
p := NewParser(tokens)
|
p := NewParser("", tokens)
|
||||||
|
|
||||||
if p == nil {
|
if p == nil {
|
||||||
t.Fatal("parser should not be nil")
|
t.Fatal("parser should not be nil")
|
||||||
|
|
@ -32,7 +34,7 @@ func TestNewParser(t *testing.T) {
|
||||||
func BenchmarkNewParser(b *testing.B) {
|
func BenchmarkNewParser(b *testing.B) {
|
||||||
tokens := make([]Token, 0)
|
tokens := make([]Token, 0)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = NewParser(tokens)
|
_ = NewParser("", tokens)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,14 +68,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
2,
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"assignment": {
|
"assignment": {
|
||||||
|
|
@ -90,10 +97,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&StringNode{
|
&StringNode{
|
||||||
"Hello world!",
|
"Hello world!",
|
||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"declaration": {
|
"declaration": {
|
||||||
|
|
@ -113,14 +123,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"b",
|
"b",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2
|
// (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2
|
||||||
|
|
@ -167,30 +182,63 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryMultiplication,
|
BinaryMultiplication,
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&NumberNode{2},
|
&NumberNode{
|
||||||
&NumberNode{1},
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{5},
|
&NumberNode{
|
||||||
|
1,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
&NumberNode{
|
||||||
|
5,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryDivision,
|
BinaryDivision,
|
||||||
&NumberNode{3},
|
&NumberNode{
|
||||||
|
3,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinarySubtraction,
|
BinarySubtraction,
|
||||||
&NumberNode{6},
|
&NumberNode{
|
||||||
&NumberNode{2},
|
6,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BinaryNode{
|
&BinaryNode{
|
||||||
BinaryDivision,
|
BinaryDivision,
|
||||||
&NumberNode{10},
|
&NumberNode{
|
||||||
&NumberNode{2},
|
10,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"condition_equal": {
|
"condition_equal": {
|
||||||
|
|
@ -210,14 +258,19 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryEquality,
|
BinaryEquality,
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
20,
|
20,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
15,
|
15,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"if_statement": {
|
"if_statement": {
|
||||||
|
|
@ -240,10 +293,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryEquality,
|
BinaryEquality,
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
do: &BlockNode{
|
do: &BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -251,13 +307,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
0, 0,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"if_else_statement": {
|
"if_else_statement": {
|
||||||
|
|
@ -286,10 +346,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryEquality,
|
BinaryEquality,
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
do: &BlockNode{
|
do: &BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -297,10 +360,13 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
1,
|
1,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
otherwise: &BlockNode{
|
otherwise: &BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
|
|
@ -308,13 +374,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"b",
|
"b",
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
0,
|
0,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
0, 0,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"empty_block": {
|
"empty_block": {
|
||||||
|
|
@ -327,8 +397,10 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
[]Node{
|
[]Node{
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{},
|
[]Node{},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"lambda": { // a := func(a, b) { return a + b }
|
"lambda": { // a := func(a, b) { return a + b }
|
||||||
|
|
@ -338,9 +410,14 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
NewToken(TokenFunc, 3, 4, 0, "func"),
|
NewToken(TokenFunc, 3, 4, 0, "func"),
|
||||||
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, 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(TokenName, 10, 5, 0, "number"),
|
||||||
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
|
NewToken(TokenCloseParenthesis, 11, 1, 0, ")"),
|
||||||
|
NewToken(TokenName, 10, 5, 0, "number"),
|
||||||
|
|
||||||
NewToken(TokenOpenBrace, 12, 1, 1, "{"),
|
NewToken(TokenOpenBrace, 12, 1, 1, "{"),
|
||||||
NewToken(TokenReturn, 13, 6, 1, "return"),
|
NewToken(TokenReturn, 13, 6, 1, "return"),
|
||||||
|
|
@ -357,7 +434,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"a",
|
"a",
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
"*",
|
"*",
|
||||||
[]string{"a", "b"},
|
[]FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&NumberSignature{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&ReturnNode{
|
&ReturnNode{
|
||||||
|
|
@ -365,18 +452,26 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"b",
|
"b",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
0, 0,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"function_declaration": {
|
"function_declaration": {
|
||||||
|
|
@ -404,7 +499,17 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
"a",
|
"a",
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
"a",
|
"a",
|
||||||
[]string{"a", "b"},
|
[]FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&NumberSignature{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&ReturnNode{
|
&ReturnNode{
|
||||||
|
|
@ -412,18 +517,26 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
BinaryAddition,
|
BinaryAddition,
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"b",
|
"b",
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
0, 0,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"prop_getting": {
|
"prop_getting": {
|
||||||
|
|
@ -443,12 +556,16 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
&AccessNode{
|
&AccessNode{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
"b",
|
"b",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"list_init": {
|
"list_init": {
|
||||||
|
|
@ -488,27 +605,43 @@ func GetTokenTestData() map[string]TokenTestData {
|
||||||
[]Node{
|
[]Node{
|
||||||
&ReferenceNode{
|
&ReferenceNode{
|
||||||
"a",
|
"a",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&NumberNode{
|
&NumberNode{
|
||||||
3.141,
|
3.141,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&StringNode{
|
&StringNode{
|
||||||
"Hello world!",
|
"Hello world!",
|
||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&BooleanNode{
|
&BooleanNode{
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
&ListNode{
|
&ListNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&NumberNode{2}, &NumberNode{3},
|
&NumberNode{
|
||||||
|
2,
|
||||||
|
0, 0,
|
||||||
|
}, &NumberNode{
|
||||||
|
3,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
|
0, 0,
|
||||||
|
},
|
||||||
true,
|
true,
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -642,15 +775,17 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
||||||
t.Logf("Function node names match (%s)", n.name)
|
t.Logf("Function node names match (%s)", n.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(n.params) != len(m.params) {
|
if len(n.parameters) != len(m.parameters) {
|
||||||
t.Fatalf("Function node parameters count does not match (%d and %d)", len(n.params), len(m.params))
|
t.Fatalf("Function node parameters count does not match (%d and %d)", len(n.parameters), len(m.parameters))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Function node parameters count is equal (%d) ", len(n.params))
|
t.Logf("Function node parameters count is equal (%d) ", len(n.parameters))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, p := range m.params {
|
for i, p := range m.parameters {
|
||||||
if n.params[i] != p {
|
if !n.parameters[i].Signature.Matches(p.Signature) {
|
||||||
t.Errorf("Function node parameter %d does not match: %s and %s", i, p, m.params)
|
t.Errorf("Function node parameter signature %d does not match: %s and %s", i, p.Signature, n.parameters[i].Signature)
|
||||||
|
} else if n.parameters[i].Name != p.Name {
|
||||||
|
t.Errorf("Function node parameter name %d does not match: %s and %s", i, p.Name, n.parameters[i].Name)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Function node parameter %d matches (%s)", i, p)
|
t.Logf("Function node parameter %d matches (%s)", i, p)
|
||||||
}
|
}
|
||||||
|
|
@ -665,6 +800,105 @@ func NodeEquality(t *testing.T, n1 Node, n2 Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SerializeTokens(tokens []Token) string {
|
||||||
|
out := strings.Builder{}
|
||||||
|
level := 0
|
||||||
|
|
||||||
|
for _, token := range tokens {
|
||||||
|
switch token.Type {
|
||||||
|
case TokenPlus:
|
||||||
|
out.WriteString(" + ")
|
||||||
|
case TokenMinus:
|
||||||
|
out.WriteString(" - ")
|
||||||
|
case TokenStar:
|
||||||
|
out.WriteString("*")
|
||||||
|
case TokenSlash:
|
||||||
|
out.WriteString("/")
|
||||||
|
case TokenBang:
|
||||||
|
out.WriteString("!")
|
||||||
|
case TokenSemicolon:
|
||||||
|
out.WriteString(";")
|
||||||
|
case TokenNumber:
|
||||||
|
out.WriteString(token.Lexeme)
|
||||||
|
case TokenString:
|
||||||
|
out.WriteString(fmt.Sprintf("\"%s\"", token.Lexeme))
|
||||||
|
case TokenName:
|
||||||
|
out.WriteString(token.Lexeme)
|
||||||
|
case TokenOpenParenthesis:
|
||||||
|
out.WriteString("(")
|
||||||
|
case TokenCloseParenthesis:
|
||||||
|
out.WriteString(")")
|
||||||
|
case TokenOpenBracket:
|
||||||
|
out.WriteString("[")
|
||||||
|
case TokenCloseBracket:
|
||||||
|
out.WriteString("]")
|
||||||
|
case TokenOpenBrace:
|
||||||
|
out.WriteString("{")
|
||||||
|
level = level + 1
|
||||||
|
case TokenCloseBrace:
|
||||||
|
out.WriteString("}")
|
||||||
|
level = level - 1
|
||||||
|
case TokenTrue:
|
||||||
|
out.WriteString("true")
|
||||||
|
case TokenFalse:
|
||||||
|
out.WriteString("false")
|
||||||
|
case TokenNil:
|
||||||
|
out.WriteString("nil")
|
||||||
|
case TokenFunc:
|
||||||
|
out.WriteString("func")
|
||||||
|
case TokenReturn:
|
||||||
|
out.WriteString("return ")
|
||||||
|
case TokenWhile:
|
||||||
|
out.WriteString("while ")
|
||||||
|
case TokenVar:
|
||||||
|
out.WriteString("var ")
|
||||||
|
case TokenIf:
|
||||||
|
out.WriteString("if ")
|
||||||
|
case TokenElse:
|
||||||
|
out.WriteString(" else ")
|
||||||
|
case TokenImport:
|
||||||
|
out.WriteString("import ")
|
||||||
|
case TokenComma:
|
||||||
|
out.WriteString(", ")
|
||||||
|
case TokenDot:
|
||||||
|
out.WriteString(".")
|
||||||
|
case TokenColon:
|
||||||
|
out.WriteString(": ")
|
||||||
|
case TokenAssign:
|
||||||
|
out.WriteString(" = ")
|
||||||
|
case TokenDeclare:
|
||||||
|
out.WriteString(" := ")
|
||||||
|
case TokenBangEquals:
|
||||||
|
out.WriteString(" != ")
|
||||||
|
case TokenEquals:
|
||||||
|
out.WriteString(" == ")
|
||||||
|
case TokenGreaterThan:
|
||||||
|
out.WriteString(" > ")
|
||||||
|
case TokenLessThan:
|
||||||
|
out.WriteString(" < ")
|
||||||
|
case TokenGreaterThanOrEqual:
|
||||||
|
out.WriteString(" >= ")
|
||||||
|
case TokenLessThanOrEqual:
|
||||||
|
out.WriteString(" <= ")
|
||||||
|
case TokenDoubleAmpersand:
|
||||||
|
out.WriteString(" && ")
|
||||||
|
case TokenDoublePipe:
|
||||||
|
out.WriteString(" || ")
|
||||||
|
case TokenBreakpoint:
|
||||||
|
out.WriteString("breakpoint")
|
||||||
|
case TokenEOF:
|
||||||
|
out.WriteString(fmt.Sprintf("<error: \"%s\">", token.Lexeme))
|
||||||
|
case TokenHexadecimal:
|
||||||
|
out.WriteString(token.Lexeme)
|
||||||
|
case TokenPipe:
|
||||||
|
out.WriteString(" | ")
|
||||||
|
case TokenError:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|
||||||
func TestParser_Parse(t *testing.T) {
|
func TestParser_Parse(t *testing.T) {
|
||||||
t.Logf("Getting test data")
|
t.Logf("Getting test data")
|
||||||
tokenData := GetTokenTestData()
|
tokenData := GetTokenTestData()
|
||||||
|
|
@ -676,17 +910,17 @@ func TestParser_Parse(t *testing.T) {
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
t.Logf("Initializing parser")
|
t.Logf("Initializing parser")
|
||||||
p := NewParser(data.tokens)
|
p := NewParser("", data.tokens)
|
||||||
|
|
||||||
t.Logf("Parsing main")
|
t.Logf("Parsing main")
|
||||||
tree, err := p.Parse()
|
tree, err := p.Parse("")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error(s): %s", err.(*ParsingError).Format([]rune{}))
|
t.Fatalf("Unexpected error(s): %s", err.(ParsingError).Format())
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Checking parsed tree")
|
t.Logf("Checking parsed tree")
|
||||||
NodeEquality(t, tree, data.tree)
|
NodeEquality(t, tree.Block, data.tree)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -697,9 +931,9 @@ func BenchmarkParser_Parse(b *testing.B) {
|
||||||
for name, data := range tokenData {
|
for name, data := range tokenData {
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
p := NewParser(data.tokens)
|
p := NewParser("", data.tokens)
|
||||||
|
|
||||||
_, _ = p.Parse()
|
_, _ = p.Parse("")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,27 @@ package core
|
||||||
|
|
||||||
type Stack[T any] struct {
|
type Stack[T any] struct {
|
||||||
Current Pos
|
Current Pos
|
||||||
Size Pos
|
Capacity Pos
|
||||||
|
|
||||||
items []T
|
items []T
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStack[T any](size Pos) *Stack[T] {
|
func NewStack[T any](capacity Pos) *Stack[T] {
|
||||||
return &Stack[T]{
|
return &Stack[T]{
|
||||||
items: make([]T, size),
|
items: make([]T, 16),
|
||||||
Size: size,
|
Capacity: capacity,
|
||||||
Current: 0,
|
Current: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack[T]) Push(items ...T) {
|
func (s *Stack[T]) Push(items ...T) {
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if s.Current >= s.Size {
|
if s.Current >= s.Capacity {
|
||||||
panic("stack overflow")
|
panic("stack overflow")
|
||||||
}
|
}
|
||||||
|
if int(s.Current) == len(s.items) {
|
||||||
|
s.items = append(s.items, item)
|
||||||
|
}
|
||||||
|
|
||||||
s.items[s.Current] = item
|
s.items[s.Current] = item
|
||||||
s.Current++
|
s.Current++
|
||||||
|
|
@ -45,7 +48,7 @@ func (s *Stack[T]) Peek() T {
|
||||||
|
|
||||||
// check whether the stack is invalid (stack over-/underflow)
|
// check whether the stack is invalid (stack over-/underflow)
|
||||||
func (s *Stack[T]) check() {
|
func (s *Stack[T]) check() {
|
||||||
if s.Current >= s.Size {
|
if s.Current >= s.Capacity {
|
||||||
panic("stack underflow")
|
panic("stack underflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,10 @@ func TestNewStack(t *testing.T) {
|
||||||
|
|
||||||
s := NewStack[any](Pos(size))
|
s := NewStack[any](Pos(size))
|
||||||
|
|
||||||
if s.Size != Pos(size) {
|
if s.Capacity != Pos(size) {
|
||||||
t.Errorf("Stack size (%d) does not match expected size (%d)", s.Size, size)
|
t.Errorf("Stack size (%d) does not match expected size (%d)", s.Capacity, size)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Stack size is expected size (%d)", s.Size)
|
t.Logf("Stack size is expected size (%d)", s.Capacity)
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.items) != size {
|
|
||||||
t.Errorf("internal items slice size (%d) does not match expected size (%d)", len(s.items), size)
|
|
||||||
} else {
|
|
||||||
t.Logf("internal items slice size is as expected (%d)", len(s.items))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Current != 0 {
|
if s.Current != 0 {
|
||||||
|
|
|
||||||
349
core/types.go
Normal file
349
core/types.go
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeString Type = iota
|
||||||
|
TypeNumber
|
||||||
|
TypeBoolean
|
||||||
|
TypeNil
|
||||||
|
TypeList
|
||||||
|
TypeObject
|
||||||
|
TypeFunction
|
||||||
|
TypeAny
|
||||||
|
TypeComposite
|
||||||
|
TypeInner
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) String() string {
|
||||||
|
switch t {
|
||||||
|
case TypeString:
|
||||||
|
return "string"
|
||||||
|
case TypeNumber:
|
||||||
|
return "number"
|
||||||
|
case TypeBoolean:
|
||||||
|
return "boolean"
|
||||||
|
case TypeNil:
|
||||||
|
return "nil"
|
||||||
|
case TypeList:
|
||||||
|
return "list"
|
||||||
|
case TypeObject:
|
||||||
|
return "object"
|
||||||
|
case TypeFunction:
|
||||||
|
return "func"
|
||||||
|
case TypeAny:
|
||||||
|
return "any"
|
||||||
|
case TypeComposite:
|
||||||
|
return "composite"
|
||||||
|
case TypeInner:
|
||||||
|
return "inner"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unsupported string conversion for type %v", int(t)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignatureOf(v Value) TypeSignature {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case *StringValue:
|
||||||
|
return &StringSignature{}
|
||||||
|
case *NumberValue:
|
||||||
|
return &NumberSignature{}
|
||||||
|
case *BoolValue:
|
||||||
|
return &BooleanSignature{}
|
||||||
|
case *ListValue:
|
||||||
|
// try to deduce contents type
|
||||||
|
var contains TypeSignature
|
||||||
|
|
||||||
|
for _, p := range t.Items {
|
||||||
|
sig := SignatureOf(p)
|
||||||
|
if contains == nil {
|
||||||
|
contains = sig
|
||||||
|
} else if !contains.Matches(sig) {
|
||||||
|
contains = &AnySignature{}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ListSignature{
|
||||||
|
contains,
|
||||||
|
}
|
||||||
|
case *ObjectValue:
|
||||||
|
return &ObjectSignature{}
|
||||||
|
case *FunctionValue:
|
||||||
|
params := make([]TypeSignature, len(t.Params))
|
||||||
|
|
||||||
|
for i, p := range t.Params {
|
||||||
|
params[i] = p.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FunctionSignature{
|
||||||
|
params,
|
||||||
|
t.Yield,
|
||||||
|
}
|
||||||
|
case *BuiltinFunctionValue:
|
||||||
|
return t.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unknown value; cannot get signature of %s", v))
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeSignature interface {
|
||||||
|
Type() Type
|
||||||
|
|
||||||
|
// Matches check if this type signature matches another.
|
||||||
|
Matches(TypeSignature) bool
|
||||||
|
|
||||||
|
// String create a human-readable string version of the value type.
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NilSignature struct{}
|
||||||
|
|
||||||
|
func (*NilSignature) Type() Type {
|
||||||
|
return TypeNil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NilSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Type() == TypeAny || other.Type() == TypeNil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*NilSignature) String() string {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringSignature struct{}
|
||||||
|
|
||||||
|
func (*StringSignature) Type() Type {
|
||||||
|
return TypeString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StringSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Type() == TypeAny || other.Type() == TypeString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StringSignature) String() string {
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
type NumberSignature struct{}
|
||||||
|
|
||||||
|
func (*NumberSignature) Type() Type {
|
||||||
|
return TypeNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *NumberSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Type() == TypeAny || other.Type() == TypeNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*NumberSignature) String() string {
|
||||||
|
return "number"
|
||||||
|
}
|
||||||
|
|
||||||
|
type BooleanSignature struct{}
|
||||||
|
|
||||||
|
func (*BooleanSignature) Type() Type {
|
||||||
|
return TypeBoolean
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BooleanSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Type() == TypeAny || other.Type() == TypeBoolean
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*BooleanSignature) String() string {
|
||||||
|
return "boolean"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListSignature struct {
|
||||||
|
Contents TypeSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ListSignature) Type() Type {
|
||||||
|
return TypeList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ListSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.Type() == TypeAny || (other.Type() == TypeList && other.(*ListSignature).Contents.Matches(s.Contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ListSignature) String() string {
|
||||||
|
return fmt.Sprintf("list[%s]", s.Contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjectSignature struct {
|
||||||
|
Members map[string]TypeSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ObjectSignature) Type() Type {
|
||||||
|
return TypeObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ObjectSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Type() == TypeAny {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Type() != TypeObject {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
o := other.(*ObjectSignature)
|
||||||
|
|
||||||
|
if len(o.Members) != len(s.Members) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, member := range s.Members {
|
||||||
|
v, ok := o.Members[name]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v.Matches(member) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ObjectSignature) String() string {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionSignature struct {
|
||||||
|
In []TypeSignature
|
||||||
|
Out TypeSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*FunctionSignature) Type() Type {
|
||||||
|
return TypeFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FunctionSignature) Matches(other TypeSignature) bool {
|
||||||
|
if other.Type() == TypeComposite {
|
||||||
|
return other.Matches(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Type() == TypeAny {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.Type() != TypeFunction {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
f := other.(*FunctionSignature)
|
||||||
|
|
||||||
|
if !s.Out.Matches(f.Out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.In) != len(s.In) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range s.In {
|
||||||
|
v := f.In[i]
|
||||||
|
if !p.Matches(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FunctionSignature) String() string {
|
||||||
|
b := strings.Builder{}
|
||||||
|
|
||||||
|
b.WriteString("func(")
|
||||||
|
|
||||||
|
for i, t := range s.In {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteString(", ")
|
||||||
|
}
|
||||||
|
b.WriteString(t.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(")")
|
||||||
|
if s.Out.Type() != TypeNil {
|
||||||
|
b.WriteString(" ")
|
||||||
|
b.WriteString(s.Out.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnySignature struct{}
|
||||||
|
|
||||||
|
func (*AnySignature) Type() Type {
|
||||||
|
return TypeAny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AnySignature) Matches(_ TypeSignature) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AnySignature) String() string {
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompositeSignature struct {
|
||||||
|
A TypeSignature
|
||||||
|
B TypeSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CompositeSignature) Type() Type {
|
||||||
|
return TypeComposite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompositeSignature) Matches(other TypeSignature) bool {
|
||||||
|
return s.A.Matches(other) || s.B.Matches(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CompositeSignature) String() string {
|
||||||
|
return fmt.Sprintf("%s|%s", s.A, s.B)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InnerSignature struct{}
|
||||||
|
|
||||||
|
func (*InnerSignature) Type() Type {
|
||||||
|
return TypeInner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*InnerSignature) Matches(_ TypeSignature) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*InnerSignature) String() string {
|
||||||
|
return "inner"
|
||||||
|
}
|
||||||
275
core/values.go
275
core/values.go
|
|
@ -68,15 +68,6 @@ func GoToValue(gov interface{}) Value {
|
||||||
return &StringValue{
|
return &StringValue{
|
||||||
v,
|
v,
|
||||||
}
|
}
|
||||||
case []interface{}:
|
|
||||||
values := make([]Value, len(v))
|
|
||||||
for i, value := range v {
|
|
||||||
values[i] = GoToValue(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ListValue{
|
|
||||||
values,
|
|
||||||
}
|
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
values := map[string]Value{}
|
values := map[string]Value{}
|
||||||
for key, value := range v {
|
for key, value := range v {
|
||||||
|
|
@ -86,9 +77,17 @@ func GoToValue(gov interface{}) Value {
|
||||||
return &ObjectValue{
|
return &ObjectValue{
|
||||||
values,
|
values,
|
||||||
}
|
}
|
||||||
|
case Value:
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
if reflect.TypeOf(v).Kind() == reflect.Slice {
|
||||||
|
return &ListValue{
|
||||||
|
v.([]Value),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(fmt.Sprintf("unsupported automatic type conversion: %v (%s)", gov, reflect.TypeOf(gov).Name()))
|
panic(fmt.Sprintf("unsupported automatic type conversion: %v (%s)", gov, reflect.TypeOf(gov)))
|
||||||
}
|
}
|
||||||
|
|
||||||
type Value interface {
|
type Value interface {
|
||||||
|
|
@ -106,6 +105,9 @@ type Value interface {
|
||||||
|
|
||||||
// Get a member from the value. An error is returned if the member does not exist
|
// Get a member from the value. An error is returned if the member does not exist
|
||||||
Get(string) (Value, error)
|
Get(string) (Value, error)
|
||||||
|
|
||||||
|
// Clone create a clone of the value. The returned value is a pointer to a new value of the same type as the value.
|
||||||
|
Clone() Value
|
||||||
}
|
}
|
||||||
|
|
||||||
type NilValue struct{}
|
type NilValue struct{}
|
||||||
|
|
@ -130,8 +132,12 @@ func (v *NilValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("nil has no properties")
|
return nil, errors.New("nil has no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *NilValue) Clone() Value {
|
||||||
|
return &NilValue{}
|
||||||
|
}
|
||||||
|
|
||||||
type BoolValue struct {
|
type BoolValue struct {
|
||||||
bool
|
Boolean bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BoolValue) Type() ValueType {
|
func (v *BoolValue) Type() ValueType {
|
||||||
|
|
@ -139,7 +145,7 @@ func (v *BoolValue) Type() ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BoolValue) String() string {
|
func (v *BoolValue) String() string {
|
||||||
if v.bool {
|
if v.Boolean {
|
||||||
return "true"
|
return "true"
|
||||||
} else {
|
} else {
|
||||||
return "false"
|
return "false"
|
||||||
|
|
@ -151,16 +157,22 @@ func (v *BoolValue) DebugString() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BoolValue) Equals(other Value) bool {
|
func (v *BoolValue) Equals(other Value) bool {
|
||||||
return other.Type() == BoolValueType && other.(*BoolValue).bool == v.bool
|
return other.Type() == BoolValueType && other.(*BoolValue).Boolean == v.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BoolValue) Get(_ string) (Value, error) {
|
func (v *BoolValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("booleans have no properties")
|
return nil, errors.New("booleans have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *BoolValue) Clone() Value {
|
||||||
|
return &BoolValue{
|
||||||
|
v.Boolean,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ObjectValue An object with any number of members (key-value pairs)
|
// ObjectValue An object with any number of members (key-value pairs)
|
||||||
type ObjectValue struct {
|
type ObjectValue struct {
|
||||||
members map[string]Value
|
Members map[string]Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ObjectValue) Type() ValueType {
|
func (v *ObjectValue) Type() ValueType {
|
||||||
|
|
@ -169,12 +181,12 @@ func (v *ObjectValue) Type() ValueType {
|
||||||
|
|
||||||
func (v *ObjectValue) String() string {
|
func (v *ObjectValue) String() string {
|
||||||
out := "{"
|
out := "{"
|
||||||
for key, value := range v.members {
|
for key, value := range v.Members {
|
||||||
if out != "{" {
|
if out != "{" {
|
||||||
out += ", "
|
out += ", "
|
||||||
}
|
}
|
||||||
|
|
||||||
out += fmt.Sprintf("%q=%s", key, value.String())
|
out += fmt.Sprintf("%q=%s", key, value.DebugString())
|
||||||
}
|
}
|
||||||
out += "}"
|
out += "}"
|
||||||
|
|
||||||
|
|
@ -191,8 +203,8 @@ func (v *ObjectValue) Equals(other Value) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range v.members {
|
for key, value := range v.Members {
|
||||||
if !object.members[key].Equals(value) {
|
if !object.Members[key].Equals(value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,26 +215,30 @@ func (v *ObjectValue) Equals(other Value) bool {
|
||||||
var ObjectPrototype = map[string]Value{
|
var ObjectPrototype = map[string]Value{
|
||||||
"set": &BuiltinFunctionValue{
|
"set": &BuiltinFunctionValue{
|
||||||
"set",
|
"set",
|
||||||
[]string{"property", "value"},
|
&FunctionSignature{
|
||||||
func(vm *VM, _this Value, params map[string]Value) (Value, error) {
|
[]TypeSignature{&StringSignature{}, &ListSignature{}},
|
||||||
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, _this Value, params []Value) (Value, error) {
|
||||||
this := _this.(*ObjectValue)
|
this := _this.(*ObjectValue)
|
||||||
|
|
||||||
p := params["property"]
|
p := params[1]
|
||||||
v, ok := params["value"].(*StringValue)
|
v, ok := params[0].(*StringValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("property is not a string")
|
return nil, errors.New("property is not a string")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.members[v.string] = p
|
this.Members[v.Text] = p
|
||||||
|
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ObjectValue) Get(key string) (Value, error) {
|
func (v *ObjectValue) Get(key string) (Value, error) {
|
||||||
if member, ok := v.members[key]; ok {
|
if member, ok := v.Members[key]; ok {
|
||||||
return member, nil
|
return member, nil
|
||||||
} else if p, ok := ObjectPrototype[key]; ok {
|
} else if p, ok := ObjectPrototype[key]; ok {
|
||||||
return p, nil
|
return p, nil
|
||||||
|
|
@ -231,9 +247,21 @@ func (v *ObjectValue) Get(key string) (Value, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *ObjectValue) Clone() Value {
|
||||||
|
m := make(map[string]Value, len(v.Members))
|
||||||
|
|
||||||
|
for name, mem := range v.Members {
|
||||||
|
m[name] = mem.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ObjectValue{
|
||||||
|
m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NumberValue Integer or floating-point values
|
// NumberValue Integer or floating-point values
|
||||||
type NumberValue struct {
|
type NumberValue struct {
|
||||||
float64
|
Number float64
|
||||||
}
|
}
|
||||||
|
|
||||||
const NumberSize int = 64
|
const NumberSize int = 64
|
||||||
|
|
@ -243,7 +271,7 @@ func (v *NumberValue) Type() ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NumberValue) String() string {
|
func (v *NumberValue) String() string {
|
||||||
return strconv.FormatFloat(v.float64, 'g', -1, NumberSize)
|
return strconv.FormatFloat(v.Number, 'g', -1, NumberSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NumberValue) DebugString() string {
|
func (v *NumberValue) DebugString() string {
|
||||||
|
|
@ -251,7 +279,7 @@ func (v *NumberValue) DebugString() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NumberValue) Equals(other Value) bool {
|
func (v *NumberValue) Equals(other Value) bool {
|
||||||
return other.Type() == NumberValueType && other.(*NumberValue).float64 == v.float64
|
return other.Type() == NumberValueType && other.(*NumberValue).Number == v.Number
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NumberValue) Get(_ string) (Value, error) {
|
func (v *NumberValue) Get(_ string) (Value, error) {
|
||||||
|
|
@ -259,8 +287,14 @@ func (v *NumberValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("numbers have no properties")
|
return nil, errors.New("numbers have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *NumberValue) Clone() Value {
|
||||||
|
return &NumberValue{
|
||||||
|
v.Number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type StringValue struct {
|
type StringValue struct {
|
||||||
string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *StringValue) Type() ValueType {
|
func (v *StringValue) Type() ValueType {
|
||||||
|
|
@ -268,7 +302,7 @@ func (v *StringValue) Type() ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *StringValue) String() string {
|
func (v *StringValue) String() string {
|
||||||
return v.string
|
return v.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *StringValue) DebugString() string {
|
func (v *StringValue) DebugString() string {
|
||||||
|
|
@ -276,31 +310,47 @@ func (v *StringValue) DebugString() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *StringValue) Equals(other Value) bool {
|
func (v *StringValue) Equals(other Value) bool {
|
||||||
return other.Type() == StringValueType && other.(*StringValue).string == v.string
|
return other.Type() == StringValueType && other.(*StringValue).Text == v.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
var StringPrototype = map[string]*BuiltinFunctionValue{
|
var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||||
"split": {
|
"split": {
|
||||||
"split",
|
"split",
|
||||||
[]string{"seperator"},
|
&FunctionSignature{
|
||||||
func(vm *VM, this Value, m map[string]Value) (Value, error) {
|
[]TypeSignature{&StringSignature{}},
|
||||||
|
&ListSignature{
|
||||||
|
&StringSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, v []Value) (Value, error) {
|
||||||
str := this.(*StringValue).String()
|
str := this.(*StringValue).String()
|
||||||
sep := m["seperator"].(*StringValue).String()
|
sep := v[0].(*StringValue).String()
|
||||||
|
|
||||||
var out []string
|
var out []Value
|
||||||
tmp := strings.Builder{}
|
tmp := strings.Builder{}
|
||||||
for i := 0; i < len(str)-len(sep); i++ {
|
for i := 0; i < len(str)-len(sep); i++ {
|
||||||
tmp.WriteRune([]rune(str)[i])
|
tmp.WriteRune([]rune(str)[i])
|
||||||
|
|
||||||
if str[i:i+len(sep)] == sep {
|
if str[i:i+len(sep)] == sep {
|
||||||
out = append(out, tmp.String())
|
out = append(out, &StringValue{tmp.String()})
|
||||||
tmp.Reset()
|
tmp.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GoToValue(out), nil
|
return &ListValue{out}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"length": {
|
||||||
|
Name: "length",
|
||||||
|
Signature: &FunctionSignature{
|
||||||
|
[]TypeSignature{},
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
F: func(vm *VM, this Value, _ []Value) (Value, error) {
|
||||||
|
return GoToValue(len(this.(*StringValue).Text)), nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,9 +362,15 @@ func (v *StringValue) Get(key string) (Value, error) {
|
||||||
return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key))
|
return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *StringValue) Clone() Value {
|
||||||
|
return &StringValue{
|
||||||
|
v.Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ListValue a dynamic list of values
|
// ListValue a dynamic list of values
|
||||||
type ListValue struct {
|
type ListValue struct {
|
||||||
items []Value
|
Items []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ListValue) Type() ValueType {
|
func (v *ListValue) Type() ValueType {
|
||||||
|
|
@ -323,7 +379,7 @@ func (v *ListValue) Type() ValueType {
|
||||||
|
|
||||||
func (v *ListValue) String() string {
|
func (v *ListValue) String() string {
|
||||||
out := "["
|
out := "["
|
||||||
for i, item := range v.items {
|
for i, item := range v.Items {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
out += ", "
|
out += ", "
|
||||||
}
|
}
|
||||||
|
|
@ -345,12 +401,12 @@ func (v *ListValue) Equals(other Value) bool {
|
||||||
|
|
||||||
l := other.(*ListValue)
|
l := other.(*ListValue)
|
||||||
|
|
||||||
if len(v.items) != len(l.items) {
|
if len(v.Items) != len(l.Items) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, item := range l.items {
|
for i, item := range v.Items {
|
||||||
if !item.Equals(l.items[i]) {
|
if !item.Equals(l.Items[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -361,19 +417,28 @@ func (v *ListValue) Equals(other Value) bool {
|
||||||
var ListPrototype = map[string]*BuiltinFunctionValue{
|
var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
"append": {
|
"append": {
|
||||||
"append",
|
"append",
|
||||||
[]string{"item"},
|
&FunctionSignature{
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
[]TypeSignature{&AnySignature{}},
|
||||||
this.(*ListValue).items = append(this.(*ListValue).items, p["item"])
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||||
|
this.(*ListValue).Items = append(this.(*ListValue).Items, v[0])
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"at": {
|
"at": {
|
||||||
"at",
|
"at",
|
||||||
[]string{"index"},
|
&FunctionSignature{
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
[]TypeSignature{
|
||||||
items := this.(*ListValue).items
|
&NumberSignature{},
|
||||||
index := int(p["index"].(*NumberValue).float64)
|
},
|
||||||
|
&InnerSignature{},
|
||||||
|
},
|
||||||
|
func(_ *VM, this Value, p []Value) (Value, error) {
|
||||||
|
items := this.(*ListValue).Items
|
||||||
|
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))
|
||||||
|
|
@ -382,56 +447,41 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return items[index], nil
|
return items[index], nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"length": {
|
"length": {
|
||||||
"length",
|
"length",
|
||||||
[]string{},
|
&FunctionSignature{
|
||||||
func(_ *VM, this Value, p map[string]Value) (Value, error) {
|
[]TypeSignature{},
|
||||||
return GoToValue(len(this.(*ListValue).items)), nil
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
nil,
|
func(_ *VM, this Value, _ []Value) (Value, error) {
|
||||||
},
|
return GoToValue(len(this.(*ListValue).Items)), nil
|
||||||
"map": {
|
|
||||||
"map",
|
|
||||||
[]string{"f"},
|
|
||||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
|
||||||
list := value.(*ListValue)
|
|
||||||
|
|
||||||
v := m["f"]
|
|
||||||
var f Value
|
|
||||||
f, ok := v.(*FunctionValue)
|
|
||||||
if !ok {
|
|
||||||
f, ok = v.(*BuiltinFunctionValue)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New(fmt.Sprintf("not a function to apply: %s", v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, item := range list.items {
|
|
||||||
var err error
|
|
||||||
list.items[i], err = vm.Call(f, []Value{
|
|
||||||
item,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"reduce": {
|
"reduce": {
|
||||||
"reduce",
|
"reduce",
|
||||||
[]string{"f", "start"},
|
&FunctionSignature{
|
||||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
[]TypeSignature{
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{
|
||||||
|
&AnySignature{},
|
||||||
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||||
list := value.(*ListValue)
|
list := value.(*ListValue)
|
||||||
f := m["f"]
|
f := m[0]
|
||||||
sum := m["start"]
|
sum := m[1]
|
||||||
|
|
||||||
for _, v := range list.items {
|
for _, v := range list.Items {
|
||||||
result, err := vm.Call(f, []Value{sum, v})
|
result, err := vm.Call(f, []Value{sum, v})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -442,6 +492,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return sum, nil
|
return sum, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,9 +504,22 @@ func (v *ListValue) Get(key string) (Value, error) {
|
||||||
return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key))
|
return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *ListValue) Clone() Value {
|
||||||
|
n := make([]Value, len(v.Items))
|
||||||
|
|
||||||
|
for i, item := range v.Items {
|
||||||
|
n[i] = item.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ListValue{
|
||||||
|
n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type FunctionValue struct {
|
type FunctionValue struct {
|
||||||
Name string
|
Name string
|
||||||
Params []string
|
Params []FunctionParameter
|
||||||
|
Yield TypeSignature
|
||||||
Chunk *Chunk
|
Chunk *Chunk
|
||||||
Parent Value
|
Parent Value
|
||||||
}
|
}
|
||||||
|
|
@ -482,11 +546,22 @@ func (v *FunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *FunctionValue) Clone() Value {
|
||||||
|
return &FunctionValue{
|
||||||
|
v.Name,
|
||||||
|
v.Params,
|
||||||
|
v.Yield,
|
||||||
|
v.Chunk,
|
||||||
|
v.Parent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type BuiltinFunctionValue struct {
|
type BuiltinFunctionValue struct {
|
||||||
Name string
|
Name string
|
||||||
Parameters []string
|
Signature *FunctionSignature
|
||||||
F func(*VM, Value, map[string]Value) (Value, error)
|
F func(*VM, Value, []Value) (Value, error)
|
||||||
Parent Value
|
Parent Value
|
||||||
|
Constant bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *BuiltinFunctionValue) Type() ValueType {
|
func (v *BuiltinFunctionValue) Type() ValueType {
|
||||||
|
|
@ -510,6 +585,16 @@ func (v *BuiltinFunctionValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("functions have no properties")
|
return nil, errors.New("functions have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *BuiltinFunctionValue) Clone() Value {
|
||||||
|
return &BuiltinFunctionValue{
|
||||||
|
v.Name,
|
||||||
|
v.Signature,
|
||||||
|
v.F,
|
||||||
|
v.Parent,
|
||||||
|
v.Constant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VariableValue a value wrapper for variables kept on the stack
|
// VariableValue a value wrapper for variables kept on the stack
|
||||||
type VariableValue struct {
|
type VariableValue struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -541,3 +626,11 @@ func (v *VariableValue) Equals(other Value) bool {
|
||||||
func (v *VariableValue) Get(_ string) (Value, error) {
|
func (v *VariableValue) Get(_ string) (Value, error) {
|
||||||
return nil, errors.New("variables have no properties")
|
return nil, errors.New("variables have no properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *VariableValue) Clone() Value {
|
||||||
|
return &VariableValue{
|
||||||
|
v.name,
|
||||||
|
v.value.Clone(),
|
||||||
|
v.scope,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,19 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
||||||
t.Logf("Both are nil")
|
t.Logf("Both are nil")
|
||||||
return
|
return
|
||||||
case BoolValueType:
|
case BoolValueType:
|
||||||
if got.(*BoolValue).bool != want.(*BoolValue).bool {
|
if got.(*BoolValue).Boolean != want.(*BoolValue).Boolean {
|
||||||
t.Errorf("bool value mismatch: got %v, want %v", got.(*BoolValue), want.(*BoolValue))
|
t.Errorf("bool value mismatch: got %v, want %v", got.(*BoolValue), want.(*BoolValue))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Both are same boolean (%s)", want.(*BoolValue).String())
|
t.Logf("Both are same boolean (%s)", want.(*BoolValue).String())
|
||||||
}
|
}
|
||||||
case NumberValueType:
|
case NumberValueType:
|
||||||
if got.(*NumberValue).float64 != want.(*NumberValue).float64 {
|
if got.(*NumberValue).Number != want.(*NumberValue).Number {
|
||||||
t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue))
|
t.Errorf("number value mismatch: got %v, want %v", got.(*NumberValue), want.(*NumberValue))
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Both are same number (%s)", got.(*NumberValue).String())
|
t.Logf("Both are same number (%s)", got.(*NumberValue).String())
|
||||||
}
|
}
|
||||||
case StringValueType:
|
case StringValueType:
|
||||||
if got.(*StringValue).string != want.(*StringValue).string {
|
if got.(*StringValue).Text != want.(*StringValue).Text {
|
||||||
t.Errorf("string value mismatch: got %v, want %v", got.(*StringValue), want.(*StringValue))
|
t.Errorf("string value mismatch: got %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())
|
||||||
|
|
@ -60,19 +60,10 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
||||||
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
|
t.Errorf("builtin function name mismatch: got %v, want %v", n.Name, m.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(n.Parameters) != len(m.Parameters) {
|
if !n.Signature.Matches(m.Signature) {
|
||||||
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n.Parameters, m.Parameters)
|
t.Errorf("builtin function parameter count mismatch: got %v, want %v", n, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, v := range n.Parameters {
|
|
||||||
if v != m.Parameters[i] {
|
|
||||||
t.Errorf("builtin function parameter %d mismatch: got %v, want %v", i, v, m.Parameters[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if &n.F != &m.F {
|
|
||||||
t.Errorf("builtin function f mismatch: got %v, want %v", &n.F, &m.F)
|
|
||||||
}
|
|
||||||
case VariableValueType:
|
case VariableValueType:
|
||||||
n := got.(*VariableValue)
|
n := got.(*VariableValue)
|
||||||
m := want.(*VariableValue)
|
m := want.(*VariableValue)
|
||||||
|
|
@ -87,6 +78,32 @@ func CompareValues(t *testing.T, got Value, want Value) {
|
||||||
|
|
||||||
CompareValues(t, n.value, m.value)
|
CompareValues(t, n.value, m.value)
|
||||||
|
|
||||||
|
case ListValueType:
|
||||||
|
n := got.(*ListValue)
|
||||||
|
m := want.(*ListValue)
|
||||||
|
|
||||||
|
if len(n.Items) != len(m.Items) {
|
||||||
|
t.Fatalf("list items length mismatch: got %d, want %d", len(n.Items), len(m.Items))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range n.Items {
|
||||||
|
t.Logf("comparing list items #%d: got %s, want %s", i, v, m.Items[i])
|
||||||
|
CompareValues(t, v, m.Items[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
case ObjectValueType:
|
||||||
|
n := got.(*ObjectValue)
|
||||||
|
m := want.(*ObjectValue)
|
||||||
|
|
||||||
|
if len(n.Members) != len(m.Members) {
|
||||||
|
t.Fatalf("object members count mismatch: got %d, want %d", len(n.Members), len(m.Members))
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range n.Members {
|
||||||
|
t.Logf("comparing object member %s: got %s, want %s", k, v, m.Members[k])
|
||||||
|
CompareValues(t, v, m.Members[k])
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unimplemented comparison")
|
panic("unimplemented comparison")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
325
core/vm.go
325
core/vm.go
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -26,6 +28,8 @@ const (
|
||||||
InstructionMul
|
InstructionMul
|
||||||
// InstructionDiv pop two and divide the second by the first
|
// InstructionDiv pop two and divide the second by the first
|
||||||
InstructionDiv
|
InstructionDiv
|
||||||
|
// InstructionNegate negate the value; if it was positive, make it negative, and vice versa.
|
||||||
|
InstructionNegate
|
||||||
// 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
|
||||||
|
|
@ -99,6 +103,8 @@ const (
|
||||||
// items to include minus one. (value of 0 => 1 item, value of 1 => 2 items, etc.) The order is reversed compared
|
// items to include minus one. (value of 0 => 1 item, value of 1 => 2 items, etc.) The order is reversed compared
|
||||||
// to on the stack; the top value on the stack is the last in the list.
|
// to on the stack; the top value on the stack is the last in the list.
|
||||||
InstructionFormList
|
InstructionFormList
|
||||||
|
// InstructionConcatLists concatenate lists, producing a new list with the values of both lists. Pops two lists.
|
||||||
|
InstructionConcatLists
|
||||||
|
|
||||||
// InstructionBreakpoint for debugging purposes
|
// InstructionBreakpoint for debugging purposes
|
||||||
InstructionBreakpoint
|
InstructionBreakpoint
|
||||||
|
|
@ -118,6 +124,8 @@ func (b Bytecode) String() string {
|
||||||
return "MUL"
|
return "MUL"
|
||||||
case InstructionDiv:
|
case InstructionDiv:
|
||||||
return "DIV"
|
return "DIV"
|
||||||
|
case InstructionNegate:
|
||||||
|
return "NEGATE"
|
||||||
case InstructionEquals:
|
case InstructionEquals:
|
||||||
return "EQUALS"
|
return "EQUALS"
|
||||||
case InstructionNotEqual:
|
case InstructionNotEqual:
|
||||||
|
|
@ -182,6 +190,8 @@ func (b Bytecode) String() string {
|
||||||
return "APPEND"
|
return "APPEND"
|
||||||
case InstructionAccessProperty:
|
case InstructionAccessProperty:
|
||||||
return "ACCESS_PROPERTY"
|
return "ACCESS_PROPERTY"
|
||||||
|
case InstructionConcatLists:
|
||||||
|
return "CONCAT_LISTS"
|
||||||
}
|
}
|
||||||
return "UNDEFINED"
|
return "UNDEFINED"
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +212,7 @@ func (c Chunk) String() string {
|
||||||
b.WriteString("=-= constants =-=\n")
|
b.WriteString("=-= constants =-=\n")
|
||||||
|
|
||||||
for i, ct := range c.Constants {
|
for i, ct := range c.Constants {
|
||||||
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct))
|
b.WriteString(fmt.Sprintf("c=%d \t%s\n", i, ct.DebugString()))
|
||||||
|
|
||||||
f, ok := ct.(*FunctionValue)
|
f, ok := ct.(*FunctionValue)
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -228,6 +238,16 @@ func RegisterGOBTypes() {
|
||||||
Params: nil,
|
Params: nil,
|
||||||
Chunk: nil,
|
Chunk: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Signatures
|
||||||
|
gob.Register(&NilSignature{})
|
||||||
|
gob.Register(&NumberSignature{})
|
||||||
|
gob.Register(&StringSignature{})
|
||||||
|
gob.Register(&FunctionSignature{})
|
||||||
|
gob.Register(&ListSignature{})
|
||||||
|
gob.Register(&ObjectSignature{})
|
||||||
|
gob.Register(&BooleanSignature{})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chunk) Serialize() []byte {
|
func (c Chunk) Serialize() []byte {
|
||||||
|
|
@ -287,38 +307,108 @@ type Call struct {
|
||||||
var DefaultGlobals = map[string]Value{
|
var DefaultGlobals = map[string]Value{
|
||||||
"write": &BuiltinFunctionValue{
|
"write": &BuiltinFunctionValue{
|
||||||
"write", // always remember where you come from...
|
"write", // always remember where you come from...
|
||||||
[]string{"value"},
|
&FunctionSignature{
|
||||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
[]TypeSignature{&StringSignature{}},
|
||||||
println(v["value"].String())
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||||
|
println(v[0].String())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"print": &BuiltinFunctionValue{
|
"print": &BuiltinFunctionValue{
|
||||||
"print",
|
"print",
|
||||||
[]string{"value"},
|
&FunctionSignature{
|
||||||
func(_ *VM, this Value, v map[string]Value) (Value, error) {
|
[]TypeSignature{&StringSignature{}},
|
||||||
print(v["value"].String())
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(_ *VM, this Value, v []Value) (Value, error) {
|
||||||
|
print(v[0].String())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"format": &BuiltinFunctionValue{
|
"format": &BuiltinFunctionValue{
|
||||||
"format",
|
"format",
|
||||||
[]string{"format_string", "values"},
|
&FunctionSignature{
|
||||||
func(vm *VM, value Value, m map[string]Value) (Value, error) {
|
[]TypeSignature{
|
||||||
valuies := m["values"].(*ListValue).items
|
&StringSignature{},
|
||||||
|
&ListSignature{
|
||||||
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&StringSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, value Value, m []Value) (Value, error) {
|
||||||
|
b := strings.Builder{}
|
||||||
|
template := m[0].(*StringValue).Text
|
||||||
|
valuies := m[1].(*ListValue).Items
|
||||||
|
|
||||||
return GoToValue(fmt.Sprintf(m["format_string"].String(), valuies)), nil
|
vi := 0
|
||||||
|
last := 0
|
||||||
|
for i := 0; i < len(template); i++ {
|
||||||
|
if template[i] == '%' {
|
||||||
|
b.WriteString(template[last:i])
|
||||||
|
b.WriteString(valuies[vi].String())
|
||||||
|
vi++
|
||||||
|
last = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(template[last:])
|
||||||
|
|
||||||
|
return GoToValue(b.String()), nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"char": &BuiltinFunctionValue{
|
||||||
|
"char",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&NumberSignature{}},
|
||||||
|
&StringSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
n := args[0].(*NumberValue).Number
|
||||||
|
b := byte(n)
|
||||||
|
|
||||||
|
return &StringValue{
|
||||||
|
string([]byte{b}),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"byte": &BuiltinFunctionValue{
|
||||||
|
"char",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&StringSignature{}},
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
s := args[0].(*StringValue).Text
|
||||||
|
b := []byte(s)[0]
|
||||||
|
|
||||||
|
return &NumberValue{float64(b)}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
"assertEq": &BuiltinFunctionValue{
|
"assertEq": &BuiltinFunctionValue{
|
||||||
"assertEq",
|
"assertEq",
|
||||||
[]string{"a", "b"},
|
&FunctionSignature{
|
||||||
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
[]TypeSignature{
|
||||||
a := params["a"]
|
&AnySignature{},
|
||||||
b := params["b"]
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, params []Value) (Value, error) {
|
||||||
|
a := params[0]
|
||||||
|
b := params[1]
|
||||||
|
|
||||||
if !a.Equals(b) {
|
if !a.Equals(b) {
|
||||||
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||||
|
|
@ -327,13 +417,20 @@ var DefaultGlobals = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"assertNotEq": &BuiltinFunctionValue{
|
"assertNotEq": &BuiltinFunctionValue{
|
||||||
"assertNotEq",
|
"assertNotEq",
|
||||||
[]string{"a", "b"},
|
&FunctionSignature{
|
||||||
func(vm *VM, this Value, params map[string]Value) (Value, error) {
|
[]TypeSignature{
|
||||||
a := params["a"]
|
&AnySignature{},
|
||||||
b := params["b"]
|
&AnySignature{},
|
||||||
|
},
|
||||||
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, params []Value) (Value, error) {
|
||||||
|
a := params[0]
|
||||||
|
b := params[1]
|
||||||
|
|
||||||
if a.Equals(b) {
|
if a.Equals(b) {
|
||||||
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
return nil, errors.New(fmt.Sprintf("assertion failed: %s does not equal %s", a, b))
|
||||||
|
|
@ -342,6 +439,85 @@ var DefaultGlobals = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"str": &BuiltinFunctionValue{
|
||||||
|
"str",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&AnySignature{}},
|
||||||
|
&StringSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, _ Value, args []Value) (Value, error) {
|
||||||
|
return GoToValue(args[0].String()), nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"type": &BuiltinFunctionValue{
|
||||||
|
Name: "type",
|
||||||
|
Signature: &FunctionSignature{
|
||||||
|
In: []TypeSignature{&AnySignature{}},
|
||||||
|
Out: &StringSignature{},
|
||||||
|
},
|
||||||
|
F: func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
v := args[0]
|
||||||
|
sig := SignatureOf(v)
|
||||||
|
|
||||||
|
return GoToValue(sig.String()), nil
|
||||||
|
},
|
||||||
|
Constant: true,
|
||||||
|
},
|
||||||
|
"exit": &BuiltinFunctionValue{
|
||||||
|
"exit",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&NumberSignature{}},
|
||||||
|
&NilSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
os.Exit(int(args[0].(*NumberValue).Number))
|
||||||
|
return &NilValue{}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"floor": &BuiltinFunctionValue{
|
||||||
|
"floor",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&NumberSignature{}},
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
return &NumberValue{math.Floor(args[0].(*NumberValue).Number)}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"ceil": &BuiltinFunctionValue{
|
||||||
|
"ceil",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&NumberSignature{}},
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
return &NumberValue{math.Ceil(args[0].(*NumberValue).Number)}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"roundd": &BuiltinFunctionValue{
|
||||||
|
"roundd",
|
||||||
|
&FunctionSignature{
|
||||||
|
[]TypeSignature{&NumberSignature{}, &NumberSignature{}},
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
func(vm *VM, this Value, args []Value) (Value, error) {
|
||||||
|
x := args[0].(*NumberValue).Number
|
||||||
|
decimals := args[1].(*NumberValue).Number
|
||||||
|
multiplier := math.Pow(10, decimals)
|
||||||
|
return &NumberValue{math.Round(x*multiplier) / multiplier}, nil
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -393,29 +569,34 @@ func (vm *VM) Next() bool {
|
||||||
vm.stack.Push(vm.ReadConstant())
|
vm.stack.Push(vm.ReadConstant())
|
||||||
|
|
||||||
case InstructionAdd:
|
case InstructionAdd:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&NumberValue{l + r})
|
vm.stack.Push(&NumberValue{l + r})
|
||||||
|
|
||||||
case InstructionSub:
|
case InstructionSub:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&NumberValue{l - r})
|
vm.stack.Push(&NumberValue{l - r})
|
||||||
|
|
||||||
case InstructionMul:
|
case InstructionMul:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&NumberValue{l * r})
|
vm.stack.Push(&NumberValue{l * r})
|
||||||
|
|
||||||
case InstructionDiv:
|
case InstructionDiv:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&NumberValue{l / r})
|
vm.stack.Push(&NumberValue{l / r})
|
||||||
|
|
||||||
|
case InstructionNegate:
|
||||||
|
v := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
|
vm.stack.Push(&NumberValue{-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())},
|
||||||
|
|
@ -427,40 +608,40 @@ func (vm *VM) Next() bool {
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionNot:
|
case InstructionNot:
|
||||||
b := vm.stack.Pop().(*BoolValue).bool
|
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).bool
|
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
l := vm.stack.Pop().(*BoolValue).bool
|
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).bool
|
r := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
l := vm.stack.Pop().(*BoolValue).bool
|
l := vm.stack.Pop().(*BoolValue).Boolean
|
||||||
vm.stack.Push(&BoolValue{l || r})
|
vm.stack.Push(&BoolValue{l || r})
|
||||||
|
|
||||||
case InstructionLess:
|
case InstructionLess:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&BoolValue{l < r})
|
vm.stack.Push(&BoolValue{l < r})
|
||||||
|
|
||||||
case InstructionLessOrEqual:
|
case InstructionLessOrEqual:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&BoolValue{l <= r})
|
vm.stack.Push(&BoolValue{l <= r})
|
||||||
|
|
||||||
case InstructionGreater:
|
case InstructionGreater:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&BoolValue{l > r})
|
vm.stack.Push(&BoolValue{l > r})
|
||||||
|
|
||||||
case InstructionGreaterOrEqual:
|
case InstructionGreaterOrEqual:
|
||||||
r := vm.stack.Pop().(*NumberValue).float64
|
r := vm.stack.Pop().(*NumberValue).Number
|
||||||
l := vm.stack.Pop().(*NumberValue).float64
|
l := vm.stack.Pop().(*NumberValue).Number
|
||||||
|
|
||||||
vm.stack.Push(&BoolValue{l >= r})
|
vm.stack.Push(&BoolValue{l >= r})
|
||||||
|
|
||||||
|
|
@ -479,7 +660,7 @@ func (vm *VM) Next() bool {
|
||||||
for i := len(f.Params) - 1; i >= 0; i-- {
|
for i := len(f.Params) - 1; i >= 0; i-- {
|
||||||
p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
|
p := vm.stack.Current - Pos(len(f.Params)) + Pos(i)
|
||||||
vm.stack.items[p] = &VariableValue{
|
vm.stack.items[p] = &VariableValue{
|
||||||
f.Params[i],
|
f.Params[i].Name,
|
||||||
vm.stack.items[p],
|
vm.stack.items[p],
|
||||||
vm.scope,
|
vm.scope,
|
||||||
}
|
}
|
||||||
|
|
@ -494,10 +675,10 @@ func (vm *VM) Next() bool {
|
||||||
vm.chunk = f.Chunk
|
vm.chunk = f.Chunk
|
||||||
vm.ip = 0
|
vm.ip = 0
|
||||||
case *BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
args := map[string]Value{}
|
args := make([]Value, len(f.Signature.In))
|
||||||
|
|
||||||
for i := len(f.Parameters) - 1; i >= 0; i-- {
|
for i := len(f.Signature.In) - 1; i >= 0; i-- {
|
||||||
args[f.Parameters[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)
|
||||||
|
|
@ -519,12 +700,12 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionJumpFalse:
|
case InstructionJumpFalse:
|
||||||
n := vm.NextU16()
|
n := vm.NextU16()
|
||||||
if !vm.stack.Pop().(*BoolValue).bool {
|
if !vm.stack.Pop().(*BoolValue).Boolean {
|
||||||
vm.ip += Pos(n)
|
vm.ip += Pos(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
case InstructionGetLocal:
|
case InstructionGetLocal:
|
||||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||||
v := vm.getVar(name)
|
v := vm.getVar(name)
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
|
@ -536,7 +717,7 @@ func (vm *VM) Next() bool {
|
||||||
|
|
||||||
case InstructionSetLocal:
|
case InstructionSetLocal:
|
||||||
value := vm.stack.Pop().(Value)
|
value := vm.stack.Pop().(Value)
|
||||||
name := vm.GetConstant(vm.NextByte()).(*StringValue).string
|
name := vm.GetConstant(vm.NextByte()).(*StringValue).Text
|
||||||
|
|
||||||
v := vm.getVar(name)
|
v := vm.getVar(name)
|
||||||
|
|
||||||
|
|
@ -544,19 +725,19 @@ func (vm *VM) Next() bool {
|
||||||
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
vm.error(fmt.Sprintf("cannot set local: undefined variable %s", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
v.value = value
|
v.value = value.Clone()
|
||||||
|
|
||||||
case InstructionDeclareLocal:
|
case InstructionDeclareLocal:
|
||||||
vm.addVar(
|
vm.addVar(
|
||||||
vm.GetConstant(vm.NextByte()).(*StringValue).string,
|
vm.GetConstant(vm.NextByte()).(*StringValue).Text,
|
||||||
vm.stack.Pop().(Value),
|
vm.stack.Pop().Clone(),
|
||||||
)
|
)
|
||||||
|
|
||||||
case InstructionGetGlobal:
|
case InstructionGetGlobal:
|
||||||
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string])
|
vm.stack.Push(vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).Text])
|
||||||
|
|
||||||
case InstructionSetGlobal:
|
case InstructionSetGlobal:
|
||||||
vm.globals[vm.GetConstant(vm.NextByte()).(*StringValue).string] = 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})
|
||||||
|
|
@ -570,20 +751,32 @@ func (vm *VM) Next() bool {
|
||||||
case InstructionFormList:
|
case InstructionFormList:
|
||||||
n := int(vm.NextU16())
|
n := int(vm.NextU16())
|
||||||
|
|
||||||
items := make([]Value, n+1)
|
items := make([]Value, n)
|
||||||
for i := 0; i <= n; i++ {
|
for i := n - 1; i >= 0; i-- {
|
||||||
items[n-i] = vm.stack.Pop()
|
items[i] = vm.stack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vm.stack.Push(&ListValue{
|
||||||
|
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:
|
||||||
|
r := vm.stack.Pop().(*ListValue)
|
||||||
|
l := vm.stack.Pop().(*ListValue)
|
||||||
|
|
||||||
|
vm.stack.Push(&ListValue{
|
||||||
|
append(l.Items, r.Items...),
|
||||||
|
})
|
||||||
|
|
||||||
case InstructionDescend:
|
case InstructionDescend:
|
||||||
vm.descend()
|
vm.descend()
|
||||||
|
|
||||||
|
|
@ -595,8 +788,8 @@ func (vm *VM) Next() bool {
|
||||||
vm.stack.Push(&StringValue{v.String()})
|
vm.stack.Push(&StringValue{v.String()})
|
||||||
|
|
||||||
case InstructionStringConcatenation:
|
case InstructionStringConcatenation:
|
||||||
r := vm.stack.Pop().(*StringValue).string
|
r := vm.stack.Pop().(*StringValue).Text
|
||||||
l := vm.stack.Pop().(*StringValue).string
|
l := vm.stack.Pop().(*StringValue).Text
|
||||||
|
|
||||||
vm.stack.Push(&StringValue{l + r})
|
vm.stack.Push(&StringValue{l + r})
|
||||||
|
|
||||||
|
|
@ -645,7 +838,7 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := 0; i < len(f.Params); i++ {
|
for i := 0; i < len(f.Params); i++ {
|
||||||
vm.addVar(f.Params[i], args[i])
|
vm.addVar(f.Params[i].Name, args[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Parent != nil {
|
if f.Parent != nil {
|
||||||
|
|
@ -660,25 +853,21 @@ func (vm *VM) Call(v Value, args []Value) (Value, error) {
|
||||||
for vm.chunk.Bytecode[vm.ip] != InstructionReturn && vm.Next() {
|
for vm.chunk.Bytecode[vm.ip] != InstructionReturn && vm.Next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if vm.HasNext() {
|
|
||||||
vm.Next()
|
vm.Next()
|
||||||
}
|
|
||||||
|
|
||||||
return vm.stack.Pop(), nil
|
return vm.stack.Pop(), nil
|
||||||
|
|
||||||
case *BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
argies := map[string]Value{}
|
return f.F(vm, f.Parent, args)
|
||||||
|
|
||||||
for i, arg := range args {
|
|
||||||
argies[f.Parameters[i]] = arg
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.F(vm, f.Parent, argies)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New(fmt.Sprintf("value is not a function (%s)", v.DebugString()))
|
return nil, errors.New(fmt.Sprintf("value is not a function (%s)", v.DebugString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *VM) SetChunk(c *Chunk) {
|
||||||
|
vm.chunk = c
|
||||||
|
}
|
||||||
|
|
||||||
func (vm *VM) TryNextByte() (Bytecode, error) {
|
func (vm *VM) TryNextByte() (Bytecode, error) {
|
||||||
if !vm.HasNext() {
|
if !vm.HasNext() {
|
||||||
return 0, errors.New("there are no more instructions")
|
return 0, errors.New("there are no more instructions")
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,13 @@ func TestNewVM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// should have given stack size
|
// should have given stack size
|
||||||
if vm.stack.Size != stackSize {
|
if vm.stack.Capacity != stackSize {
|
||||||
t.Errorf("vm.stack.Size = %d, want %d", vm.stack.Size, 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
|
||||||
if vm.call.Size != callstackSize {
|
if vm.call.Capacity != callstackSize {
|
||||||
t.Errorf("vm.call.Size = %d, want %d", vm.call.Size, callstackSize)
|
t.Errorf("vm.call.Capacity = %d, want %d", vm.call.Capacity, callstackSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,7 +442,16 @@ func GetExecutionTestData() map[string]struct {
|
||||||
&NumberValue{2},
|
&NumberValue{2},
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionGetLocal, 0,
|
InstructionGetLocal, 0,
|
||||||
|
|
@ -476,7 +485,16 @@ func GetExecutionTestData() map[string]struct {
|
||||||
&NumberValue{2},
|
&NumberValue{2},
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
Name: "sum",
|
Name: "sum",
|
||||||
Params: []string{"a", "b"},
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"a",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"b",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionGetLocal, 0,
|
InstructionGetLocal, 0,
|
||||||
|
|
@ -493,7 +511,12 @@ func GetExecutionTestData() map[string]struct {
|
||||||
},
|
},
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
Name: "square",
|
Name: "square",
|
||||||
Params: []string{"n"},
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"n",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionGetLocal, 0,
|
InstructionGetLocal, 0,
|
||||||
|
|
@ -514,7 +537,12 @@ func GetExecutionTestData() map[string]struct {
|
||||||
"square",
|
"square",
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
Name: "square",
|
Name: "square",
|
||||||
Params: []string{"n"},
|
Params: []FunctionParameter{
|
||||||
|
{
|
||||||
|
"n",
|
||||||
|
&NumberSignature{},
|
||||||
|
},
|
||||||
|
},
|
||||||
Chunk: NewChunk(
|
Chunk: NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionGetLocal, 0,
|
InstructionGetLocal, 0,
|
||||||
|
|
@ -532,6 +560,37 @@ func GetExecutionTestData() map[string]struct {
|
||||||
&NumberValue{5},
|
&NumberValue{5},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"list_concat": {
|
||||||
|
NewChunk(
|
||||||
|
[]Bytecode{
|
||||||
|
InstructionConstant, 0,
|
||||||
|
InstructionConstant, 1,
|
||||||
|
InstructionConcatLists,
|
||||||
|
},
|
||||||
|
[]Value{
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[]Value{
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
&NumberValue{3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
emoji.ang
Normal file
2
emoji.ang
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
write(char(0x12) + char(0x85) + char(0x07))
|
||||||
0
examples/brainfuck.ang
Normal file
0
examples/brainfuck.ang
Normal file
|
|
@ -18,7 +18,7 @@ while n <= terms {
|
||||||
tot = tot * 6
|
tot = tot * 6
|
||||||
|
|
||||||
# get the absolute value of a number
|
# get the absolute value of a number
|
||||||
func abs(x) {
|
func abs(x: number) number {
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
return -x
|
return -x
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ func abs(x) {
|
||||||
# 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
|
||||||
func sqrt(x) {
|
func sqrt(x: number) number {
|
||||||
pg := 0 # previous guess
|
pg := 0 # previous guess
|
||||||
g := 1 # current guess
|
g := 1 # current guess
|
||||||
|
|
||||||
|
|
@ -45,4 +45,4 @@ func sqrt(x) {
|
||||||
tot = sqrt(tot)
|
tot = sqrt(tot)
|
||||||
|
|
||||||
# output the result
|
# output the result
|
||||||
write(tot)
|
write(str(tot))
|
||||||
|
|
|
||||||
36
examples/pi-approx.py
Normal file
36
examples/pi-approx.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
terms = 100000
|
||||||
|
|
||||||
|
tot = 0
|
||||||
|
|
||||||
|
n = 1
|
||||||
|
while n <= terms:
|
||||||
|
tot = tot + 1 / (n*n)
|
||||||
|
n = n + 1
|
||||||
|
|
||||||
|
tot = tot * 6
|
||||||
|
|
||||||
|
# get the absolute value of a number
|
||||||
|
def abs(x):
|
||||||
|
if x < 0:
|
||||||
|
return -x
|
||||||
|
return x
|
||||||
|
|
||||||
|
# calculate an approximation of the square root of tot using
|
||||||
|
# newton's method.
|
||||||
|
# see: https://en.wikipedia.org/wiki/Newton's_method
|
||||||
|
# The required accuracy
|
||||||
|
SQRT_ACC = 0.00000001
|
||||||
|
def sqrt(x):
|
||||||
|
pg = 0 # previous guess
|
||||||
|
g = 1 # current guess
|
||||||
|
|
||||||
|
while abs(pg - g) >= SQRT_ACC:
|
||||||
|
pg = g
|
||||||
|
g = (pg + tot/pg)/2
|
||||||
|
|
||||||
|
return g
|
||||||
|
|
||||||
|
tot = sqrt(tot)
|
||||||
|
|
||||||
|
# output the result
|
||||||
|
print(tot)
|
||||||
16
examples/pøck.ang
Normal file
16
examples/pøck.ang
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import "math.ang"
|
||||||
|
|
||||||
|
func r_x(t) {
|
||||||
|
return 8*(exp(-t) - t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func r_y(t) {
|
||||||
|
return 5*(exp(-t) - t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func r(t) {
|
||||||
|
return format("(%s, %s)", [r_x(t), r_y(t)])
|
||||||
|
}
|
||||||
|
|
||||||
|
write(r(1))
|
||||||
|
write()
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
|
# This program computes the fibonacci numbers using recursion (O(2^n))
|
||||||
# fibonacci sequence
|
# It is very slow
|
||||||
func fib(n) {
|
func fib(x: number) number {
|
||||||
if n <= 1 {
|
if x <= 1 {
|
||||||
return n
|
return x
|
||||||
}
|
}
|
||||||
|
return fib(x - 1) + fib(x - 2)
|
||||||
return fib(n - 1) + fib(n - 2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
while n < 10 {
|
while n < 100 {
|
||||||
write(fib(n))
|
write(str(fib(n)))
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
examples/solving.ang
Normal file
13
examples/solving.ang
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import "../lib/math.ang"
|
||||||
|
|
||||||
|
a := 120
|
||||||
|
b := 10
|
||||||
|
|
||||||
|
# find log_b(a)
|
||||||
|
func f(x: number)number {
|
||||||
|
return pow(b, x) - a
|
||||||
|
}
|
||||||
|
|
||||||
|
log_b := newtons(f)
|
||||||
|
write(str(log_b))
|
||||||
|
write("inverse: "+str(pow(b, log_b))+" = "+str(a))
|
||||||
4
fails.ang
Normal file
4
fails.ang
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import "lib/honning.ang"
|
||||||
|
|
||||||
|
write(_bell+_italic+"Hello "+_underline+"world "+_strike+"micheal"+_reset)
|
||||||
|
|
||||||
15
imp.ang
Normal file
15
imp.ang
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
func is_cool(x: number|string) boolean {
|
||||||
|
if x == "cool" {
|
||||||
|
return true
|
||||||
|
} else if x == 69 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
write(str(is_cool("not cool")))
|
||||||
|
write(str(is_cool("cool")))
|
||||||
|
write(str(is_cool(0)))
|
||||||
|
write(str(is_cool(69)))
|
||||||
15
lib/honning.ang
Normal file
15
lib/honning.ang
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
_bell := char(0x07)
|
||||||
|
|
||||||
|
ESC := char(0x1B)
|
||||||
|
CSI := ESC + "["
|
||||||
|
|
||||||
|
_reset := CSI + "0m"
|
||||||
|
_bold := CSI + "1m"
|
||||||
|
_faint := CSI + "2m"
|
||||||
|
_italic := CSI + "3m"
|
||||||
|
_underline := CSI + "4m"
|
||||||
|
_slow_blink := CSI + "5m"
|
||||||
|
_rapid_blink := CSI + "6m"
|
||||||
|
_strike := CSI + "9m"
|
||||||
|
_primary_font := CSI + "10m"
|
||||||
12
lib/list.ang
Normal file
12
lib/list.ang
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
func map(list: list[any], f: func(any)any) list[any] {
|
||||||
|
out := []
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
while i < list.length() {
|
||||||
|
out.append(f(list.at(i)))
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
func abs(x) {
|
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,13 +17,31 @@ func abs(x) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DERIVE_DX := 0.00000001
|
||||||
|
func derive(f: func(number)number, x: number) number {
|
||||||
|
return (f(x + DERIVE_DX) - f(x))/DERIVE_DX
|
||||||
|
}
|
||||||
|
|
||||||
|
NEWTONS_ACC := 0.000000000001
|
||||||
|
func newtons(f: func(number)number) number {
|
||||||
|
pg := 0
|
||||||
|
g := 1
|
||||||
|
|
||||||
|
while abs(g - pg) > NEWTONS_ACC {
|
||||||
|
pg = g
|
||||||
|
g = pg - f(pg) / derive(f, pg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
MAX_SQRT_DX := 0.0000001
|
MAX_SQRT_DX := 0.0000001
|
||||||
|
|
||||||
# sqrt(x)
|
# sqrt(x)
|
||||||
# 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`.
|
||||||
func sqrt(x) {
|
func sqrt(x: number) number {
|
||||||
ng := x
|
ng := x
|
||||||
g := 1
|
g := 1
|
||||||
|
|
||||||
|
|
@ -42,23 +60,19 @@ func sqrt(x) {
|
||||||
# Return the whole number part of the number. if x is a whole number,
|
# Return the whole number part of the number. if x is a whole number,
|
||||||
# the returned value is x. If x is not a whole number, the closest
|
# the returned value is x. If x is not a whole number, the closest
|
||||||
# whole number which is less than or equal to x is returned.
|
# whole number which is less than or equal to x is returned.
|
||||||
func floor(x) {
|
|
||||||
# todo
|
|
||||||
}
|
|
||||||
|
|
||||||
# ceil(x)
|
# ceil(x)
|
||||||
# x: number
|
# x: number
|
||||||
# Return the whole number part of the number. if x is a whole number,
|
# Return the whole number part of the number. if x is a whole number,
|
||||||
# the returned value is x. If x is not a whole number, the closest
|
# the returned value is x. If x is not a whole number, the closest
|
||||||
# whole number which is greater than or equal to x is returned.
|
# whole number which is greater than or equal to x is returned.
|
||||||
func ceil(x) {
|
|
||||||
# todo
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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.
|
||||||
func round(x) {
|
func round(x: number) number {
|
||||||
f := floor(x)
|
f := floor(x)
|
||||||
|
|
||||||
if x - f > 0.5 {
|
if x - f > 0.5 {
|
||||||
|
|
@ -68,15 +82,128 @@ func round(x) {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# mod(x, n)
|
||||||
|
# x: number; any number
|
||||||
|
# n: number; the number to divide by
|
||||||
|
# Return the rest from a division of x by n.
|
||||||
|
func mod(x: number, n: number) number {
|
||||||
|
if x == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < 0 {
|
||||||
|
while x + n <= 0 {
|
||||||
|
x = x + n
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while x - n >= 0 {
|
||||||
|
x = x - n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
# sm_exp(x)
|
||||||
|
# x: number; any number between 0 and 1
|
||||||
|
# Get an approximate value of e raised to the power of x.
|
||||||
|
# This value is only reasonable if 0<x<1.
|
||||||
|
# It is approximated using the taylor series of e**x.
|
||||||
|
SM_EXP_ACC := 0.00000000001
|
||||||
|
func sm_exp(x: number) number {
|
||||||
|
p_tot := 0
|
||||||
|
tot := 1
|
||||||
|
n := 1
|
||||||
|
x_pow := x
|
||||||
|
f := 1
|
||||||
|
while abs(tot - p_tot) > SM_EXP_ACC {
|
||||||
|
p_tot = tot
|
||||||
|
t := x_pow / f
|
||||||
|
tot = tot + t
|
||||||
|
f = f * (n+1)
|
||||||
|
x_pow = x_pow * x
|
||||||
|
n = n + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return tot
|
||||||
|
}
|
||||||
|
|
||||||
|
# exp(x)
|
||||||
|
# x: number; any number
|
||||||
|
# Get an approximate value of e raised to the power of x.
|
||||||
|
func exp(x: number) number {
|
||||||
|
n := abs(x)
|
||||||
|
tot := 1
|
||||||
|
while n >= 1 {
|
||||||
|
tot = tot * E
|
||||||
|
n = n - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
tot = tot * sm_exp(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < 0 {
|
||||||
|
return 1/tot
|
||||||
|
} else {
|
||||||
|
return tot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ln(x)
|
||||||
|
# x: number; any number
|
||||||
|
# Get the approximate value of the natural logarithm
|
||||||
|
# This function uses newton's method to approximate.
|
||||||
|
LN_ACC := 0.0000000001
|
||||||
|
func ln(x: number) number {
|
||||||
|
pg := 0
|
||||||
|
g := 1
|
||||||
|
|
||||||
|
while abs(pg - g) > LN_ACC {
|
||||||
|
pg = g
|
||||||
|
g = pg + x / exp(pg) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
# pow(x, p)
|
||||||
|
# x: number; any number. The base
|
||||||
|
# p: number; the value of the exponent
|
||||||
|
# Raise any number to any power (x^p)
|
||||||
|
func pow(x: number, p: number) number {
|
||||||
|
return exp(p*ln(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
# log(x, b)
|
||||||
|
# x: number; any number greater than 0
|
||||||
|
# b: number; any number as the base
|
||||||
|
# Calculate the approximate value of the logarithm
|
||||||
|
# of a with b as base.
|
||||||
|
LOG_ACC := 0.0000001
|
||||||
|
func log(a: number, b: number) number {
|
||||||
|
ln_b := ln(b)
|
||||||
|
|
||||||
|
pg := 0
|
||||||
|
g := 1
|
||||||
|
|
||||||
|
while abs(g - pg) > LOG_ACC {
|
||||||
|
pg = g
|
||||||
|
g = pg - 1/ln_b - a/(ln_b*pow(b, pg))
|
||||||
|
}
|
||||||
|
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
# sin(x)
|
# sin(x)
|
||||||
# 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
|
||||||
func sin(x) {
|
func sin(x: number) number {
|
||||||
f := 1
|
f := 1
|
||||||
x = mod(x, 2*PI)
|
x = mod(x, 2*PI)
|
||||||
if x > PI {
|
if x > PI {
|
||||||
x = -x
|
x = PI - x
|
||||||
f = -1
|
f = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,106 +228,13 @@ func sin(x) {
|
||||||
# cos(x)
|
# cos(x)
|
||||||
# x: number; an angle in radians
|
# x: number; an angle in radians
|
||||||
# Get the cosine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine
|
# Get the cosine of an angle (in radians). https://en.wikipedia.org/wiki/Sine_and_cosine
|
||||||
func cos(x) {
|
func cos(x: number) number {
|
||||||
# todo
|
# todo
|
||||||
}
|
}
|
||||||
|
|
||||||
# tan(x)
|
# tan(x)
|
||||||
# x: number; an angle in radians
|
# x: number; an angle in radians
|
||||||
# Get the tangent of an angle. https://en.wikipedia.org/wiki/Tangent
|
# Get the tangent of an angle. https://en.wikipedia.org/wiki/Tangent
|
||||||
func tan(x) {
|
func tan(x: number) number {
|
||||||
# todo
|
# todo
|
||||||
}
|
}
|
||||||
|
|
||||||
# mod(x, n)
|
|
||||||
# x: number; any number
|
|
||||||
# n: number; the number to divide by
|
|
||||||
# Return the rest from a division of x by n.
|
|
||||||
func mod(x, n) {
|
|
||||||
if x == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if x < 0 {
|
|
||||||
while x + n <= 0 {
|
|
||||||
x = x + n
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while x - n >= 0 {
|
|
||||||
x = x - n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
# ln(x)
|
|
||||||
# x: number; any number
|
|
||||||
# Get the approximate value of the natural logarithm
|
|
||||||
# This function uses newton's method to approximate.
|
|
||||||
LN_ACC := 0.000000001
|
|
||||||
func ln(x) {
|
|
||||||
pg := 0
|
|
||||||
g := 1
|
|
||||||
|
|
||||||
while abs(pg - g) > LN_ACC {
|
|
||||||
pg = g
|
|
||||||
g = pg + x / exp(pg) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
# sm_exp(x)
|
|
||||||
# x: number; any number between 0 and 1
|
|
||||||
# Get an approximate value of e raised to the power of x.
|
|
||||||
# This value is only reasonable if 0<x<1.
|
|
||||||
# It is approximated using the taylor series of e**x.
|
|
||||||
SM_EXP_ACC := 0.00000000001
|
|
||||||
func sm_exp(x) {
|
|
||||||
p_tot := 0
|
|
||||||
tot := 1
|
|
||||||
n := 1
|
|
||||||
x_pow := x
|
|
||||||
f := 1
|
|
||||||
while abs(tot - p_tot) > SM_EXP_ACC {
|
|
||||||
p_tot = tot
|
|
||||||
t := x_pow / f
|
|
||||||
tot = tot + t
|
|
||||||
f = f * (n+1)
|
|
||||||
x_pow = x_pow * x
|
|
||||||
n = n + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return tot
|
|
||||||
}
|
|
||||||
|
|
||||||
# exp(x)
|
|
||||||
# x: number; any number
|
|
||||||
# Get an approximate value of e raised to the power of x.
|
|
||||||
func exp(x) {
|
|
||||||
n := abs(x)
|
|
||||||
tot := 1
|
|
||||||
while n >= 1 {
|
|
||||||
tot = tot * E
|
|
||||||
n = n - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
tot = tot * sm_exp(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
if x < 0 {
|
|
||||||
return 1/tot
|
|
||||||
} else {
|
|
||||||
return tot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# pow(x, p)
|
|
||||||
# x: number; any number. The base
|
|
||||||
# p: number; the value of the exponent
|
|
||||||
# Raise any number to any power (x^p)
|
|
||||||
func pow(x, p) {
|
|
||||||
return exp(p*ln(x))
|
|
||||||
}
|
|
||||||
25
lib/testing.ang
Normal file
25
lib/testing.ang
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
NAMESPACE := ""
|
||||||
|
|
||||||
|
func namespace(name: string, test: func()) {
|
||||||
|
NAMESPACE = name
|
||||||
|
test()
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertEqual(a: any, b: any) {
|
||||||
|
if a != b {
|
||||||
|
write(format("assertion error: % should (but doesn't) equal %", [a, b]))
|
||||||
|
exit(1)
|
||||||
|
} else if env("DEBUG") != "" {
|
||||||
|
write(format("assertion success: % equals %", [a, b]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertNotEqual(a: any, b: any) {
|
||||||
|
if a == b {
|
||||||
|
write(format("assertion error: % shouldn't (but does) equal %", [a, b]))
|
||||||
|
exit(1)
|
||||||
|
} else if env("DEBUG") != "" {
|
||||||
|
write(format("assertion success: % doesn't equal %", [a, b]))
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/util.ang
Normal file
4
lib/util.ang
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
func memoize(f) {
|
||||||
|
|
||||||
|
}
|
||||||
46
test_all.sh
46
test_all.sh
|
|
@ -1,22 +1,46 @@
|
||||||
#!/bin/zsh
|
#!/bin/bash
|
||||||
|
|
||||||
echo '== Building CLI =='
|
echo '=== Building CLI ==='
|
||||||
cd cli
|
cd cli || exit 1
|
||||||
go build .
|
if ! go build .; then
|
||||||
|
echo "=== Had error building CLI ==="
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "=+= Successfully built CLI =+="
|
||||||
|
fi
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
echo "=== Running go core tests ==="
|
||||||
|
cd core || exit 1
|
||||||
|
if ! go test .; then
|
||||||
|
echo "=x= Core testing failed =x= "
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "=+= Successfully ran core tests =+="
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
|
||||||
echo '== Testing anglais =='
|
errors=()
|
||||||
|
|
||||||
for file in ./tests/*.ang; do
|
echo '=== Testing anglais ==='
|
||||||
echo "-- Test-running file $file --"
|
|
||||||
|
# read files
|
||||||
|
for file in $(find tests -type f); do
|
||||||
|
echo "-v- Test-running file $file -v-"
|
||||||
|
|
||||||
if ! ./cli/cli run "$file"; then
|
if ! ./cli/cli run "$file"; then
|
||||||
echo "-- Error --"
|
echo "-x- Error -x-"
|
||||||
exit 1
|
errors+=("$file")
|
||||||
else
|
else
|
||||||
echo "-- Success --"
|
echo "-+- Success -+-"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo '== Successfully ran all tests =='
|
|
||||||
|
if [ 0 -ne "$(wc -w <<< "${errors[@]}")" ]; then
|
||||||
|
echo "=x= Errors occured while executing =x="
|
||||||
|
echo "erroring files: $(printf '%s ' "${errors[@]}")"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo '=+= Successfully ran all tests =+='
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ assertEq(1, 1)
|
||||||
assertEq(0, 0)
|
assertEq(0, 0)
|
||||||
assertEq("", "")
|
assertEq("", "")
|
||||||
|
|
||||||
assertEq([], [])
|
assertEq([]number, []number)
|
||||||
assertEq([3, 1, 4, 1], [3, 1, 4, 1])
|
assertEq([3, 1, 4, 1], [3, 1, 4, 1])
|
||||||
assertEq([true, 1024, nil, "Hello world!"], [true, 1024, nil, "Hello world!"])
|
|
||||||
|
|
||||||
# Inequality
|
# Inequality
|
||||||
assertNotEq(2, 3)
|
assertNotEq(2, 3)
|
||||||
|
|
|
||||||
3
tests/hex.ang
Normal file
3
tests/hex.ang
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
assertEq(0x00, 0)
|
||||||
|
assertEq(0xFF, 255)
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
list := []
|
list := []number
|
||||||
|
|
||||||
x := 1
|
x := 1
|
||||||
while x <= 1000 {
|
while x <= 1000 {
|
||||||
list.append(x)
|
list.append(x)
|
||||||
assertEq(list.reduce(func(tot, a){
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func sum(a, b) {
|
func sum(a: number, b: number) number {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
list = []
|
list = []number
|
||||||
x = 1
|
x = 1
|
||||||
while x <= 100 {
|
while x <= 100 {
|
||||||
list.append(2*x - 1)
|
list.append(2*x - 1)
|
||||||
|
|
@ -22,3 +22,6 @@ while x <= 100 {
|
||||||
|
|
||||||
x = x + 1
|
x = x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertEq([1, 2] + [3], [1, 2, 3])
|
||||||
|
assertEq(["Eny", "meanie"] + ["minie", "moe"], ["Eny", "meanie", "minie", "moe"])
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@
|
||||||
fibonacci_numbers := [
|
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,
|
||||||
610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657,
|
610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657,
|
||||||
46368, 75025, 121393, 196418, 317811, 514229, 832040,
|
46368, 75025, 121393, 196418, 317811, 514229, 832040
|
||||||
1346269, 2178309, 3524578, 5702887, 9227465, 14930352
|
|
||||||
]
|
]
|
||||||
|
|
||||||
func fib(n) {
|
func fib(n: number) number {
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
@ -16,11 +15,12 @@ func fib(n) {
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
while n < fibonacci_numbers.length() {
|
while n < fibonacci_numbers.length() {
|
||||||
print("_")
|
print("-")
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
write("")
|
# return to start of line (with carriage return \r)
|
||||||
|
print(char(0x0D))
|
||||||
|
|
||||||
x := 0
|
x := 0
|
||||||
while x < fibonacci_numbers.length() {
|
while x < fibonacci_numbers.length() {
|
||||||
|
|
|
||||||
13
tests/refs.ang
Normal file
13
tests/refs.ang
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
list := []number
|
||||||
|
|
||||||
|
list.append(1)
|
||||||
|
list.append(2)
|
||||||
|
|
||||||
|
assertEq(list, [1, 2])
|
||||||
|
|
||||||
|
other := list
|
||||||
|
other.append(3)
|
||||||
|
|
||||||
|
assertEq(other, [1, 2, 3])
|
||||||
|
assertEq(list, [1, 2])
|
||||||
|
|
@ -4,9 +4,12 @@ a := 2
|
||||||
{
|
{
|
||||||
a := 3
|
a := 3
|
||||||
assertEq(a, 3)
|
assertEq(a, 3)
|
||||||
|
breakpoint
|
||||||
|
|
||||||
a = 4
|
a = 4
|
||||||
assertEq(a, 4)
|
assertEq(a, 4)
|
||||||
|
breakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEq(a, 2)
|
assertEq(a, 2)
|
||||||
|
breakpoint
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
func sum(a, b) {
|
func sum(a: number, b: number) number {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
8
tests/types.ang
Normal file
8
tests/types.ang
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
assertEq(type(1), "number")
|
||||||
|
assertEq(type("Hello"), "string")
|
||||||
|
assertEq(type(true), "boolean")
|
||||||
|
|
||||||
|
# lists
|
||||||
|
assertEq(type(["Hello", "world"]), "list[string]")
|
||||||
|
assertEq(type([0, 1]), "list[number]")
|
||||||
76
wasm/wasm.go
76
wasm/wasm.go
|
|
@ -13,32 +13,22 @@ type JsResolver struct {
|
||||||
jsResolver js.Value
|
jsResolver js.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *JsResolver) Resolve(name string) (core.Node, error) {
|
func (r *JsResolver) IsSame(a, b string) bool {
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JsResolver) Resolve(name string) (string, error) {
|
||||||
jsv := r.jsResolver.Invoke(name)
|
jsv := r.jsResolver.Invoke(name)
|
||||||
|
|
||||||
if jsv.Type() == js.TypeUndefined {
|
if jsv.Type() == js.TypeUndefined {
|
||||||
return nil, errors.New("cannot find import with name " + name)
|
return "", errors.New("cannot find import with name " + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if jsv.Type() != js.TypeString {
|
if jsv.Type() != js.TypeString {
|
||||||
return nil, errors.New("invalid value for source: " + jsv.String())
|
return "", errors.New("invalid value for source: " + jsv.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
source := jsv.String()
|
return jsv.String(), nil
|
||||||
|
|
||||||
l := core.NewLexer(source)
|
|
||||||
tokens, err := l.Tokenize()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := core.NewParser(tokens)
|
|
||||||
tree, err := p.Parse()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsError(err error) interface{} {
|
func jsError(err error) interface{} {
|
||||||
|
|
@ -55,7 +45,8 @@ func jsErrorOfString(err string) interface{} {
|
||||||
func run(_ js.Value, args []js.Value) interface{} {
|
func run(_ js.Value, args []js.Value) interface{} {
|
||||||
source := args[0].String()
|
source := args[0].String()
|
||||||
outputHandler := args[1]
|
outputHandler := args[1]
|
||||||
resolver := args[2]
|
errorHandler := args[2]
|
||||||
|
resolver := args[3]
|
||||||
log.Printf("got source: %s", source)
|
log.Printf("got source: %s", source)
|
||||||
|
|
||||||
lexer := core.NewLexer(source)
|
lexer := core.NewLexer(source)
|
||||||
|
|
@ -67,17 +58,25 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
|
|
||||||
log.Printf("got tokens: %v", tokens)
|
log.Printf("got tokens: %v", tokens)
|
||||||
|
|
||||||
parser := core.NewParser(tokens)
|
parser := core.NewParser(source, tokens)
|
||||||
|
|
||||||
tree, err := parser.Parse()
|
tree, err := parser.Parse(source)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return jsErrorOfString(err.Error())
|
var e core.FormatedError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
errorHandler.Invoke(e.Format())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errorHandler.Invoke(err.Error())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Parsed tree: %s", tree.String())
|
log.Printf("Parsed tree: %s", tree.String())
|
||||||
|
|
||||||
compiler := core.NewCompiler()
|
compiler := core.NewCompiler([]rune(source))
|
||||||
|
|
||||||
|
log.Println("Set imports resolver")
|
||||||
|
|
||||||
compiler.SetImportsResolver(&JsResolver{
|
compiler.SetImportsResolver(&JsResolver{
|
||||||
resolver,
|
resolver,
|
||||||
|
|
@ -91,6 +90,13 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
|
|
||||||
err = compiler.Compile(tree)
|
err = compiler.Compile(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
var e core.CompilerError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
errorHandler.Invoke(e.Format())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errorHandler.Invoke(err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,19 +107,27 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
// overwrite output
|
// overwrite output
|
||||||
vm.SetGlobal("write", &core.BuiltinFunctionValue{
|
vm.SetGlobal("write", &core.BuiltinFunctionValue{
|
||||||
Name: "write",
|
Name: "write",
|
||||||
Parameters: []string{"value"},
|
Signature: &core.FunctionSignature{
|
||||||
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
In: []core.TypeSignature{&core.StringSignature{}},
|
||||||
log.Printf("Writing value: %s", v["value"].String())
|
Out: &core.NilSignature{},
|
||||||
outputHandler.Invoke(js.ValueOf(v["value"].String() + "\n"))
|
},
|
||||||
|
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
|
||||||
|
s := args[0].String()
|
||||||
|
log.Printf("Writing value: %s", s)
|
||||||
|
outputHandler.Invoke(js.ValueOf(s + "\n"))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
vm.SetGlobal("print", &core.BuiltinFunctionValue{
|
vm.SetGlobal("print", &core.BuiltinFunctionValue{
|
||||||
Name: "print",
|
Name: "print",
|
||||||
Parameters: []string{"value"},
|
Signature: &core.FunctionSignature{
|
||||||
F: func(vm *core.VM, this core.Value, v map[string]core.Value) (core.Value, error) {
|
In: []core.TypeSignature{&core.StringSignature{}},
|
||||||
log.Printf("Printing value: %s", v["value"].String())
|
Out: &core.NilSignature{},
|
||||||
outputHandler.Invoke(js.ValueOf(v["value"].String()))
|
},
|
||||||
|
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
|
||||||
|
s := args[0].String()
|
||||||
|
log.Printf("Printing value: %s", s)
|
||||||
|
outputHandler.Invoke(js.ValueOf(s))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue