improve errors, add warnings, add some compile-time optimizations, fix variables, make sure conditionals and loops get booleans, affirm types, keep variables to a single type
This commit is contained in:
parent
e34b423967
commit
b1b8e61f57
14 changed files with 435 additions and 118 deletions
28
cli/main.go
28
cli/main.go
|
|
@ -14,6 +14,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"`
|
||||||
}
|
}
|
||||||
|
|
@ -30,9 +31,9 @@ func (r *WorkingDirectoryResolver) Resolve(path string) (core.Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
str := string(f)
|
src := string(f)
|
||||||
|
|
||||||
l := core.NewLexer(str)
|
l := core.NewLexer(src)
|
||||||
|
|
||||||
tokens, err := l.Tokenize()
|
tokens, err := l.Tokenize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -107,7 +108,7 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
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")
|
||||||
|
|
@ -125,11 +126,19 @@ func (cmd *RunCmd) Run(ctx *Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var e core.CompilerError
|
var e core.CompilerError
|
||||||
if errors.As(err, &e) {
|
if errors.As(err, &e) {
|
||||||
log.Fatal(e.Format([]rune(src)))
|
log.Fatal(e.Format())
|
||||||
}
|
}
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there were non-critical warnings, report them
|
||||||
|
if !cmd.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)")
|
||||||
|
}
|
||||||
|
|
||||||
chunk = c.Chunk
|
chunk = c.Chunk
|
||||||
} else {
|
} else {
|
||||||
if ctx.Debug {
|
if ctx.Debug {
|
||||||
|
|
@ -216,7 +225,7 @@ func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
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 import resolver")
|
log.Println("Setting import resolver")
|
||||||
|
|
@ -235,11 +244,18 @@ func (cmd *CompileCmd) Run(ctx *Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var e core.CompilerError
|
var e core.CompilerError
|
||||||
if errors.As(err, &e) {
|
if errors.As(err, &e) {
|
||||||
log.Fatal(e.Format([]rune(src)))
|
log.Fatal(e.Format())
|
||||||
}
|
}
|
||||||
log.Fatal(err)
|
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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"func": {
|
"func": {
|
||||||
"func sum(a: number, b: number) {\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",
|
||||||
|
|
@ -54,6 +54,21 @@ func GetAllTestCases() map[string]AllTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"list": {
|
||||||
|
"a := [1, 2]",
|
||||||
|
[]Value{
|
||||||
|
&VariableValue{
|
||||||
|
"a",
|
||||||
|
&ListValue{
|
||||||
|
[]Value{
|
||||||
|
&NumberValue{1},
|
||||||
|
&NumberValue{2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +99,7 @@ func TestAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -119,7 +134,7 @@ func BenchmarkAll(b *testing.B) {
|
||||||
p := NewParser(tokens)
|
p := NewParser(tokens)
|
||||||
tree, _ := p.Parse()
|
tree, _ := p.Parse()
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
||||||
174
core/compiler.go
174
core/compiler.go
|
|
@ -12,6 +12,8 @@ type Compiler struct {
|
||||||
|
|
||||||
imports map[string]Node
|
imports map[string]Node
|
||||||
resolver ImportsResolver
|
resolver ImportsResolver
|
||||||
|
source []rune
|
||||||
|
Warnings []CompilerError
|
||||||
|
|
||||||
stack *Stack[LocalVariable]
|
stack *Stack[LocalVariable]
|
||||||
}
|
}
|
||||||
|
|
@ -29,25 +31,29 @@ type LocalVariable struct {
|
||||||
type CompilerError struct {
|
type CompilerError struct {
|
||||||
Description string
|
Description string
|
||||||
Causer Node
|
Causer Node
|
||||||
|
Source []rune
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e CompilerError) Error() string {
|
func (e CompilerError) Error() string {
|
||||||
return e.Description
|
return e.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e CompilerError) Format(src []rune) string {
|
func (e CompilerError) Format() string {
|
||||||
b := strings.Builder{}
|
b := strings.Builder{}
|
||||||
|
|
||||||
|
src := e.Source
|
||||||
|
|
||||||
b.WriteString(e.Description)
|
b.WriteString(e.Description)
|
||||||
b.WriteString("\n")
|
|
||||||
|
|
||||||
// highlight offending area
|
// highlight offending area
|
||||||
start, end := e.Causer.Bounds()
|
start, end := e.Causer.Bounds()
|
||||||
|
|
||||||
|
lineEnd := 0
|
||||||
lineStart := 0
|
lineStart := 0
|
||||||
line := 1
|
line := 1
|
||||||
pos := 0
|
pos := 0
|
||||||
for i := Pos(0); i < start; i++ {
|
|
||||||
|
for i := Pos(0); i <= start; i++ {
|
||||||
pos++
|
pos++
|
||||||
|
|
||||||
if src[i] == '\n' {
|
if src[i] == '\n' {
|
||||||
|
|
@ -57,7 +63,9 @@ func (e CompilerError) Format(src []rune) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lineEnd := lineStart
|
for lineEnd < int(end) {
|
||||||
|
b.WriteString("\n")
|
||||||
|
lineEnd = lineStart
|
||||||
for lineEnd < len(src) {
|
for lineEnd < len(src) {
|
||||||
lineEnd++
|
lineEnd++
|
||||||
if src[lineEnd] == '\n' {
|
if src[lineEnd] == '\n' {
|
||||||
|
|
@ -65,29 +73,41 @@ func (e CompilerError) Format(src []rune) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lineDescriptor := fmt.Sprintf("%d:%d~%d", line, pos, int(end-start)+pos)
|
begin := max(0, int(start)-lineStart)
|
||||||
|
length := int(min(end, Pos(lineEnd)) - max(start, Pos(lineStart)))
|
||||||
|
|
||||||
|
lineDescriptor := fmt.Sprintf("%d:%d~%d",
|
||||||
|
line,
|
||||||
|
begin,
|
||||||
|
begin+length,
|
||||||
|
)
|
||||||
|
|
||||||
b.WriteString(lineDescriptor)
|
b.WriteString(lineDescriptor)
|
||||||
b.WriteString("\t | ")
|
b.WriteString(" | ")
|
||||||
|
|
||||||
b.WriteString(string(src[lineStart+1 : lineEnd]))
|
b.WriteString(string(src[lineStart+1 : lineEnd]))
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
|
||||||
b.WriteString(strings.Repeat(" ", len(lineDescriptor)))
|
b.WriteString(strings.Repeat(" ", len(lineDescriptor)))
|
||||||
b.WriteString("\t ")
|
b.WriteString(" ")
|
||||||
b.WriteString(strings.Repeat(" ", int(start)-lineStart-1))
|
b.WriteString(strings.Repeat(" ", max(int(start)-lineStart, 0)))
|
||||||
b.WriteString(strings.Repeat("^", int(end-start)))
|
b.WriteString(strings.Repeat("^", length))
|
||||||
|
|
||||||
|
lineStart = lineEnd
|
||||||
|
line++
|
||||||
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompiler() *Compiler {
|
func NewCompiler(source []rune) *Compiler {
|
||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
NewChunk(make([]Bytecode, 0), make([]Value, 0)),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
make(map[string]Node),
|
make(map[string]Node),
|
||||||
nil,
|
nil,
|
||||||
|
source,
|
||||||
|
[]CompilerError{},
|
||||||
NewStack[LocalVariable](256),
|
NewStack[LocalVariable](256),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,20 +222,29 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.add(InstructionNil)
|
c.add(InstructionNil)
|
||||||
|
|
||||||
case BlockNodeType:
|
case BlockNodeType:
|
||||||
c.descend()
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.ascend()
|
c.addAscend()
|
||||||
|
|
||||||
case ConditionalNodeType:
|
case ConditionalNodeType:
|
||||||
n := tree.(*ConditionalNode)
|
n := tree.(*ConditionalNode)
|
||||||
|
|
||||||
|
// make sure condition is boolean
|
||||||
|
sig, err := c.deduceSignature(n.condition)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sig.Type() != TypeBoolean {
|
||||||
|
return c.error(fmt.Sprintf("condition cannot give non-boolean type %s", sig), n.condition)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
|
@ -256,8 +285,17 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
case LoopNodeType:
|
case LoopNodeType:
|
||||||
n := tree.(*LoopNode)
|
n := tree.(*LoopNode)
|
||||||
|
|
||||||
|
// make sure condition is boolean
|
||||||
|
sig, err := c.deduceSignature(n.condition)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sig.Type() != TypeBoolean {
|
||||||
|
return c.error(fmt.Sprintf("cannot loop over value of type %s; requires boolean", sig), n.condition)
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
@ -307,6 +345,14 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !n.keep && f.Out.Type() != TypeNil {
|
||||||
|
c.warn(fmt.Sprintf("shouldn't void result of function call (output is non-nil %s)", f.Out), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.args) != len(f.In) {
|
||||||
|
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
|
||||||
|
}
|
||||||
|
|
||||||
for i, arg := range n.args {
|
for i, arg := range n.args {
|
||||||
sig, err := c.deduceSignature(arg)
|
sig, err := c.deduceSignature(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -365,6 +411,10 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
c.registerVar(p.Name, p.Signature)
|
c.registerVar(p.Name, p.Signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.affirmReturnSignature(n.logic, n.yield); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = c.Compile(n.logic)
|
err = c.Compile(n.logic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -377,6 +427,7 @@ func (c *Compiler) Compile(tree Node) error {
|
||||||
mc.Constants[fi] = &FunctionValue{
|
mc.Constants[fi] = &FunctionValue{
|
||||||
n.name,
|
n.name,
|
||||||
n.parameters,
|
n.parameters,
|
||||||
|
n.yield,
|
||||||
c.Chunk,
|
c.Chunk,
|
||||||
nil,
|
nil,
|
||||||
}
|
}
|
||||||
|
|
@ -493,18 +544,13 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
return &NumberSignature{}, nil
|
return &NumberSignature{}, nil
|
||||||
case ReferenceNodeType:
|
case ReferenceNodeType:
|
||||||
n := tree.(*ReferenceNode)
|
n := tree.(*ReferenceNode)
|
||||||
if c.isGlobal(n.name) {
|
sig, err := c.getVarSignature(n.name, n)
|
||||||
return SignatureOf(DefaultGlobals[n.name]), nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := c.stack.Current - 1; i >= 0; i-- {
|
return sig, nil
|
||||||
v := c.stack.items[i]
|
|
||||||
if v.name == n.name {
|
|
||||||
return v.signature, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, c.error(fmt.Sprintf("variable %s not defined", n.name), n)
|
|
||||||
case BooleanNodeType:
|
case BooleanNodeType:
|
||||||
return &BooleanSignature{}, nil
|
return &BooleanSignature{}, nil
|
||||||
case NilNodeType:
|
case NilNodeType:
|
||||||
|
|
@ -564,7 +610,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
}
|
}
|
||||||
case BinaryAnd, BinaryOr:
|
case BinaryAnd, BinaryOr:
|
||||||
if l.Type() != TypeBoolean {
|
if l.Type() != TypeBoolean {
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform binary %s on type %s", l, n.BinaryOperation), n)
|
return nil, c.error(fmt.Sprintf("cannot perform binary %s on non-boolean type %s", n.BinaryOperation, l), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BooleanSignature{}, nil
|
return &BooleanSignature{}, nil
|
||||||
|
|
@ -572,7 +618,7 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
return &BooleanSignature{}, nil
|
return &BooleanSignature{}, nil
|
||||||
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
case BinaryLess, BinaryGreater, BinaryLessEqual, BinaryGreaterEqual:
|
||||||
if l.Type() != TypeNumber {
|
if l.Type() != TypeNumber {
|
||||||
return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on type %s", l, n.BinaryOperation), n)
|
return nil, c.error(fmt.Sprintf("cannot perform number comparison (%s) on non-number type %s", n.BinaryOperation, l), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BooleanSignature{}, nil
|
return &BooleanSignature{}, nil
|
||||||
|
|
@ -588,15 +634,30 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
|
|
||||||
switch sig.Type() {
|
switch sig.Type() {
|
||||||
case TypeString:
|
case TypeString:
|
||||||
return SignatureOf(StringPrototype[n.property]), nil
|
v := StringPrototype[n.property]
|
||||||
|
if v == nil {
|
||||||
|
return nil, c.error(fmt.Sprintf("string has no property %s", n.property), tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SignatureOf(v), nil
|
||||||
case TypeList:
|
case TypeList:
|
||||||
return SignatureOf(ListPrototype[n.property]), nil
|
v := ListPrototype[n.property]
|
||||||
|
if v == nil {
|
||||||
|
return nil, c.error(fmt.Sprintf("list has no property %s", n.property), tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SignatureOf(v), nil
|
||||||
case TypeObject:
|
case TypeObject:
|
||||||
if v, ok := ObjectPrototype[n.property]; ok {
|
if v, ok := ObjectPrototype[n.property]; ok {
|
||||||
return SignatureOf(v), nil
|
return SignatureOf(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return sig.(*ObjectSignature).Members[n.property], nil
|
v := sig.(*ObjectSignature).Members[n.property]
|
||||||
|
if v == nil {
|
||||||
|
return nil, c.error(fmt.Sprintf("object has no property %s", n.property), tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
||||||
|
|
@ -674,14 +735,31 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) getVarSignature(name string, causer Node) (TypeSignature, error) {
|
||||||
|
if c.isGlobal(name) {
|
||||||
|
return SignatureOf(DefaultGlobals[name]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := c.stack.Current - 1; i >= 0; i-- {
|
||||||
|
v := c.stack.items[i]
|
||||||
|
if v.name == name {
|
||||||
|
return v.signature, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, c.error(fmt.Sprintf("variable %s not defined", name), causer)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
switch tree.Type() {
|
switch tree.Type() {
|
||||||
case BlockNodeType:
|
case BlockNodeType:
|
||||||
|
c.descend()
|
||||||
for _, stmt := range tree.(*BlockNode).statements {
|
for _, stmt := range tree.(*BlockNode).statements {
|
||||||
if err := c.affirmReturnSignature(stmt, sig); err != nil {
|
if err := c.affirmReturnSignature(stmt, sig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.ascend()
|
||||||
|
|
||||||
case ReturnNodeType:
|
case ReturnNodeType:
|
||||||
n := tree.(*ReturnNode)
|
n := tree.(*ReturnNode)
|
||||||
|
|
@ -713,6 +791,33 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
||||||
if err := c.affirmReturnSignature(n.do, sig); err != nil {
|
if err := c.affirmReturnSignature(n.do, sig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AssignNodeType:
|
||||||
|
n := tree.(*AssignNode)
|
||||||
|
|
||||||
|
if !n.declare {
|
||||||
|
prev, err := c.getVarSignature(n.name, n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := c.deduceSignature(n.value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sig.Matches(prev) {
|
||||||
|
return c.error(fmt.Sprintf("cannot assign value of type %s to variable of type %s", prev, sig), n.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := c.deduceSignature(n.value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.registerVar(n.name, sig)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -902,6 +1007,10 @@ func (c *Compiler) ascend() {
|
||||||
|
|
||||||
for ; c.stack.Current > 0 && c.stack.Peek().scope > int(c.scope); c.stack.Pop() {
|
for ; c.stack.Current > 0 && c.stack.Peek().scope > int(c.scope); c.stack.Pop() {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) addAscend() {
|
||||||
|
c.ascend()
|
||||||
|
|
||||||
if c.scope != 0 {
|
if c.scope != 0 {
|
||||||
c.add(InstructionAscend)
|
c.add(InstructionAscend)
|
||||||
|
|
@ -910,6 +1019,10 @@ func (c *Compiler) ascend() {
|
||||||
|
|
||||||
func (c *Compiler) descend() {
|
func (c *Compiler) descend() {
|
||||||
c.scope++
|
c.scope++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) addDescend() {
|
||||||
|
c.descend()
|
||||||
if c.scope != 1 {
|
if c.scope != 1 {
|
||||||
c.add(InstructionDescend)
|
c.add(InstructionDescend)
|
||||||
}
|
}
|
||||||
|
|
@ -919,9 +1032,14 @@ func (c *Compiler) error(msg string, causer Node) CompilerError {
|
||||||
return CompilerError{
|
return CompilerError{
|
||||||
msg,
|
msg,
|
||||||
causer,
|
causer,
|
||||||
|
c.source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Compiler) warn(msg string, causer Node) {
|
||||||
|
c.Warnings = append(c.Warnings, c.error(msg, causer))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Compiler) resolveImport(path string) Node {
|
func (c *Compiler) resolveImport(path string) Node {
|
||||||
if chunk, ok := c.imports[path]; ok {
|
if chunk, ok := c.imports[path]; ok {
|
||||||
return chunk
|
return chunk
|
||||||
|
|
|
||||||
|
|
@ -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,7 +23,7 @@ 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{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,7 +267,8 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
&NumberValue{3},
|
&NumberValue{3},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"sum_function": {&BlockNode{
|
"sum_function": {
|
||||||
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
"sum",
|
"sum",
|
||||||
|
|
@ -328,6 +329,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
&NumberSignature{},
|
&NumberSignature{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&NumberSignature{},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionDescend,
|
InstructionDescend,
|
||||||
|
|
@ -355,7 +357,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
&FunctionNode{
|
&FunctionNode{
|
||||||
"a",
|
"a",
|
||||||
[]FunctionParameter{},
|
[]FunctionParameter{},
|
||||||
&NilSignature{},
|
&NumberSignature{},
|
||||||
&BlockNode{
|
&BlockNode{
|
||||||
[]Node{
|
[]Node{
|
||||||
&AssignNode{
|
&AssignNode{
|
||||||
|
|
@ -400,6 +402,7 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
&FunctionValue{
|
&FunctionValue{
|
||||||
"a",
|
"a",
|
||||||
[]FunctionParameter{},
|
[]FunctionParameter{},
|
||||||
|
&NumberSignature{},
|
||||||
NewChunk(
|
NewChunk(
|
||||||
[]Bytecode{
|
[]Bytecode{
|
||||||
InstructionDescend,
|
InstructionDescend,
|
||||||
|
|
@ -419,6 +422,54 @@ func GetCompileTestData() map[string]CompileTestData {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"two_lists": {
|
||||||
|
tree: &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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +499,7 @@ 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.tree.String()))
|
||||||
|
|
||||||
t.Log("Compiling node tree")
|
t.Log("Compiling node tree")
|
||||||
err := c.Compile(testCase.tree)
|
err := c.Compile(testCase.tree)
|
||||||
|
|
@ -477,7 +528,7 @@ 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.tree)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -487,7 +538,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) {
|
||||||
|
|
@ -521,7 +572,7 @@ func TestCompiler_CleanStack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
c := NewCompiler()
|
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)
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,10 @@ 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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
(*p.prev).Lexeme[1 : len((*p.prev).Lexeme)-1],
|
||||||
(*p.prev).Lexeme,
|
(*p.prev).Lexeme,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.Length,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNumber:
|
case TokenNumber:
|
||||||
|
|
@ -162,7 +162,7 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &NumberNode{
|
return &NumberNode{
|
||||||
num,
|
num,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.Length,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenHexadecimal:
|
case TokenHexadecimal:
|
||||||
|
|
@ -184,14 +184,14 @@ func (p *Parser) factor() (Node, error) {
|
||||||
return &BooleanNode{
|
return &BooleanNode{
|
||||||
true,
|
true,
|
||||||
p.prev.Start,
|
p.prev.Start,
|
||||||
p.prev.Length,
|
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,
|
p.prev.Start + p.prev.Length,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case TokenNil:
|
case TokenNil:
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,35 @@ func SignatureOf(v Value) TypeSignature {
|
||||||
case *BoolValue:
|
case *BoolValue:
|
||||||
return &BooleanSignature{}
|
return &BooleanSignature{}
|
||||||
case *ListValue:
|
case *ListValue:
|
||||||
return &ListSignature{}
|
// 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:
|
case *ObjectValue:
|
||||||
return &ObjectSignature{}
|
return &ObjectSignature{}
|
||||||
case *FunctionValue:
|
case *FunctionValue:
|
||||||
return &FunctionSignature{}
|
params := make([]TypeSignature, len(t.Params))
|
||||||
|
|
||||||
|
for i, p := range t.Params {
|
||||||
|
params[i] = p.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FunctionSignature{
|
||||||
|
params,
|
||||||
|
t.Yield,
|
||||||
|
}
|
||||||
case *BuiltinFunctionValue:
|
case *BuiltinFunctionValue:
|
||||||
return t.Signature
|
return t.Signature
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +292,10 @@ func (s *FunctionSignature) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString(")")
|
b.WriteString(")")
|
||||||
|
if s.Out.Type() != TypeNil {
|
||||||
|
b.WriteString(" ")
|
||||||
b.WriteString(s.Out.String())
|
b.WriteString(s.Out.String())
|
||||||
|
}
|
||||||
|
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ var ObjectPrototype = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,6 +339,17 @@ var StringPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return GoToValue(out), nil
|
return GoToValue(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
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +404,7 @@ func (v *ListValue) Equals(other Value) bool {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
@ -413,6 +425,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"at": {
|
"at": {
|
||||||
"at",
|
"at",
|
||||||
|
|
@ -433,6 +446,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return items[index], nil
|
return items[index], nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"length": {
|
"length": {
|
||||||
"length",
|
"length",
|
||||||
|
|
@ -444,6 +458,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return GoToValue(len(this.(*ListValue).Items)), nil
|
return GoToValue(len(this.(*ListValue).Items)), nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"map": {
|
"map": {
|
||||||
"map",
|
"map",
|
||||||
|
|
@ -484,6 +499,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return list, nil
|
return list, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"reduce": {
|
"reduce": {
|
||||||
"reduce",
|
"reduce",
|
||||||
|
|
@ -516,6 +532,7 @@ var ListPrototype = map[string]*BuiltinFunctionValue{
|
||||||
return sum, nil
|
return sum, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -542,6 +559,7 @@ func (v *ListValue) Clone() Value {
|
||||||
type FunctionValue struct {
|
type FunctionValue struct {
|
||||||
Name string
|
Name string
|
||||||
Params []FunctionParameter
|
Params []FunctionParameter
|
||||||
|
Yield TypeSignature
|
||||||
Chunk *Chunk
|
Chunk *Chunk
|
||||||
Parent Value
|
Parent Value
|
||||||
}
|
}
|
||||||
|
|
@ -572,6 +590,7 @@ func (v *FunctionValue) Clone() Value {
|
||||||
return &FunctionValue{
|
return &FunctionValue{
|
||||||
v.Name,
|
v.Name,
|
||||||
v.Params,
|
v.Params,
|
||||||
|
v.Yield,
|
||||||
v.Chunk,
|
v.Chunk,
|
||||||
v.Parent,
|
v.Parent,
|
||||||
}
|
}
|
||||||
|
|
@ -582,6 +601,7 @@ type BuiltinFunctionValue struct {
|
||||||
Signature *FunctionSignature
|
Signature *FunctionSignature
|
||||||
F func(*VM, Value, []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 {
|
||||||
|
|
@ -611,6 +631,7 @@ func (v *BuiltinFunctionValue) Clone() Value {
|
||||||
v.Signature,
|
v.Signature,
|
||||||
v.F,
|
v.F,
|
||||||
v.Parent,
|
v.Parent,
|
||||||
|
v.Constant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
core/vm.go
22
core/vm.go
|
|
@ -311,6 +311,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"print": &BuiltinFunctionValue{
|
"print": &BuiltinFunctionValue{
|
||||||
"print",
|
"print",
|
||||||
|
|
@ -323,6 +324,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"format": &BuiltinFunctionValue{
|
"format": &BuiltinFunctionValue{
|
||||||
"format",
|
"format",
|
||||||
|
|
@ -356,6 +358,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return GoToValue(b.String()), nil
|
return GoToValue(b.String()), nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
"char": &BuiltinFunctionValue{
|
"char": &BuiltinFunctionValue{
|
||||||
"char",
|
"char",
|
||||||
|
|
@ -372,6 +375,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
"assertEq": &BuiltinFunctionValue{
|
"assertEq": &BuiltinFunctionValue{
|
||||||
"assertEq",
|
"assertEq",
|
||||||
|
|
@ -393,6 +397,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"assertNotEq": &BuiltinFunctionValue{
|
"assertNotEq": &BuiltinFunctionValue{
|
||||||
"assertNotEq",
|
"assertNotEq",
|
||||||
|
|
@ -414,6 +419,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
"str": &BuiltinFunctionValue{
|
"str": &BuiltinFunctionValue{
|
||||||
"str",
|
"str",
|
||||||
|
|
@ -425,6 +431,21 @@ var DefaultGlobals = map[string]Value{
|
||||||
return GoToValue(args[0].String()), nil
|
return GoToValue(args[0].String()), nil
|
||||||
},
|
},
|
||||||
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": &BuiltinFunctionValue{
|
||||||
"exit",
|
"exit",
|
||||||
|
|
@ -437,6 +458,7 @@ var DefaultGlobals = map[string]Value{
|
||||||
return &NilValue{}, nil
|
return &NilValue{}, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
|
|
||||||
|
NAMESPACE := ""
|
||||||
|
|
||||||
|
func namespace(name: string, test: func()) {
|
||||||
|
NAMESPACE = name
|
||||||
|
test()
|
||||||
|
}
|
||||||
|
|
||||||
func assertEqual(a: any, b: any) {
|
func assertEqual(a: any, b: any) {
|
||||||
if a != b {
|
if a != b {
|
||||||
write(format("assertion error: % should (but doesn't) equal %", [a, b]))
|
write(format("assertion error: % should (but doesn't) equal %", [a, b]))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
} else if env("DEBUG") != "" {
|
||||||
|
write(format("assertion success: % equals %", [a, b]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10,5 +19,7 @@ func assertNotEqual(a: any, b: any) {
|
||||||
if a == b {
|
if a == b {
|
||||||
write(format("assertion error: % shouldn't (but does) equal %", [a, b]))
|
write(format("assertion error: % shouldn't (but does) equal %", [a, b]))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
} else if env("DEBUG") != "" {
|
||||||
|
write(format("assertion success: % doesn't equal %", [a, b]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
|
|
||||||
echo '=== Building CLI ==='
|
echo '=== Building CLI ==='
|
||||||
(
|
|
||||||
cd cli || exit 1
|
cd cli || exit 1
|
||||||
if ! go build .; then
|
if ! go build .; then
|
||||||
echo "=== Had error building CLI ==="
|
echo "=== Had error building CLI ==="
|
||||||
|
|
@ -9,10 +8,9 @@ echo '=== Building CLI ==='
|
||||||
else
|
else
|
||||||
echo "=+= Successfully built CLI =+="
|
echo "=+= Successfully built CLI =+="
|
||||||
fi
|
fi
|
||||||
)
|
cd ..
|
||||||
|
|
||||||
echo "=== Running go core tests ==="
|
echo "=== Running go core tests ==="
|
||||||
(
|
|
||||||
cd core || exit 1
|
cd core || exit 1
|
||||||
if ! go test .; then
|
if ! go test .; then
|
||||||
echo "=x= Core testing failed =x= "
|
echo "=x= Core testing failed =x= "
|
||||||
|
|
@ -20,7 +18,7 @@ echo "=== Running go core tests ==="
|
||||||
else
|
else
|
||||||
echo "=+= Successfully ran core tests =+="
|
echo "=+= Successfully ran core tests =+="
|
||||||
fi
|
fi
|
||||||
)
|
cd ..
|
||||||
|
|
||||||
errors=()
|
errors=()
|
||||||
|
|
||||||
|
|
|
||||||
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]")
|
||||||
|
|
@ -77,7 +77,7 @@ func run(_ js.Value, args []js.Value) interface{} {
|
||||||
|
|
||||||
log.Printf("Parsed tree: %s", tree.String())
|
log.Printf("Parsed tree: %s", tree.String())
|
||||||
|
|
||||||
compiler := core.NewCompiler()
|
compiler := core.NewCompiler([]rune(source))
|
||||||
|
|
||||||
compiler.SetImportsResolver(&JsResolver{
|
compiler.SetImportsResolver(&JsResolver{
|
||||||
resolver,
|
resolver,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue