not done yet
This commit is contained in:
parent
b73af5f92f
commit
49c15f6fb6
15 changed files with 473 additions and 389 deletions
5
bad.ang
Normal file
5
bad.ang
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import "lib/list.ang"
|
||||||
|
|
||||||
|
write(str(map([1, 2, 3], func(n: number) number {
|
||||||
|
return n*n
|
||||||
|
})))
|
||||||
144
cli/main.go
144
cli/main.go
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"neemek.com/anglais/core"
|
"neemek.com/anglais/core"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -24,45 +25,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
|
||||||
}
|
}
|
||||||
|
|
||||||
src := string(f)
|
return string(f), nil
|
||||||
|
|
||||||
l := core.NewLexer(src)
|
|
||||||
|
|
||||||
tokens, err := l.Tokenize()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p := core.NewParser(tokens)
|
func (r *WorkingDirectoryResolver) IsSame(a, b string) bool {
|
||||||
|
apath := filepath.Clean(filepath.Join(r.workingDirectory, a))
|
||||||
|
bpath := filepath.Clean(filepath.Join(r.workingDirectory, b))
|
||||||
|
|
||||||
tree, err := p.Parse()
|
return apath == bpath
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree, nil
|
func makeChunk(ctx *Context, filepath string, ignoreWarnings bool) (*core.Chunk, error) {
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *RunCmd) Run(ctx *Context) 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(filepath)
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -76,18 +66,18 @@ 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")
|
||||||
|
|
@ -101,7 +91,7 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
|
|
||||||
// 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +104,7 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
log.Println("Setting imports resolver")
|
log.Println("Setting imports resolver")
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, _ := filepath.Split(cmd.File)
|
dir, _ := path.Split(filepath)
|
||||||
c.SetImportsResolver(&WorkingDirectoryResolver{
|
c.SetImportsResolver(&WorkingDirectoryResolver{
|
||||||
dir,
|
dir,
|
||||||
})
|
})
|
||||||
|
|
@ -124,7 +114,7 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
}
|
}
|
||||||
err = c.Compile(tree)
|
err = c.Compile(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var e core.CompilerError
|
var e core.FormatedError
|
||||||
if errors.As(err, &e) {
|
if errors.As(err, &e) {
|
||||||
log.Fatal(e.Format())
|
log.Fatal(e.Format())
|
||||||
}
|
}
|
||||||
|
|
@ -132,15 +122,35 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there were non-critical warnings, report them
|
// if there were non-critical warnings, report them
|
||||||
if !cmd.IgnoreWarnings && len(c.Warnings) != 0 {
|
if !ignoreWarnings && len(c.Warnings) != 0 {
|
||||||
for _, warning := range c.Warnings {
|
for _, warning := range c.Warnings {
|
||||||
log.Println(warning.Format())
|
log.Println(warning.Format())
|
||||||
}
|
}
|
||||||
log.Fatal("compiler reported warning(s) (ignore warnings with the --ignore-warnings option)")
|
log.Fatal("compiler reported warning(s) (ignore warnings with the --ignore-warnings option)")
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = c.Chunk
|
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 {
|
} else {
|
||||||
|
if ctx.Debug {
|
||||||
|
log.Println("Reading file")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.ReadFile(cmd.File)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Registering GOB types")
|
log.Println("Registering GOB types")
|
||||||
}
|
}
|
||||||
|
|
@ -177,85 +187,15 @@ 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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
|
||||||
log.Fatal(err.(*core.ParsingError).Format([]rune(src)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Debug {
|
|
||||||
log.Println("Initialized compiler")
|
|
||||||
}
|
|
||||||
|
|
||||||
c := core.NewCompiler([]rune(src))
|
|
||||||
|
|
||||||
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 {
|
|
||||||
var e core.CompilerError
|
|
||||||
if errors.As(err, &e) {
|
|
||||||
log.Fatal(e.Format())
|
|
||||||
}
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there were non-critical warnings, report them
|
|
||||||
if len(c.Warnings) != 0 {
|
|
||||||
for _, warning := range c.Warnings {
|
|
||||||
log.Println(warning.Format())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
log.Println("Registering GOB types")
|
log.Println("Registering GOB types")
|
||||||
}
|
}
|
||||||
|
|
@ -266,7 +206,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")
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,48 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,7 +144,7 @@ func TestAll(t *testing.T) {
|
||||||
c := NewCompiler([]rune(tc.src))
|
c := NewCompiler([]rune(tc.src))
|
||||||
|
|
||||||
t.Log("Compiling parse tree")
|
t.Log("Compiling parse tree")
|
||||||
err = c.Compile(tree)
|
err = c.compile(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiler had an error: %s", err)
|
t.Fatalf("Compiler had an error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +177,7 @@ func BenchmarkAll(b *testing.B) {
|
||||||
tree, _ := p.Parse()
|
tree, _ := p.Parse()
|
||||||
|
|
||||||
c := NewCompiler([]rune(tc.src))
|
c := NewCompiler([]rune(tc.src))
|
||||||
_ = c.Compile(tree)
|
_ = c.compile(tree)
|
||||||
|
|
||||||
vm := NewVM(c.Chunk, 256, 256)
|
vm := NewVM(c.Chunk, 256, 256)
|
||||||
|
|
||||||
|
|
|
||||||
170
core/compiler.go
170
core/compiler.go
|
|
@ -2,6 +2,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -10,7 +11,8 @@ type Compiler struct {
|
||||||
ip Pos
|
ip Pos
|
||||||
scope Pos
|
scope Pos
|
||||||
|
|
||||||
imports map[string]Node
|
imports []string
|
||||||
|
importStack *Stack[string]
|
||||||
resolver ImportsResolver
|
resolver ImportsResolver
|
||||||
source []rune
|
source []rune
|
||||||
Warnings []CompilerError
|
Warnings []CompilerError
|
||||||
|
|
@ -19,7 +21,8 @@ type Compiler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImportsResolver interface {
|
type ImportsResolver interface {
|
||||||
Resolve(path string) (Node, error)
|
Resolve(path string) (string, error)
|
||||||
|
IsSame(a, b string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type LocalVariable struct {
|
type LocalVariable struct {
|
||||||
|
|
@ -106,7 +109,8 @@ func NewCompiler(source []rune) *Compiler {
|
||||||
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
make(map[string]Node),
|
make([]string, 0),
|
||||||
|
NewStack[string](256),
|
||||||
nil,
|
nil,
|
||||||
source,
|
source,
|
||||||
[]CompilerError{},
|
[]CompilerError{},
|
||||||
|
|
@ -141,7 +145,17 @@ func (c *Compiler) addConstant(value Value) {
|
||||||
c.add(Bytecode(len(chunk.Constants) - 1))
|
c.add(Bytecode(len(chunk.Constants) - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) Compile(tree Node) error {
|
func (c *Compiler) Compile(p *Program) error {
|
||||||
|
for _, i := range p.Imports {
|
||||||
|
if err := c.resolveImport(i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.compile(p.Block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) compile(tree Node) error {
|
||||||
if tree == nil {
|
if tree == nil {
|
||||||
panic("compile called with nil value")
|
panic("compile called with nil value")
|
||||||
}
|
}
|
||||||
|
|
@ -172,7 +186,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.addConstant(v)
|
c.addConstant(v)
|
||||||
} else {
|
} else {
|
||||||
for _, n := range l.items {
|
for _, n := range l.items {
|
||||||
err := c.Compile(n)
|
err := c.compile(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +214,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.add(InstructionConstant)
|
c.add(InstructionConstant)
|
||||||
c.addConstant(v)
|
c.addConstant(v)
|
||||||
} else {
|
} else {
|
||||||
err := c.Compile(tree.(*UnaryNode).value)
|
err := c.compile(tree.(*UnaryNode).value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +240,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
case BlockNodeType:
|
case BlockNodeType:
|
||||||
c.addDescend()
|
c.addDescend()
|
||||||
for _, n := range tree.(*BlockNode).statements {
|
for _, n := range tree.(*BlockNode).statements {
|
||||||
err := c.Compile(n)
|
err := c.compile(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +260,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the stack should have whether the condition was truthful
|
// the stack should have whether the condition was truthful
|
||||||
err = c.Compile(n.condition)
|
err = c.compile(n.condition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +273,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.advance(2)
|
c.advance(2)
|
||||||
|
|
||||||
// this part would be executed if the value was true
|
// this part would be executed if the value was true
|
||||||
err = c.Compile(n.do)
|
err = c.compile(n.do)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +291,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
|
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
|
||||||
|
|
||||||
if n.otherwise != nil {
|
if n.otherwise != nil {
|
||||||
err := c.Compile(n.otherwise)
|
err := c.compile(n.otherwise)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +311,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
conditionPos := c.ip
|
conditionPos := c.ip
|
||||||
err = c.Compile(n.condition)
|
err = c.compile(n.condition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -306,7 +320,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
jumpValuePos := c.ip
|
jumpValuePos := c.ip
|
||||||
c.advance(2)
|
c.advance(2)
|
||||||
|
|
||||||
err = c.Compile(n.do)
|
err = c.compile(n.do)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -322,13 +336,13 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
|
|
||||||
if n.name == "_" {
|
if n.name == "_" {
|
||||||
// allow non-ish statements
|
// allow non-ish statements
|
||||||
err := c.Compile(n.value)
|
err := c.compile(n.value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.add(InstructionPop)
|
c.add(InstructionPop)
|
||||||
} else {
|
} else {
|
||||||
if c.isVarDeclaredHere(n.name) {
|
if n.declare && c.isVarDeclaredHere(n.name) {
|
||||||
return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
|
return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,13 +384,13 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Compile(arg)
|
err = c.compile(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Compile(n.source)
|
err = c.compile(n.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -413,6 +427,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
// reset instruction pointer (ip)
|
// reset instruction pointer (ip)
|
||||||
c.ip = 0
|
c.ip = 0
|
||||||
|
|
||||||
|
c.descend()
|
||||||
for _, p := range n.parameters {
|
for _, p := range n.parameters {
|
||||||
c.registerVar(p.Name, p.Signature)
|
c.registerVar(p.Name, p.Signature)
|
||||||
}
|
}
|
||||||
|
|
@ -421,14 +436,11 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Compile(n.logic)
|
err = c.compile(n.logic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.ascend()
|
||||||
if n.logic.Type() != BlockNodeType {
|
|
||||||
c.stack.Pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.Constants[fi] = &FunctionValue{
|
mc.Constants[fi] = &FunctionValue{
|
||||||
n.name,
|
n.name,
|
||||||
|
|
@ -444,7 +456,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
|
|
||||||
case AccessNodeType:
|
case AccessNodeType:
|
||||||
n := tree.(*AccessNode)
|
n := tree.(*AccessNode)
|
||||||
err := c.Compile(n.source)
|
err := c.compile(n.source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -453,20 +465,8 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
n.property,
|
n.property,
|
||||||
})
|
})
|
||||||
|
|
||||||
case ImportNodeType:
|
|
||||||
n := tree.(*ImportNode)
|
|
||||||
|
|
||||||
t := c.resolveImport(n.path).(*BlockNode)
|
|
||||||
|
|
||||||
for _, statement := range t.statements {
|
|
||||||
err := c.Compile(statement)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ReturnNodeType:
|
case ReturnNodeType:
|
||||||
err := c.Compile(tree.(*ReturnNode).value)
|
err := c.compile(tree.(*ReturnNode).value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -494,11 +494,11 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.Compile(binary.Left)
|
err := c.compile(binary.Left)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = c.Compile(binary.Right)
|
err = c.compile(binary.Right)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -512,6 +512,8 @@ func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||||
|
|
||||||
if res.Type() == TypeString {
|
if res.Type() == TypeString {
|
||||||
c.add(InstructionStringConcatenation)
|
c.add(InstructionStringConcatenation)
|
||||||
|
} else if res.Type() == TypeList {
|
||||||
|
c.add(InstructionConcatLists)
|
||||||
} else {
|
} else {
|
||||||
c.add(InstructionAdd)
|
c.add(InstructionAdd)
|
||||||
}
|
}
|
||||||
|
|
@ -574,12 +576,16 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
|
|
||||||
if contents == nil {
|
if contents == nil {
|
||||||
contents = sig
|
contents = sig
|
||||||
} else {
|
} else if !contents.Matches(sig) {
|
||||||
contents = &AnySignature{}
|
contents = &AnySignature{}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if contents == nil {
|
||||||
|
return nil, c.error("can't deduce content type", n)
|
||||||
|
}
|
||||||
|
|
||||||
return &ListSignature{
|
return &ListSignature{
|
||||||
contents,
|
contents,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -594,7 +600,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if l != r {
|
if !l.Matches(r) {
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform binary %s on different types: %s and %s", n.BinaryOperation, l, r), n)
|
return nil, c.error(fmt.Sprintf("cannot perform binary %s on different types: %s and %s", n.BinaryOperation, l, r), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -611,6 +617,10 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
return &StringSignature{}, nil
|
return &StringSignature{}, nil
|
||||||
case TypeNumber:
|
case TypeNumber:
|
||||||
return &NumberSignature{}, nil
|
return &NumberSignature{}, nil
|
||||||
|
case TypeList:
|
||||||
|
return &ListSignature{
|
||||||
|
l.(*ListSignature).Contents,
|
||||||
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform binary addition on type %s", l), n)
|
return nil, c.error(fmt.Sprintf("cannot perform binary addition on type %s", l), n)
|
||||||
}
|
}
|
||||||
|
|
@ -774,6 +784,7 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("try affirming %s matches %s", v, sig)
|
||||||
if !sig.Matches(v) {
|
if !sig.Matches(v) {
|
||||||
return c.error(fmt.Sprintf("function cannot return a value with type %s. defined to be %s", v, sig), n.value)
|
return c.error(fmt.Sprintf("function cannot return a value with type %s. defined to be %s", v, sig), n.value)
|
||||||
}
|
}
|
||||||
|
|
@ -857,7 +868,7 @@ func (c *Compiler) getVar(name string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) setVar(name string, value Node, declare bool) error {
|
func (c *Compiler) setVar(name string, value Node, declare bool) error {
|
||||||
err := c.Compile(value)
|
err := c.compile(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -915,7 +926,7 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
|
||||||
case BinaryNodeType:
|
case BinaryNodeType:
|
||||||
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
||||||
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, CallNodeType, FunctionNodeType,
|
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, CallNodeType, FunctionNodeType,
|
||||||
ReturnNodeType, AccessNodeType, BreakpointNodeType, ImportNodeType, ReferenceNodeType:
|
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unexpected node %s", tree))
|
panic(fmt.Sprintf("unexpected node %s", tree))
|
||||||
|
|
@ -984,10 +995,42 @@ func (c *Compiler) computeBinary(n *BinaryNode) (Value, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l.Type() != r.Type() {
|
||||||
|
return nil, c.error(fmt.Sprintf("cannot perform binary %s on different types %s and %s", n.BinaryOperation, l.Type(), r.Type()), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform type check
|
||||||
|
switch n.BinaryOperation {
|
||||||
|
case BinaryAddition:
|
||||||
|
if l.Type() != StringValueType && l.Type() != NumberValueType && l.Type() != ListValueType {
|
||||||
|
return nil, c.error(fmt.Sprintf("cannot add values of type %s", l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinarySubtraction, BinaryMultiplication, BinaryDivision, BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
|
if l.Type() != NumberValueType {
|
||||||
|
return nil, c.error(fmt.Sprintf("cannot do binary %s on non-number type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryAnd, BinaryOr:
|
||||||
|
if l.Type() != BoolValueType {
|
||||||
|
return nil, c.error(fmt.Sprintf("cannot do binary %s on non-boolean type %s", n.BinaryOperation, l.Type()), n)
|
||||||
|
}
|
||||||
|
case BinaryEquality, BinaryInequality:
|
||||||
|
// can compare all types with themselves
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
var v interface{}
|
var v interface{}
|
||||||
switch n.BinaryOperation {
|
switch n.BinaryOperation {
|
||||||
case BinaryAddition:
|
case BinaryAddition:
|
||||||
|
switch l.Type() {
|
||||||
|
case NumberValueType:
|
||||||
v = l.(*NumberValue).Number + r.(*NumberValue).Number
|
v = l.(*NumberValue).Number + r.(*NumberValue).Number
|
||||||
|
case StringValueType:
|
||||||
|
v = l.(*StringValue).Text + r.(*StringValue).Text
|
||||||
|
case ListValueType:
|
||||||
|
v = append(l.(*ListValue).Items, r.(*ListValue).Items...)
|
||||||
|
default:
|
||||||
|
return nil, c.error(fmt.Sprintf("cannot perform binary add on type %s", l.Type()), n)
|
||||||
|
}
|
||||||
case BinarySubtraction:
|
case BinarySubtraction:
|
||||||
v = l.(*NumberValue).Number - r.(*NumberValue).Number
|
v = l.(*NumberValue).Number - r.(*NumberValue).Number
|
||||||
case BinaryMultiplication:
|
case BinaryMultiplication:
|
||||||
|
|
@ -1058,20 +1101,47 @@ func (c *Compiler) warn(msg string, causer Node) {
|
||||||
c.Warnings = append(c.Warnings, c.error(msg, causer))
|
c.Warnings = append(c.Warnings, c.error(msg, causer))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) resolveImport(path string) Node {
|
func (c *Compiler) resolveImport(path string) error {
|
||||||
if chunk, ok := c.imports[path]; ok {
|
// if already imported and available
|
||||||
return chunk
|
for _, i := range c.imports {
|
||||||
|
if c.resolver.IsSame(path, i) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find tree
|
src, err := c.resolver.Resolve(path)
|
||||||
tree, err := c.resolver.Resolve(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.imports[path] = tree
|
l := NewLexer(src)
|
||||||
|
tokens, err := l.Tokenize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return tree
|
parser := NewParser(src, tokens)
|
||||||
|
p, err := parser.Parse()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldChunk := c.Chunk
|
||||||
|
oldSrc := c.source
|
||||||
|
|
||||||
|
c.Chunk = NewChunk([]Bytecode{}, []Value{})
|
||||||
|
c.source = []rune(src)
|
||||||
|
|
||||||
|
if err := c.Compile(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.imports[path] = c.Chunk
|
||||||
|
|
||||||
|
c.Chunk = oldChunk
|
||||||
|
c.source = oldSrc
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) SetImportsResolver(resolver ImportsResolver) {
|
func (c *Compiler) SetImportsResolver(resolver ImportsResolver) {
|
||||||
|
|
@ -1087,7 +1157,7 @@ func (c *Compiler) addU16(v uint16) {
|
||||||
c.add(Bytecode(v & 0xff)) // last 8 bits
|
c.add(Bytecode(v & 0xff)) // last 8 bits
|
||||||
}
|
}
|
||||||
|
|
||||||
// putU16 put a unsigned 16-bit value at an arbitrary position.
|
// putU16 put an unsigned 16-bit value at an arbitrary position.
|
||||||
// p is the position before the value
|
// p is the position before the value
|
||||||
func (c *Compiler) putU16(p Pos, v uint16) {
|
func (c *Compiler) putU16(p Pos, v uint16) {
|
||||||
// save original position
|
// save original position
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,7 @@ func TestCompile(t *testing.T) {
|
||||||
c := NewCompiler([]rune(testCase.tree.String()))
|
c := NewCompiler([]rune(testCase.tree.String()))
|
||||||
|
|
||||||
t.Log("Compiling node tree")
|
t.Log("Compiling node tree")
|
||||||
err := c.Compile(testCase.tree)
|
err := c.compile(testCase.tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -529,7 +529,7 @@ func BenchmarkCompile(b *testing.B) {
|
||||||
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([]rune{})
|
c := NewCompiler([]rune{})
|
||||||
_ = c.Compile(testCase.tree)
|
_ = c.compile(testCase.tree)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -573,7 +573,7 @@ func TestCompiler_CleanStack(t *testing.T) {
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
c := NewCompiler([]rune(tc.tree.String()))
|
c := NewCompiler([]rune(tc.tree.String()))
|
||||||
err := c.Compile(tc.tree)
|
err := c.compile(tc.tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling failed: %v", err)
|
t.Fatalf("Compiling failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ const (
|
||||||
FunctionNodeType
|
FunctionNodeType
|
||||||
ReturnNodeType
|
ReturnNodeType
|
||||||
AccessNodeType
|
AccessNodeType
|
||||||
ImportNodeType
|
|
||||||
BreakpointNodeType
|
BreakpointNodeType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -70,8 +69,6 @@ func (n NodeType) String() string {
|
||||||
return "Access"
|
return "Access"
|
||||||
case BreakpointNodeType:
|
case BreakpointNodeType:
|
||||||
return "Breakpoint"
|
return "Breakpoint"
|
||||||
case ImportNodeType:
|
|
||||||
return "Import"
|
|
||||||
case UnaryNodeType:
|
case UnaryNodeType:
|
||||||
return "Unary"
|
return "Unary"
|
||||||
}
|
}
|
||||||
|
|
@ -405,25 +402,6 @@ func (n BlockNode) Bounds() (Pos, Pos) {
|
||||||
return n.start, n.end
|
return n.start, n.end
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImportNode struct {
|
|
||||||
path string
|
|
||||||
|
|
||||||
start Pos
|
|
||||||
end Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n ImportNode) Type() NodeType {
|
|
||||||
return ImportNodeType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n ImportNode) String() string {
|
|
||||||
return fmt.Sprintf("import %s", n.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n ImportNode) Bounds() (Pos, Pos) {
|
|
||||||
return n.start, n.end
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConditionalNode conditionals (if statements)
|
// ConditionalNode conditionals (if statements)
|
||||||
type ConditionalNode struct {
|
type ConditionalNode struct {
|
||||||
condition Node
|
condition Node
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,15 @@ 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 {
|
||||||
|
|
@ -18,7 +24,8 @@ func (p ParsingError) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -62,20 +69,44 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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() (*Program, error) {
|
||||||
|
imports := make([]string, 0)
|
||||||
|
|
||||||
// top level statements
|
// top level statements
|
||||||
statements := make([]Node, 0)
|
statements := make([]Node, 0)
|
||||||
|
|
||||||
|
|
@ -83,6 +114,14 @@ 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); 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 {
|
||||||
|
|
@ -92,8 +131,13 @@ func (p *Parser) Parse() (Node, error) {
|
||||||
statements = append(statements, b)
|
statements = append(statements, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BlockNode{
|
return &Program{
|
||||||
statements: statements,
|
imports,
|
||||||
|
&BlockNode{
|
||||||
|
statements,
|
||||||
|
0,
|
||||||
|
p.curr.Start + p.curr.Length,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,6 +185,7 @@ func (p *Parser) error(error string, causer *Token) error {
|
||||||
return ParsingError{
|
return ParsingError{
|
||||||
Description: error,
|
Description: error,
|
||||||
Causer: causer,
|
Causer: causer,
|
||||||
|
Source: p.source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -643,22 +688,6 @@ func (p *Parser) statement() (Node, error) {
|
||||||
return p.condition()
|
return p.condition()
|
||||||
}
|
}
|
||||||
|
|
||||||
case TokenImport:
|
|
||||||
p.advance()
|
|
||||||
start := p.prev.Start
|
|
||||||
|
|
||||||
if err := p.expect(TokenString); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
path := p.prev.Lexeme[1 : len(p.prev.Lexeme)-1]
|
|
||||||
|
|
||||||
return &ImportNode{
|
|
||||||
path,
|
|
||||||
start,
|
|
||||||
p.prev.Start + p.prev.Length,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
case TokenFunc:
|
case TokenFunc:
|
||||||
p.advance()
|
p.advance()
|
||||||
|
|
||||||
|
|
@ -867,13 +896,19 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
var s TypeSignature
|
var s TypeSignature
|
||||||
|
|
||||||
if p.accept(TokenFunc) {
|
if p.accept(TokenFunc) {
|
||||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
if err := p.expect(TokenOpenParenthesis); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var in []TypeSignature
|
var in []TypeSignature
|
||||||
|
|
||||||
for !p.accept(TokenCloseParenthesis) && (len(in) == 0 || p.accept(TokenComma)) {
|
for !p.accept(TokenCloseParenthesis) {
|
||||||
|
if len(in) > 0 {
|
||||||
|
if err := p.expect(TokenComma); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sig, err := p.parseSignature()
|
sig, err := p.parseSignature()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -883,10 +918,6 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
in = append(in, sig)
|
in = append(in, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.expect(TokenCloseParenthesis); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := p.parseSignature()
|
out, err := p.parseSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -896,8 +927,7 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
in,
|
in,
|
||||||
out,
|
out,
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if err := p.expect(TokenName); err != nil {
|
if err := p.expect(TokenName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -930,6 +960,10 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
|
|
||||||
case "any":
|
case "any":
|
||||||
s = &AnySignature{}
|
s = &AnySignature{}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, p.error("unsupported type: "+name, p.prev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.accept(TokenPipe) {
|
if p.accept(TokenPipe) {
|
||||||
|
|
@ -943,9 +977,5 @@ func (p *Parser) parseSignature() (TypeSignature, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return nil, p.error("unsupported type: "+name, p.prev)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -319,24 +318,26 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||||
"split",
|
"split",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
[]TypeSignature{&StringSignature{}},
|
[]TypeSignature{&StringSignature{}},
|
||||||
&NilSignature{},
|
&ListSignature{
|
||||||
|
&StringSignature{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
func(vm *VM, this Value, v []Value) (Value, error) {
|
func(vm *VM, this Value, v []Value) (Value, error) {
|
||||||
str := this.(*StringValue).String()
|
str := this.(*StringValue).String()
|
||||||
sep := v[0].(*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,
|
true,
|
||||||
|
|
@ -460,47 +461,6 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
"map": {
|
|
||||||
"map",
|
|
||||||
&FunctionSignature{
|
|
||||||
[]TypeSignature{
|
|
||||||
&FunctionSignature{
|
|
||||||
[]TypeSignature{
|
|
||||||
&AnySignature{},
|
|
||||||
},
|
|
||||||
&AnySignature{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&ListSignature{},
|
|
||||||
},
|
|
||||||
func(vm *VM, value Value, m []Value) (Value, error) {
|
|
||||||
list := value.(*ListValue)
|
|
||||||
|
|
||||||
v := m[0]
|
|
||||||
var f Value
|
|
||||||
switch a := v.(type) {
|
|
||||||
case *FunctionValue, *BuiltinFunctionValue:
|
|
||||||
f = a
|
|
||||||
default:
|
|
||||||
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,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
"reduce": {
|
"reduce": {
|
||||||
"reduce",
|
"reduce",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
|
|
|
||||||
27
core/vm.go
27
core/vm.go
|
|
@ -102,6 +102,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
|
||||||
|
|
@ -187,6 +189,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"
|
||||||
}
|
}
|
||||||
|
|
@ -377,6 +381,21 @@ var DefaultGlobals = map[string]Value{
|
||||||
nil,
|
nil,
|
||||||
true,
|
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",
|
||||||
&FunctionSignature{
|
&FunctionSignature{
|
||||||
|
|
@ -710,6 +729,14 @@ func (vm *VM) Next() bool {
|
||||||
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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -560,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},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
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
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/zsh
|
#!/bin/bash
|
||||||
|
|
||||||
echo '=== Building CLI ==='
|
echo '=== Building CLI ==='
|
||||||
cd cli || exit 1
|
cd cli || exit 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"])
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ while n < fibonacci_numbers.length() {
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# return to start of line
|
# return to start of line (with carriage return \r)
|
||||||
print(format("%[%D", [char(0x1B), n]))
|
print(char(0x0D))
|
||||||
|
|
||||||
x := 0
|
x := 0
|
||||||
while x < fibonacci_numbers.length() {
|
while x < fibonacci_numbers.length() {
|
||||||
|
|
|
||||||
34
wasm/wasm.go
34
wasm/wasm.go
|
|
@ -13,32 +13,18 @@ type JsResolver struct {
|
||||||
jsResolver js.Value
|
jsResolver js.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *JsResolver) Resolve(name string) (core.Node, error) {
|
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{} {
|
||||||
|
|
@ -68,14 +54,14 @@ 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()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var e core.ParsingError
|
var e core.FormatedError
|
||||||
if errors.As(err, &e) {
|
if errors.As(err, &e) {
|
||||||
errorHandler.Invoke(e.Format([]rune(source)))
|
errorHandler.Invoke(e.Format())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
errorHandler.Invoke(err.Error())
|
errorHandler.Invoke(err.Error())
|
||||||
|
|
@ -86,7 +72,7 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
|
|
||||||
compiler := core.NewCompiler([]rune(source))
|
compiler := core.NewCompiler([]rune(source))
|
||||||
|
|
||||||
log.Printf("Set imports resolver", tree.String())
|
log.Println("Set imports resolver")
|
||||||
|
|
||||||
compiler.SetImportsResolver(&JsResolver{
|
compiler.SetImportsResolver(&JsResolver{
|
||||||
resolver,
|
resolver,
|
||||||
|
|
@ -118,8 +104,8 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
vm.SetGlobal("write", &core.BuiltinFunctionValue{
|
vm.SetGlobal("write", &core.BuiltinFunctionValue{
|
||||||
Name: "write",
|
Name: "write",
|
||||||
Signature: &core.FunctionSignature{
|
Signature: &core.FunctionSignature{
|
||||||
[]core.TypeSignature{&core.StringSignature{}},
|
In: []core.TypeSignature{&core.StringSignature{}},
|
||||||
&core.NilSignature{},
|
Out: &core.NilSignature{},
|
||||||
},
|
},
|
||||||
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
|
F: func(vm *core.VM, this core.Value, args []core.Value) (core.Value, error) {
|
||||||
s := args[0].String()
|
s := args[0].String()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue