further improvements
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -93,3 +93,4 @@ fabric.properties
|
|||||||
/.vscode
|
/.vscode
|
||||||
/monkey
|
/monkey
|
||||||
/examples/fib
|
/examples/fib
|
||||||
|
vendor
|
||||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
15
.idea/git_toolbox_prj.xml
generated
15
.idea/git_toolbox_prj.xml
generated
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GitToolBoxProjectSettings">
|
|
||||||
<option name="commitMessageIssueKeyValidationOverride">
|
|
||||||
<BoolValueOverride>
|
|
||||||
<option name="enabled" value="true" />
|
|
||||||
</BoolValueOverride>
|
|
||||||
</option>
|
|
||||||
<option name="commitMessageValidationEnabledOverride">
|
|
||||||
<BoolValueOverride>
|
|
||||||
<option name="enabled" value="true" />
|
|
||||||
</BoolValueOverride>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
10
Benchmark.md
10
Benchmark.md
@@ -1,7 +1,7 @@
|
|||||||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|
||||||
|:---|---:|---:|---:|---:|
|
|:---|---:|---:|---:|---:|
|
||||||
| `c` | 36.7 ± 0.6 | 36.0 | 37.8 | 1.00 |
|
| `c` | 37.4 ± 1.1 | 36.2 | 39.4 | 1.00 |
|
||||||
| `go` | 230.2 ± 14.0 | 206.2 | 252.9 | 6.28 ± 0.40 |
|
| `go` | 168.5 ± 27.2 | 120.2 | 202.6 | 4.51 ± 0.74 |
|
||||||
| `python` | 1366.9 ± 30.8 | 1330.9 | 1411.3 | 37.27 ± 1.06 |
|
| `python` | 1346.8 ± 8.2 | 1335.7 | 1361.6 | 36.05 ± 1.09 |
|
||||||
| `tengo` | 1704.3 ± 16.0 | 1687.2 | 1739.3 | 46.47 ± 0.91 |
|
| `tengo` | 1708.6 ± 15.9 | 1686.2 | 1731.4 | 45.74 ± 1.43 |
|
||||||
| `monkey` | 3347.6 ± 22.3 | 3314.0 | 3396.0 | 91.27 ± 1.69 |
|
| `monkey` | 2148.7 ± 6.6 | 2135.6 | 2157.0 | 57.52 ± 1.72 |
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ func main() {
|
|||||||
version bool
|
version bool
|
||||||
simple bool
|
simple bool
|
||||||
debug bool
|
debug bool
|
||||||
|
trace bool
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.BoolVar(&compile, "c", false, "Compile a monkey file into a '.mc' bytecode file")
|
flag.BoolVar(&compile, "c", false, "Compile a monkey file into a '.mc' bytecode file")
|
||||||
flag.BoolVar(&simple, "s", false, "Use simple REPL instead of opening a terminal")
|
flag.BoolVar(&simple, "s", false, "Use simple REPL instead of opening a terminal")
|
||||||
flag.BoolVar(&version, "v", false, "Print Monkey version information")
|
flag.BoolVar(&version, "v", false, "Print Monkey version information")
|
||||||
flag.BoolVar(&debug, "d", false, "Enable debug mode")
|
flag.BoolVar(&debug, "D", false, "Enable Compiler and VM debugging")
|
||||||
|
flag.BoolVar(&trace, "T", false, "Enable VM tracing")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -29,12 +31,12 @@ func main() {
|
|||||||
monkey.PrintVersionInfo(os.Stdout)
|
monkey.PrintVersionInfo(os.Stdout)
|
||||||
|
|
||||||
case flag.NArg() > 0:
|
case flag.NArg() > 0:
|
||||||
monkey.ExecFileVM(flag.Arg(0), flag.Args()[1:], debug)
|
monkey.ExecFileVM(flag.Arg(0), flag.Args()[1:], debug, trace)
|
||||||
|
|
||||||
case simple:
|
case simple:
|
||||||
monkey.SimpleVmREPL(flag.Args(), debug)
|
monkey.SimpleVmREPL(flag.Args(), debug, trace)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
monkey.VmREPL(flag.Args(), debug)
|
monkey.VmREPL(flag.Args(), debug, trace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
compare.sh
Normal file
24
compare.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $(basename "$0") <branch>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
compareWith="$1"
|
||||||
|
currentBranch="$(git branch --show-current)"
|
||||||
|
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "$currentBranch is not clean, please stash or commit your changes!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyperfine \
|
||||||
|
-w 5 \
|
||||||
|
-c "git checkout $currentBranch" \
|
||||||
|
-p "make build" \
|
||||||
|
-p "git checkout $compareWith; make build" \
|
||||||
|
-n "$currentBranch" \
|
||||||
|
-n "$compareWith" \
|
||||||
|
'./monkey examples/fib.monkey' \
|
||||||
|
'./monkey examples/fib.monkey'
|
||||||
1
go.mod
1
go.mod
@@ -3,7 +3,6 @@ module monkey
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/pkg/profile v1.7.0
|
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/tebeka/atexit v0.3.0
|
github.com/tebeka/atexit v0.3.0
|
||||||
golang.org/x/term v0.18.0
|
golang.org/x/term v0.18.0
|
||||||
|
|||||||
15
go.sum
15
go.sum
@@ -1,33 +1,20 @@
|
|||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
|
||||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/tebeka/atexit v0.3.0 h1:jleL99H7Ywt80oJKR+VWmJNnezcCOG0CuzcN3CIpsdI=
|
github.com/tebeka/atexit v0.3.0 h1:jleL99H7Ywt80oJKR+VWmJNnezcCOG0CuzcN3CIpsdI=
|
||||||
github.com/tebeka/atexit v0.3.0/go.mod h1:WJmSUSmMT7WoR7etUOaGBVXk+f5/ZJ+67qwuedq7Fbs=
|
github.com/tebeka/atexit v0.3.0/go.mod h1:WJmSUSmMT7WoR7etUOaGBVXk+f5/ZJ+67qwuedq7Fbs=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
|
||||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -50,5 +50,5 @@ func Bind(args ...object.Object) object.Object {
|
|||||||
return newError("SocketError: %s", err)
|
return newError("SocketError: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.Null{}
|
return object.NULL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Builtins ...
|
// Builtins ...
|
||||||
var Builtins = map[string]*object.Builtin{
|
var Builtins = map[string]object.Builtin{
|
||||||
"len": {Name: "len", Fn: Len},
|
"len": {Name: "len", Fn: Len},
|
||||||
"input": {Name: "input", Fn: Input},
|
"input": {Name: "input", Fn: Input},
|
||||||
"print": {Name: "print", Fn: Print},
|
"print": {Name: "print", Fn: Print},
|
||||||
@@ -58,7 +58,7 @@ var Builtins = map[string]*object.Builtin{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuiltinsIndex ...
|
// BuiltinsIndex ...
|
||||||
var BuiltinsIndex []*object.Builtin
|
var BuiltinsIndex []object.Builtin
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var keys []string
|
var keys []string
|
||||||
|
|||||||
@@ -23,5 +23,5 @@ func Close(args ...object.Object) object.Object {
|
|||||||
return newError("IOError: %s", err)
|
return newError("IOError: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.Null{}
|
return object.NULL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,5 +33,5 @@ func FFI(args ...object.Object) object.Object {
|
|||||||
return newError("error finding symbol: %s", err)
|
return newError("error finding symbol: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &object.Builtin{Name: symbol, Fn: v.(object.BuiltinFunction)}
|
return object.Builtin{Name: symbol, Fn: v.(object.BuiltinFunction)}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ func IdOf(args ...object.Object) object.Object {
|
|||||||
} else if h, ok := arg.(*object.Hash); ok {
|
} else if h, ok := arg.(*object.Hash); ok {
|
||||||
return object.String{Value: fmt.Sprintf("%p", h)}
|
return object.String{Value: fmt.Sprintf("%p", h)}
|
||||||
} else if f, ok := arg.(*object.Function); ok {
|
} else if f, ok := arg.(*object.Function); ok {
|
||||||
return object.String{Value: fmt.Sprintf("%p", f)}
|
return object.String{Value: fmt.Sprintf("%p", &f)}
|
||||||
} else if c, ok := arg.(*object.Closure); ok {
|
} else if c, ok := arg.(*object.Closure); ok {
|
||||||
return object.String{Value: fmt.Sprintf("%p", c)}
|
return object.String{Value: fmt.Sprintf("%p", &c)}
|
||||||
} else if b, ok := arg.(*object.Builtin); ok {
|
} else if b, ok := arg.(object.Builtin); ok {
|
||||||
return object.String{Value: fmt.Sprintf("%p", b)}
|
return object.String{Value: fmt.Sprintf("%p", &b)}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,5 +23,5 @@ func Listen(args ...object.Object) object.Object {
|
|||||||
return newError("SocketError: %s", err)
|
return newError("SocketError: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.Null{}
|
return object.NULL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ func WriteFile(args ...object.Object) object.Object {
|
|||||||
return newError("IOError: error writing file %s: %s", filename, err)
|
return newError("IOError: error writing file %s: %s", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.Null{}
|
return object.NULL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
|
|||||||
case *ast.FunctionLiteral:
|
case *ast.FunctionLiteral:
|
||||||
params := node.Parameters
|
params := node.Parameters
|
||||||
body := node.Body
|
body := node.Body
|
||||||
return &object.Function{Parameters: params, Env: env, Body: body}
|
return object.Function{Parameters: params, Env: env, Body: body}
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
case *ast.IntegerLiteral:
|
case *ast.IntegerLiteral:
|
||||||
@@ -560,12 +560,12 @@ func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Ob
|
|||||||
func applyFunction(fn object.Object, args []object.Object) object.Object {
|
func applyFunction(fn object.Object, args []object.Object) object.Object {
|
||||||
switch fn := fn.(type) {
|
switch fn := fn.(type) {
|
||||||
|
|
||||||
case *object.Function:
|
case object.Function:
|
||||||
extendedEnv := extendFunctionEnv(fn, args)
|
extendedEnv := extendFunctionEnv(fn, args)
|
||||||
evaluated := Eval(fn.Body, extendedEnv)
|
evaluated := Eval(fn.Body, extendedEnv)
|
||||||
return unwrapReturnValue(evaluated)
|
return unwrapReturnValue(evaluated)
|
||||||
|
|
||||||
case *object.Builtin:
|
case object.Builtin:
|
||||||
if result := fn.Fn(args...); result != nil {
|
if result := fn.Fn(args...); result != nil {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -577,7 +577,7 @@ func applyFunction(fn object.Object, args []object.Object) object.Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
|
func extendFunctionEnv(fn object.Function, args []object.Object) *object.Environment {
|
||||||
env := object.NewEnclosedEnvironment(fn.Env)
|
env := object.NewEnclosedEnvironment(fn.Env)
|
||||||
|
|
||||||
for paramIdx, param := range fn.Parameters {
|
for paramIdx, param := range fn.Parameters {
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ func TestFunctionObject(t *testing.T) {
|
|||||||
input := "fn(x) { x + 2; };"
|
input := "fn(x) { x + 2; };"
|
||||||
|
|
||||||
evaluated := testEval(input)
|
evaluated := testEval(input)
|
||||||
fn, ok := evaluated.(*object.Function)
|
fn, ok := evaluated.(object.Function)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated)
|
t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,22 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TRUE = Boolean{Value: true}
|
||||||
|
FALSE = Boolean{Value: false}
|
||||||
|
)
|
||||||
|
|
||||||
type Boolean struct {
|
type Boolean struct {
|
||||||
Value bool
|
Value bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewBoolean(value bool) Boolean {
|
||||||
|
if value {
|
||||||
|
return TRUE
|
||||||
|
}
|
||||||
|
return FALSE
|
||||||
|
}
|
||||||
|
|
||||||
func (b Boolean) Bool() bool {
|
func (b Boolean) Bool() bool {
|
||||||
return b.Value
|
return b.Value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ type Builtin struct {
|
|||||||
Fn BuiltinFunction
|
Fn BuiltinFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builtin) Bool() bool {
|
func (b Builtin) Bool() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builtin) Type() Type {
|
func (b Builtin) Type() Type {
|
||||||
return BuiltinType
|
return BuiltinType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builtin) Inspect() string {
|
func (b Builtin) Inspect() string {
|
||||||
return fmt.Sprintf("<built-in function %s>", b.Name)
|
return fmt.Sprintf("<built-in function %s>", b.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builtin) String() string {
|
func (b Builtin) String() string {
|
||||||
return b.Inspect()
|
return b.Inspect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,17 +27,21 @@ func (i Integer) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i Integer) Add(other Object) (Object, error) {
|
func (i Integer) Add(other Object) (Object, error) {
|
||||||
if !AssertTypes(other, IntegerType) {
|
switch obj := other.(type) {
|
||||||
|
case Integer:
|
||||||
|
return Integer{i.Value + obj.Value}, nil
|
||||||
|
default:
|
||||||
return nil, NewBinaryOpError(i, other, "+")
|
return nil, NewBinaryOpError(i, other, "+")
|
||||||
}
|
}
|
||||||
return Integer{i.Value + other.(Integer).Value}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Integer) Sub(other Object) (Object, error) {
|
func (i Integer) Sub(other Object) (Object, error) {
|
||||||
if !AssertTypes(other, IntegerType) {
|
switch obj := other.(type) {
|
||||||
|
case Integer:
|
||||||
|
return Integer{i.Value - obj.Value}, nil
|
||||||
|
default:
|
||||||
return nil, NewBinaryOpError(i, other, "-")
|
return nil, NewBinaryOpError(i, other, "-")
|
||||||
}
|
}
|
||||||
return Integer{i.Value - other.(Integer).Value}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Integer) Mul(other Object) (Object, error) {
|
func (i Integer) Mul(other Object) (Object, error) {
|
||||||
@@ -115,15 +119,16 @@ func (i Integer) Negate() Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i Integer) Compare(other Object) int {
|
func (i Integer) Compare(other Object) int {
|
||||||
if obj, ok := other.(Integer); ok {
|
switch obj := other.(type) {
|
||||||
switch {
|
case Integer:
|
||||||
case i.Value < obj.Value:
|
if i.Value < obj.Value {
|
||||||
return -1
|
return -1
|
||||||
case i.Value > obj.Value:
|
|
||||||
return 1
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
if i.Value > obj.Value {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
|
var NULL = Null{}
|
||||||
|
|
||||||
type Null struct{}
|
type Null struct{}
|
||||||
|
|
||||||
func (n Null) Bool() bool {
|
func (n Null) Bool() bool {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"monkey/internal/utils"
|
"monkey/internal/utils"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
@@ -20,10 +21,6 @@ const StackSize = 2048
|
|||||||
const GlobalsSize = 65536
|
const GlobalsSize = 65536
|
||||||
const MaxFrames = 1024
|
const MaxFrames = 1024
|
||||||
|
|
||||||
var Null = object.Null{}
|
|
||||||
var True = object.Boolean{Value: true}
|
|
||||||
var False = object.Boolean{Value: false}
|
|
||||||
|
|
||||||
func isTruthy(obj object.Object) bool {
|
func isTruthy(obj object.Object) bool {
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
|
|
||||||
@@ -114,6 +111,7 @@ func (s *VMState) ExportedHash() *object.Hash {
|
|||||||
|
|
||||||
type VM struct {
|
type VM struct {
|
||||||
Debug bool
|
Debug bool
|
||||||
|
Trace bool
|
||||||
|
|
||||||
state *VMState
|
state *VMState
|
||||||
|
|
||||||
@@ -143,9 +141,9 @@ func (vm *VM) popFrame() Frame {
|
|||||||
|
|
||||||
// New constructs a new monkey-lang bytecode virtual machine
|
// New constructs a new monkey-lang bytecode virtual machine
|
||||||
func New(fn string, bytecode *compiler.Bytecode) *VM {
|
func New(fn string, bytecode *compiler.Bytecode) *VM {
|
||||||
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||||
mainClosure := &object.Closure{Fn: mainFn}
|
mainClosure := object.Closure{Fn: &mainFn}
|
||||||
mainFrame := NewFrame(mainClosure, 0)
|
mainFrame := NewFrame(&mainClosure, 0)
|
||||||
|
|
||||||
frames := make([]Frame, MaxFrames)
|
frames := make([]Frame, MaxFrames)
|
||||||
frames[0] = mainFrame
|
frames[0] = mainFrame
|
||||||
@@ -169,9 +167,9 @@ func New(fn string, bytecode *compiler.Bytecode) *VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
||||||
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
|
mainFn := object.CompiledFunction{Instructions: bytecode.Instructions}
|
||||||
mainClosure := &object.Closure{Fn: mainFn}
|
mainClosure := object.Closure{Fn: &mainFn}
|
||||||
mainFrame := NewFrame(mainClosure, 0)
|
mainFrame := NewFrame(&mainClosure, 0)
|
||||||
|
|
||||||
frames := make([]Frame, MaxFrames)
|
frames := make([]Frame, MaxFrames)
|
||||||
frames[0] = mainFrame
|
frames[0] = mainFrame
|
||||||
@@ -222,13 +220,13 @@ func (vm *VM) executeConstant() error {
|
|||||||
func (vm *VM) executeAssignGlobal() error {
|
func (vm *VM) executeAssignGlobal() error {
|
||||||
globalIndex := vm.currentFrame().ReadUint16()
|
globalIndex := vm.currentFrame().ReadUint16()
|
||||||
vm.state.Globals[globalIndex] = vm.pop()
|
vm.state.Globals[globalIndex] = vm.pop()
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeAssignLocal() error {
|
func (vm *VM) executeAssignLocal() error {
|
||||||
localIndex := vm.currentFrame().ReadUint8()
|
localIndex := vm.currentFrame().ReadUint8()
|
||||||
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = vm.pop()
|
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = vm.pop()
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeSetGlobal() error {
|
func (vm *VM) executeSetGlobal() error {
|
||||||
@@ -241,7 +239,7 @@ func (vm *VM) executeSetGlobal() error {
|
|||||||
vm.state.Globals[globalIndex] = ref
|
vm.state.Globals[globalIndex] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeGetGlobal() error {
|
func (vm *VM) executeGetGlobal() error {
|
||||||
@@ -259,7 +257,7 @@ func (vm *VM) executeSetLocal() error {
|
|||||||
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = ref
|
vm.stack[vm.currentFrame().basePointer+int(localIndex)] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeGetLocal() error {
|
func (vm *VM) executeGetLocal() error {
|
||||||
@@ -283,15 +281,15 @@ func (vm *VM) executeCurrentClosure() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeTrue() error {
|
func (vm *VM) executeTrue() error {
|
||||||
return vm.push(True)
|
return vm.push(object.TRUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeFalse() error {
|
func (vm *VM) executeFalse() error {
|
||||||
return vm.push(False)
|
return vm.push(object.FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) executeNull() error {
|
func (vm *VM) executeNull() error {
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
||||||
@@ -547,9 +545,9 @@ func (vm *VM) executeEqual() error {
|
|||||||
if obj, ok := left.(object.Comparable); ok {
|
if obj, ok := left.(object.Comparable); ok {
|
||||||
val := obj.Compare(right)
|
val := obj.Compare(right)
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
return vm.push(True)
|
return vm.push(object.TRUE)
|
||||||
}
|
}
|
||||||
return vm.push(False)
|
return vm.push(object.FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.NewBinaryOpError(left, right, "==")
|
return object.NewBinaryOpError(left, right, "==")
|
||||||
@@ -562,9 +560,9 @@ func (vm *VM) executeNotEqual() error {
|
|||||||
if obj, ok := left.(object.Comparable); ok {
|
if obj, ok := left.(object.Comparable); ok {
|
||||||
val := obj.Compare(right)
|
val := obj.Compare(right)
|
||||||
if val != 0 {
|
if val != 0 {
|
||||||
return vm.push(True)
|
return vm.push(object.TRUE)
|
||||||
}
|
}
|
||||||
return vm.push(False)
|
return vm.push(object.FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.NewBinaryOpError(left, right, "!=")
|
return object.NewBinaryOpError(left, right, "!=")
|
||||||
@@ -577,9 +575,9 @@ func (vm *VM) executeGreaterThan() error {
|
|||||||
if obj, ok := left.(object.Comparable); ok {
|
if obj, ok := left.(object.Comparable); ok {
|
||||||
val := obj.Compare(right)
|
val := obj.Compare(right)
|
||||||
if val == 1 {
|
if val == 1 {
|
||||||
return vm.push(True)
|
return vm.push(object.TRUE)
|
||||||
}
|
}
|
||||||
return vm.push(False)
|
return vm.push(object.FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.NewBinaryOpError(left, right, ">")
|
return object.NewBinaryOpError(left, right, ">")
|
||||||
@@ -592,9 +590,9 @@ func (vm *VM) executeGreaterThanOrEqual() error {
|
|||||||
if obj, ok := left.(object.Comparable); ok {
|
if obj, ok := left.(object.Comparable); ok {
|
||||||
val := obj.Compare(right)
|
val := obj.Compare(right)
|
||||||
if val >= 0 {
|
if val >= 0 {
|
||||||
return vm.push(True)
|
return vm.push(object.TRUE)
|
||||||
}
|
}
|
||||||
return vm.push(False)
|
return vm.push(object.FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object.NewBinaryOpError(left, right, ">")
|
return object.NewBinaryOpError(left, right, ">")
|
||||||
@@ -630,7 +628,7 @@ func (vm *VM) executeSetItem() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return vm.push(Null)
|
return vm.push(object.NULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
@@ -664,7 +662,7 @@ func (vm *VM) executeCall() error {
|
|||||||
switch callee := callee.(type) {
|
switch callee := callee.(type) {
|
||||||
case *object.Closure:
|
case *object.Closure:
|
||||||
return vm.callClosure(callee, args)
|
return vm.callClosure(callee, args)
|
||||||
case *object.Builtin:
|
case object.Builtin:
|
||||||
return vm.callBuiltin(callee, args)
|
return vm.callBuiltin(callee, args)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
@@ -727,7 +725,7 @@ func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
|
func (vm *VM) callBuiltin(builtin object.Builtin, numArgs int) error {
|
||||||
args := vm.stack[vm.sp-numArgs : vm.sp]
|
args := vm.stack[vm.sp-numArgs : vm.sp]
|
||||||
|
|
||||||
result := builtin.Fn(args...)
|
result := builtin.Fn(args...)
|
||||||
@@ -739,7 +737,7 @@ func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := vm.push(Null)
|
err := vm.push(object.NULL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -761,8 +759,8 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
|
|||||||
}
|
}
|
||||||
vm.sp = vm.sp - numFree
|
vm.sp = vm.sp - numFree
|
||||||
|
|
||||||
closure := &object.Closure{Fn: function, Free: free}
|
closure := object.Closure{Fn: function, Free: free}
|
||||||
return vm.push(closure)
|
return vm.push(&closure)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) loadModule(name object.Object) error {
|
func (vm *VM) loadModule(name object.Object) error {
|
||||||
@@ -788,20 +786,38 @@ func (vm *VM) LastPoppedStackElem() object.Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) Run() (err error) {
|
func (vm *VM) Run() (err error) {
|
||||||
var n int
|
var (
|
||||||
|
op code.Opcode
|
||||||
|
opcodeFreqs = make(map[code.Opcode]int)
|
||||||
|
)
|
||||||
|
|
||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Printf("%d instructions executeuted in %s", n, time.Now().Sub(start))
|
total := 0
|
||||||
|
opcodes := make([]code.Opcode, 0, len(opcodeFreqs))
|
||||||
|
|
||||||
|
for opcode := range opcodeFreqs {
|
||||||
|
opcodes = append(opcodes, opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(opcodes, func(i, j int) bool {
|
||||||
|
return opcodeFreqs[opcodes[i]] > opcodeFreqs[opcodes[j]]
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Printf("%d instructions executed in %s", total, time.Now().Sub(start))
|
||||||
|
log.Print("Top 10 instructions:")
|
||||||
|
for _, opcode := range opcodes[:10] {
|
||||||
|
log.Printf("%10d %s", opcodeFreqs[opcode], opcode)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
op := vm.currentFrame().ReadNextOp()
|
op = vm.currentFrame().ReadNextOp()
|
||||||
|
|
||||||
if vm.Debug {
|
if vm.Debug {
|
||||||
n++
|
opcodeFreqs[op]++
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%-25s %-20s\n",
|
"%-25s %-20s\n",
|
||||||
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
|
fmt.Sprintf("%04d %s", vm.currentFrame().ip, op),
|
||||||
@@ -949,7 +965,7 @@ func (vm *VM) Run() (err error) {
|
|||||||
err = fmt.Errorf("unhandled opcode: %s", op)
|
err = fmt.Errorf("unhandled opcode: %s", op)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vm.Debug {
|
if vm.Trace {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
"%-25s [ip=%02d fp=%02d, sp=%02d]",
|
||||||
"", vm.currentFrame().ip, vm.fp-1, vm.sp,
|
"", vm.currentFrame().ip, vm.fp-1, vm.sp,
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
|
|||||||
}
|
}
|
||||||
|
|
||||||
case object.Null:
|
case object.Null:
|
||||||
if actual != Null {
|
if actual != object.NULL {
|
||||||
t.Errorf("object is not Null: %T (%+v)", actual, actual)
|
t.Errorf("object is not Null: %T (%+v)", actual, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,14 +302,14 @@ func TestConditionals(t *testing.T) {
|
|||||||
{"if (1 < 2) { 10 }", 10},
|
{"if (1 < 2) { 10 }", 10},
|
||||||
{"if (1 < 2) { 10 } else { 20 }", 10},
|
{"if (1 < 2) { 10 } else { 20 }", 10},
|
||||||
{"if (1 > 2) { 10 } else { 20 }", 20},
|
{"if (1 > 2) { 10 } else { 20 }", 20},
|
||||||
{"if (1 > 2) { 10 }", Null},
|
{"if (1 > 2) { 10 }", object.NULL},
|
||||||
{"if (false) { 10 }", Null},
|
{"if (false) { 10 }", object.NULL},
|
||||||
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
||||||
{"if (true) { a := 5; }", Null},
|
{"if (true) { a := 5; }", object.NULL},
|
||||||
{"if (true) { 10; a := 5; }", Null},
|
{"if (true) { 10; a := 5; }", object.NULL},
|
||||||
{"if (false) { 10 } else { b := 5; }", Null},
|
{"if (false) { 10 } else { b := 5; }", object.NULL},
|
||||||
{"if (false) { 10 } else { 10; b := 5; }", Null},
|
{"if (false) { 10 } else { 10; b := 5; }", object.NULL},
|
||||||
{"if (true) { a := 5; } else { 10 }", Null},
|
{"if (true) { a := 5; } else { 10 }", object.NULL},
|
||||||
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||||
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
||||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||||
@@ -430,13 +430,13 @@ func TestIndexExpressions(t *testing.T) {
|
|||||||
{"[1, 2, 3][1]", 2},
|
{"[1, 2, 3][1]", 2},
|
||||||
{"[1, 2, 3][0 + 2]", 3},
|
{"[1, 2, 3][0 + 2]", 3},
|
||||||
{"[[1, 1, 1]][0][0]", 1},
|
{"[[1, 1, 1]][0][0]", 1},
|
||||||
{"[][0]", Null},
|
{"[][0]", object.NULL},
|
||||||
{"[1, 2, 3][99]", Null},
|
{"[1, 2, 3][99]", object.NULL},
|
||||||
{"[1][-1]", Null},
|
{"[1][-1]", object.NULL},
|
||||||
{"{1: 1, 2: 2}[1]", 1},
|
{"{1: 1, 2: 2}[1]", 1},
|
||||||
{"{1: 1, 2: 2}[2]", 2},
|
{"{1: 1, 2: 2}[2]", 2},
|
||||||
{"{1: 1}[0]", Null},
|
{"{1: 1}[0]", object.NULL},
|
||||||
{"{}[0]", Null},
|
{"{}[0]", object.NULL},
|
||||||
{`"abc"[0]`, "a"},
|
{`"abc"[0]`, "a"},
|
||||||
{`"abc"[1]`, "b"},
|
{`"abc"[1]`, "b"},
|
||||||
{`"abc"[2]`, "c"},
|
{`"abc"[2]`, "c"},
|
||||||
@@ -506,7 +506,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
|||||||
noReturn := fn() { };
|
noReturn := fn() { };
|
||||||
noReturn();
|
noReturn();
|
||||||
`,
|
`,
|
||||||
expected: Null,
|
expected: object.NULL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
@@ -515,7 +515,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
|||||||
noReturn();
|
noReturn();
|
||||||
noReturnTwo();
|
noReturnTwo();
|
||||||
`,
|
`,
|
||||||
expected: Null,
|
expected: object.NULL,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,16 +724,16 @@ func TestBuiltinFunctions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{`len([1, 2, 3])`, 3},
|
{`len([1, 2, 3])`, 3},
|
||||||
{`len([])`, 0},
|
{`len([])`, 0},
|
||||||
{`print("hello", "world!")`, Null},
|
{`print("hello", "world!")`, object.NULL},
|
||||||
{`first([1, 2, 3])`, 1},
|
{`first([1, 2, 3])`, 1},
|
||||||
{`first([])`, Null},
|
{`first([])`, object.NULL},
|
||||||
{`first(1)`,
|
{`first(1)`,
|
||||||
&object.Error{
|
&object.Error{
|
||||||
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
|
Message: "TypeError: first() expected argument #1 to be `array` got `int`",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{`last([1, 2, 3])`, 3},
|
{`last([1, 2, 3])`, 3},
|
||||||
{`last([])`, Null},
|
{`last([])`, object.NULL},
|
||||||
{`last(1)`,
|
{`last(1)`,
|
||||||
&object.Error{
|
&object.Error{
|
||||||
Message: "TypeError: last() expected argument #1 to be `array` got `int`",
|
Message: "TypeError: last() expected argument #1 to be `array` got `int`",
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func compile(path string, debug bool) (bc *compiler.Bytecode, err error) {
|
|||||||
return c.Bytecode(), nil
|
return c.Bytecode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecFileVM(f string, args []string, debug bool) (err error) {
|
func ExecFileVM(f string, args []string, debug, trace bool) (err error) {
|
||||||
var bytecode *compiler.Bytecode
|
var bytecode *compiler.Bytecode
|
||||||
|
|
||||||
object.Args = args
|
object.Args = args
|
||||||
@@ -89,6 +89,7 @@ func ExecFileVM(f string, args []string, debug bool) (err error) {
|
|||||||
|
|
||||||
mvm := vm.New(f, bytecode)
|
mvm := vm.New(f, bytecode)
|
||||||
mvm.Debug = debug
|
mvm.Debug = debug
|
||||||
|
mvm.Trace = trace
|
||||||
if err = mvm.Run(); err != nil {
|
if err = mvm.Run(); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
|
|||||||
8
repl.go
8
repl.go
@@ -20,7 +20,7 @@ import (
|
|||||||
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
||||||
// by lexing, parsing and evaluating the input in the interpreter
|
// by lexing, parsing and evaluating the input in the interpreter
|
||||||
|
|
||||||
func VmREPL(args []string, debug bool) error {
|
func VmREPL(args []string, debug, trace bool) error {
|
||||||
var state = vm.NewVMState()
|
var state = vm.NewVMState()
|
||||||
|
|
||||||
object.Args = args
|
object.Args = args
|
||||||
@@ -73,6 +73,8 @@ func VmREPL(args []string, debug bool) error {
|
|||||||
|
|
||||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||||
mvm.Debug = debug
|
mvm.Debug = debug
|
||||||
|
mvm.Trace = trace
|
||||||
|
|
||||||
if err := mvm.Run(); err != nil {
|
if err := mvm.Run(); err != nil {
|
||||||
fmt.Fprintf(t, "runtime error: %v\n", err)
|
fmt.Fprintf(t, "runtime error: %v\n", err)
|
||||||
continue
|
continue
|
||||||
@@ -129,7 +131,7 @@ func acceptUntil(t *term.Terminal, start, end string) (string, error) {
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SimpleVmREPL(args []string, debug bool) {
|
func SimpleVmREPL(args []string, debug, trace bool) {
|
||||||
var (
|
var (
|
||||||
state = vm.NewVMState()
|
state = vm.NewVMState()
|
||||||
reader = bufio.NewReader(os.Stdin)
|
reader = bufio.NewReader(os.Stdin)
|
||||||
@@ -171,6 +173,8 @@ func SimpleVmREPL(args []string, debug bool) {
|
|||||||
|
|
||||||
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
mvm := vm.NewWithState("<stdin>", c.Bytecode(), state)
|
||||||
mvm.Debug = debug
|
mvm.Debug = debug
|
||||||
|
mvm.Trace = trace
|
||||||
|
|
||||||
if err := mvm.Run(); err != nil {
|
if err := mvm.Run(); err != nil {
|
||||||
fmt.Printf("runtime error: %v\n", err)
|
fmt.Printf("runtime error: %v\n", err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user