lots o fixes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -79,6 +79,7 @@ fabric.properties
|
|||||||
|
|
||||||
*~
|
*~
|
||||||
*-e
|
*-e
|
||||||
|
*.mc
|
||||||
*.so
|
*.so
|
||||||
*.bak
|
*.bak
|
||||||
*.out
|
*.out
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -73,7 +73,7 @@ compare: ## Run benchmarks comparing Monkey with other languages
|
|||||||
'go run examples/fib.go' \
|
'go run examples/fib.go' \
|
||||||
'tengo examples/fib.tengo' \
|
'tengo examples/fib.tengo' \
|
||||||
'python3 examples/fib.py' \
|
'python3 examples/fib.py' \
|
||||||
'./monkey examples/fib.monkey'
|
'./monkey examples/fib.m'
|
||||||
|
|
||||||
bench: # Run test benchmarks
|
bench: # Run test benchmarks
|
||||||
@go test -v -benchmem -bench=. ./...
|
@go test -v -benchmem -bench=. ./...
|
||||||
|
|||||||
4
bench.sh
4
bench.sh
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
currentBranch="$(git branch --show-current)"
|
currentBranch="$(git branch --show-current)"
|
||||||
|
|
||||||
|
N="${1:-35}"
|
||||||
|
|
||||||
hyperfine \
|
hyperfine \
|
||||||
-w 5 \
|
-w 5 \
|
||||||
-p "make build" \
|
-p "make build" \
|
||||||
-n "$currentBranch" \
|
-n "$currentBranch" \
|
||||||
'./monkey examples/fib.monkey'
|
"./monkey examples/fib.m $N"
|
||||||
@@ -31,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, trace)
|
monkey.ExecFile(flag.Arg(0), flag.Args()[1:], debug, trace)
|
||||||
|
|
||||||
case simple:
|
case simple:
|
||||||
monkey.SimpleVmREPL(flag.Args(), debug, trace)
|
monkey.SimpleREPL(flag.Args(), debug, trace)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
monkey.VmREPL(flag.Args(), debug, trace)
|
monkey.REPL(flag.Args(), debug, trace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if [ $# -ne 1 ]; then
|
if [ $# -lt 1 ]; then
|
||||||
echo "Usage: $(basename "$0") <branch>"
|
echo "Usage: $(basename "$0") <branch>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -8,6 +8,8 @@ fi
|
|||||||
compareWith="$1"
|
compareWith="$1"
|
||||||
currentBranch="$(git branch --show-current)"
|
currentBranch="$(git branch --show-current)"
|
||||||
|
|
||||||
|
N="${2:-35}"
|
||||||
|
|
||||||
if [ -n "$(git status --porcelain)" ]; then
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
echo "$currentBranch is not clean, please stash or commit your changes!"
|
echo "$currentBranch is not clean, please stash or commit your changes!"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -20,5 +22,5 @@ hyperfine \
|
|||||||
-p "git checkout $compareWith; make build" \
|
-p "git checkout $compareWith; make build" \
|
||||||
-n "$currentBranch" \
|
-n "$currentBranch" \
|
||||||
-n "$compareWith" \
|
-n "$compareWith" \
|
||||||
'./monkey examples/fib.monkey' \
|
"./monkey examples/fib.m $N" \
|
||||||
'./monkey examples/fib.monkey'
|
"./monkey examples/fib.m $N"
|
||||||
@@ -54,7 +54,7 @@ read := fn() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write := fn(x) {
|
write := fn(x) {
|
||||||
print(chr(x))
|
println(chr(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
VM := fn(program) {
|
VM := fn(program) {
|
||||||
@@ -99,12 +99,12 @@ VM := fn(program) {
|
|||||||
ip = ip + 1
|
ip = ip + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
print("memory:")
|
println("memory:")
|
||||||
print(memory)
|
println(memory)
|
||||||
print("ip:")
|
println("ip:")
|
||||||
print(ip)
|
println(ip)
|
||||||
print("dp:")
|
println("dp:")
|
||||||
print(dp)
|
println(dp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hello World
|
// Hello World
|
||||||
@@ -10,7 +10,7 @@ book := {
|
|||||||
printBookName := fn(book) {
|
printBookName := fn(book) {
|
||||||
title := book["title"];
|
title := book["title"];
|
||||||
author := book["author"];
|
author := book["author"];
|
||||||
print(author + " - " + title);
|
println(author + " - " + title);
|
||||||
};
|
};
|
||||||
|
|
||||||
printBookName(book);
|
printBookName(book);
|
||||||
@@ -4,5 +4,5 @@ fd := socket("tcp4")
|
|||||||
bind(fd, "127.0.0.1:32535")
|
bind(fd, "127.0.0.1:32535")
|
||||||
connect(fd, "127.0.0.1:8000")
|
connect(fd, "127.0.0.1:8000")
|
||||||
write(fd, "Hello World")
|
write(fd, "Hello World")
|
||||||
print(read(fd))
|
println(read(fd))
|
||||||
close(fd)
|
close(fd)
|
||||||
15
examples/embed.go
Normal file
15
examples/embed.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const program = `println("Hello World!")`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := monkey.ExecString(program, false, false); err != nil {
|
||||||
|
log.Fatal("error running program")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,4 +5,4 @@ fact := fn(n, a) {
|
|||||||
return fact(n - 1, a * n)
|
return fact(n - 1, a * n)
|
||||||
}
|
}
|
||||||
|
|
||||||
print(fact(5, 1))
|
println(fact(5, 1))
|
||||||
@@ -11,4 +11,4 @@ if (len(args()) > 0) {
|
|||||||
N = int(args()[0])
|
N = int(args()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
print(fib(N))
|
println(fib(N))
|
||||||
@@ -21,4 +21,4 @@ if (len(args()) > 0) {
|
|||||||
N = int(args()[0])
|
N = int(args()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
print(fib(N))
|
println(fib(N))
|
||||||
@@ -14,4 +14,4 @@ if (len(args()) > 0) {
|
|||||||
N = int(args()[0])
|
N = int(args()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
print(fib(N, 0, 1))
|
println(fib(N, 0, 1))
|
||||||
@@ -14,6 +14,6 @@ test := fn(n) {
|
|||||||
|
|
||||||
n := 1
|
n := 1
|
||||||
while (n <= 100) {
|
while (n <= 100) {
|
||||||
print(test(n))
|
println(test(n))
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
1
examples/hello.m
Normal file
1
examples/hello.m
Normal file
@@ -0,0 +1 @@
|
|||||||
|
println("Hello World!")
|
||||||
@@ -1 +0,0 @@
|
|||||||
print("Hello World!")
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
name := input("What is your name? ")
|
name := input("What is your name? ")
|
||||||
|
|
||||||
print("Hello " + name)
|
println("Hello " + name)
|
||||||
@@ -35,7 +35,7 @@ if (len(args()) == 1) {
|
|||||||
n := 1
|
n := 1
|
||||||
while (n < N) {
|
while (n < N) {
|
||||||
if (prime(n)) {
|
if (prime(n)) {
|
||||||
print(n)
|
println(n)
|
||||||
}
|
}
|
||||||
n = n + 1
|
n = n + 1
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ 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},
|
||||||
|
"println": {Name: "println", Fn: Println},
|
||||||
"first": {Name: "first", Fn: First},
|
"first": {Name: "first", Fn: First},
|
||||||
"last": {Name: "last", Fn: Last},
|
"last": {Name: "last", Fn: Last},
|
||||||
"rest": {Name: "rest", Fn: Rest},
|
"rest": {Name: "rest", Fn: Rest},
|
||||||
|
|||||||
@@ -16,7 +16,21 @@ func Print(args ...object.Object) object.Object {
|
|||||||
return newError(err.Error())
|
return newError(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(args[0].String())
|
fmt.Fprint(object.Stdout, args[0].String())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println ...
|
||||||
|
func Println(args ...object.Object) object.Object {
|
||||||
|
if err := typing.Check(
|
||||||
|
"println", args,
|
||||||
|
typing.MinimumArgs(1),
|
||||||
|
); err != nil {
|
||||||
|
return newError(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(object.Stdout, args[0].String())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,22 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"monkey/internal/code"
|
"monkey/internal/code"
|
||||||
"monkey/internal/object"
|
"monkey/internal/object"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Indent(text, ident string) string {
|
func indent(text, indent string) string {
|
||||||
if text[len(text)-1:] == "\n" {
|
if text[len(text)-1:] == "\n" {
|
||||||
result := ""
|
result := ""
|
||||||
for _, j := range strings.Split(text[:len(text)-1], "\n") {
|
for _, j := range strings.Split(text[:len(text)-1], "\n") {
|
||||||
result += ident + j + "\n"
|
result += indent + j + "\n"
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
result := ""
|
result := ""
|
||||||
for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
|
for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
|
||||||
result += ident + j + "\n"
|
result += indent + j + "\n"
|
||||||
}
|
}
|
||||||
return result[:len(result)-1]
|
return result[:len(result)-1]
|
||||||
}
|
}
|
||||||
@@ -38,7 +37,7 @@ func (b *Bytecode) String() string {
|
|||||||
for i, c := range b.Constants {
|
for i, c := range b.Constants {
|
||||||
s.WriteString(fmt.Sprintf("%02d %s\n", i, c))
|
s.WriteString(fmt.Sprintf("%02d %s\n", i, c))
|
||||||
if cf, ok := c.(*object.CompiledFunction); ok {
|
if cf, ok := c.(*object.CompiledFunction); ok {
|
||||||
s.WriteString(fmt.Sprintf(" Instructions:\n%s\n", Indent(cf.Instructions.String(), " ")))
|
s.WriteString(fmt.Sprintf(" Instructions:\n%s\n", indent(cf.Instructions.String(), " ")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,22 +50,40 @@ type encoder struct {
|
|||||||
bytes.Buffer
|
bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) Write(b []byte) (err error) {
|
func (e *encoder) WriteBytes(b []byte) error {
|
||||||
_, err = e.Buffer.Write(b)
|
if err := e.WriteValue(len(b)); err != nil {
|
||||||
return
|
return err
|
||||||
|
}
|
||||||
|
if _, err := e.Buffer.Write(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) WriteString(s string) (err error) {
|
func (e *encoder) WriteString(s string) error {
|
||||||
_, err = e.Buffer.WriteString(s)
|
if err := e.WriteValue(len(s)); err != nil {
|
||||||
return
|
return err
|
||||||
|
}
|
||||||
|
if _, err := e.Buffer.WriteString(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) WriteValue(data any) error {
|
func (e *encoder) WriteValue(data any) error {
|
||||||
|
switch val := data.(type) {
|
||||||
|
case int:
|
||||||
|
return binary.Write(&e.Buffer, binary.BigEndian, int64(val))
|
||||||
|
case object.Type:
|
||||||
|
return e.WriteValue(int(val))
|
||||||
|
default:
|
||||||
return binary.Write(&e.Buffer, binary.BigEndian, data)
|
return binary.Write(&e.Buffer, binary.BigEndian, data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) WriteObjects(objs ...object.Object) (err error) {
|
func (e *encoder) WriteObjects(objects ...object.Object) (err error) {
|
||||||
for _, obj := range objs {
|
err = errors.Join(err, e.WriteValue(len(objects)))
|
||||||
|
for _, obj := range objects {
|
||||||
err = errors.Join(err, e.WriteValue(obj.Type()))
|
err = errors.Join(err, e.WriteValue(obj.Type()))
|
||||||
|
|
||||||
switch o := obj.(type) {
|
switch o := obj.(type) {
|
||||||
@@ -77,13 +94,11 @@ func (e *encoder) WriteObjects(objs ...object.Object) (err error) {
|
|||||||
case object.Integer:
|
case object.Integer:
|
||||||
err = errors.Join(err, e.WriteValue(o.Value))
|
err = errors.Join(err, e.WriteValue(o.Value))
|
||||||
case object.String:
|
case object.String:
|
||||||
err = errors.Join(err, e.WriteValue(len(o.Value)))
|
err = errors.Join(err, e.WriteString(o.Value))
|
||||||
err = errors.Join(err, e.WriteValue(o.Value))
|
|
||||||
case *object.CompiledFunction:
|
case *object.CompiledFunction:
|
||||||
err = errors.Join(err, e.WriteValue(o.NumParameters))
|
err = errors.Join(err, e.WriteValue(o.NumParameters))
|
||||||
err = errors.Join(err, e.WriteValue(o.NumLocals))
|
err = errors.Join(err, e.WriteValue(o.NumLocals))
|
||||||
err = errors.Join(err, e.WriteValue(len(o.Instructions)))
|
err = errors.Join(err, e.WriteBytes(o.Instructions))
|
||||||
err = errors.Join(err, e.Write(o.Instructions))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -92,9 +107,7 @@ func (e *encoder) WriteObjects(objs ...object.Object) (err error) {
|
|||||||
func (b Bytecode) Encode() (data []byte, err error) {
|
func (b Bytecode) Encode() (data []byte, err error) {
|
||||||
var e encoder
|
var e encoder
|
||||||
|
|
||||||
err = errors.Join(err, e.WriteValue(len(b.Instructions)))
|
err = errors.Join(err, e.WriteBytes(b.Instructions))
|
||||||
err = errors.Join(err, e.Write(b.Instructions))
|
|
||||||
err = errors.Join(err, e.WriteValue(len(b.Constants)))
|
|
||||||
err = errors.Join(err, e.WriteObjects(b.Constants...))
|
err = errors.Join(err, e.WriteObjects(b.Constants...))
|
||||||
return e.Bytes(), err
|
return e.Bytes(), err
|
||||||
}
|
}
|
||||||
@@ -110,10 +123,8 @@ func (d *decoder) Byte() (b byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) Int() (i int) {
|
func (d *decoder) Int() int {
|
||||||
i = int(binary.BigEndian.Uint32(d.b[d.pos:]))
|
return int(d.Int64())
|
||||||
d.pos += 4
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) Uint64() (i uint64) {
|
func (d *decoder) Uint64() (i uint64) {
|
||||||
@@ -122,14 +133,10 @@ func (d *decoder) Uint64() (i uint64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) Int64() (i int64) {
|
func (d *decoder) Int64() int64 {
|
||||||
return int64(d.Uint64())
|
return int64(d.Uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) Float64() (f float64) {
|
|
||||||
return math.Float64frombits(d.Uint64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Bytes(len int) (b []byte) {
|
func (d *decoder) Bytes(len int) (b []byte) {
|
||||||
b = d.b[d.pos : d.pos+len]
|
b = d.b[d.pos : d.pos+len]
|
||||||
d.pos += len
|
d.pos += len
|
||||||
@@ -161,7 +168,7 @@ func (d *decoder) Objects(len int) (o []object.Object) {
|
|||||||
Instructions: d.Bytes(d.Int()),
|
Instructions: d.Bytes(d.Int()),
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("decoder: unsupported decoding for type %d", t))
|
panic(fmt.Sprintf("decoder: unsupported decoding for type %d (%s)", t, t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
55
internal/compiler/bytecode_test.go
Normal file
55
internal/compiler/bytecode_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"monkey/internal/lexer"
|
||||||
|
"monkey/internal/parser"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hexRe = regexp.MustCompile(`0x[0-9a-f]+`)
|
||||||
|
|
||||||
|
func compile(input string) (*Bytecode, error) {
|
||||||
|
l := lexer.New(input)
|
||||||
|
p := parser.New("<text", l)
|
||||||
|
c := New()
|
||||||
|
if err := c.Compile(p.ParseProgram()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Bytecode(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeDecode(t *testing.T) {
|
||||||
|
input := `
|
||||||
|
fib := fn(x) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (x == 1) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fib(35)
|
||||||
|
`
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
bc, err := compile(input)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
encoded, err := bc.Encode()
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
decoded := Decode(encoded)
|
||||||
|
|
||||||
|
expected := hexRe.ReplaceAllString(bc.String(), "0x1234")
|
||||||
|
actual := hexRe.ReplaceAllString(decoded.String(), "0x1234")
|
||||||
|
|
||||||
|
assert.EqualValues(expected, actual)
|
||||||
|
}
|
||||||
@@ -948,7 +948,7 @@ func TestBuiltins(t *testing.T) {
|
|||||||
code.Make(code.OpArray, 0),
|
code.Make(code.OpArray, 0),
|
||||||
code.Make(code.OpCall, 1),
|
code.Make(code.OpCall, 1),
|
||||||
code.Make(code.OpPop),
|
code.Make(code.OpPop),
|
||||||
code.Make(code.OpGetBuiltin, 33),
|
code.Make(code.OpGetBuiltin, 34),
|
||||||
code.Make(code.OpArray, 0),
|
code.Make(code.OpArray, 0),
|
||||||
code.Make(code.OpConstant, 0),
|
code.Make(code.OpConstant, 0),
|
||||||
code.Make(code.OpCall, 2),
|
code.Make(code.OpCall, 2),
|
||||||
|
|||||||
@@ -889,7 +889,7 @@ func TestImportSearchPaths(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExamples(t *testing.T) {
|
func TestExamples(t *testing.T) {
|
||||||
matches, err := filepath.Glob("../../examples/*.monkey")
|
matches, err := filepath.Glob("../../examples/*.m")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func Exists(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func FindModule(name string) string {
|
func FindModule(name string) string {
|
||||||
basename := fmt.Sprintf("%s.monkey", name)
|
basename := fmt.Sprintf("%s.m", name)
|
||||||
for _, p := range SearchPaths {
|
for _, p := range SearchPaths {
|
||||||
filename := filepath.Join(p, basename)
|
filename := filepath.Join(p, basename)
|
||||||
if Exists(filename) {
|
if Exists(filename) {
|
||||||
|
|||||||
@@ -5,57 +5,57 @@ import (
|
|||||||
"monkey/internal/object"
|
"monkey/internal/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Frame struct {
|
type frame struct {
|
||||||
cl *object.Closure
|
cl *object.Closure
|
||||||
ip int
|
ip int
|
||||||
basePointer int
|
basePointer int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFrame(cl *object.Closure, basePointer int) Frame {
|
func newFrame(cl *object.Closure, basePointer int) frame {
|
||||||
return Frame{
|
return frame{
|
||||||
cl: cl,
|
cl: cl,
|
||||||
basePointer: basePointer,
|
basePointer: basePointer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) Closure() *object.Closure {
|
func (f *frame) Closure() *object.Closure {
|
||||||
return f.cl
|
return f.cl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) GetFree(idx uint8) object.Object {
|
func (f *frame) GetFree(idx uint8) object.Object {
|
||||||
return f.cl.Free[idx]
|
return f.cl.Free[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) SetFree(idx uint8, obj object.Object) {
|
func (f *frame) SetFree(idx uint8, obj object.Object) {
|
||||||
f.cl.Free[idx] = obj
|
f.cl.Free[idx] = obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) SetIP(ip int) {
|
func (f *frame) SetIP(ip int) {
|
||||||
f.ip = ip
|
f.ip = ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Frame) PeekNextOp() code.Opcode {
|
func (f frame) PeekNextOp() code.Opcode {
|
||||||
return code.Opcode(f.cl.Fn.Instructions[f.ip])
|
return code.Opcode(f.cl.Fn.Instructions[f.ip])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) ReadNextOp() code.Opcode {
|
func (f *frame) ReadNextOp() code.Opcode {
|
||||||
op := code.Opcode(f.cl.Fn.Instructions[f.ip])
|
op := code.Opcode(f.cl.Fn.Instructions[f.ip])
|
||||||
f.ip++
|
f.ip++
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) ReadUint8() uint8 {
|
func (f *frame) ReadUint8() uint8 {
|
||||||
n := code.ReadUint8(f.cl.Fn.Instructions[f.ip:])
|
n := code.ReadUint8(f.cl.Fn.Instructions[f.ip:])
|
||||||
f.ip++
|
f.ip++
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frame) ReadUint16() uint16 {
|
func (f *frame) ReadUint16() uint16 {
|
||||||
n := code.ReadUint16(f.cl.Fn.Instructions[f.ip:])
|
n := code.ReadUint16(f.cl.Fn.Instructions[f.ip:])
|
||||||
f.ip += 2
|
f.ip += 2
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Frame) Instructions() code.Instructions {
|
func (f frame) Instructions() code.Instructions {
|
||||||
return f.cl.Fn.Instructions
|
return f.cl.Fn.Instructions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
const StackSize = 2048
|
const (
|
||||||
const GlobalsSize = 65536
|
maxStackSize = 2048
|
||||||
const MaxFrames = 1024
|
maxFrames = 1024
|
||||||
|
maxGlobals = 65536
|
||||||
|
)
|
||||||
|
|
||||||
func isTruthy(obj object.Object) bool {
|
func isTruthy(obj object.Object) bool {
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
@@ -36,7 +38,7 @@ func isTruthy(obj object.Object) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeModule compiles the named module and returns a object.Module object
|
// executeModule compiles the named module and returns a object.Module object
|
||||||
func executeModule(name string, state *VMState) (object.Object, error) {
|
func executeModule(name string, state *State) (object.Object, error) {
|
||||||
filename := utils.FindModule(name)
|
filename := utils.FindModule(name)
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
return nil, fmt.Errorf("ImportError: no module named '%s'", name)
|
return nil, fmt.Errorf("ImportError: no module named '%s'", name)
|
||||||
@@ -73,21 +75,22 @@ func executeModule(name string, state *VMState) (object.Object, error) {
|
|||||||
return state.ExportedHash(), nil
|
return state.ExportedHash(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type VMState struct {
|
// State is the state of the virtual machine.
|
||||||
|
type State struct {
|
||||||
Constants []object.Object
|
Constants []object.Object
|
||||||
Globals []object.Object
|
Globals []object.Object
|
||||||
Symbols *compiler.SymbolTable
|
Symbols *compiler.SymbolTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVMState() *VMState {
|
func NewState() *State {
|
||||||
symbolTable := compiler.NewSymbolTable()
|
symbolTable := compiler.NewSymbolTable()
|
||||||
for i, builtin := range builtins.BuiltinsIndex {
|
for i, builtin := range builtins.BuiltinsIndex {
|
||||||
symbolTable.DefineBuiltin(i, builtin.Name)
|
symbolTable.DefineBuiltin(i, builtin.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &VMState{
|
return &State{
|
||||||
Constants: []object.Object{},
|
Constants: []object.Object{},
|
||||||
Globals: make([]object.Object, GlobalsSize),
|
Globals: make([]object.Object, maxGlobals),
|
||||||
Symbols: symbolTable,
|
Symbols: symbolTable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,7 +98,7 @@ func NewVMState() *VMState {
|
|||||||
// exported binding in the vm state. That is every binding that starts with a
|
// exported binding in the vm state. That is every binding that starts with a
|
||||||
// capital letter. This is used by the module import system to wrap up the
|
// capital letter. This is used by the module import system to wrap up the
|
||||||
// compiled and evaulated module into an object.
|
// compiled and evaulated module into an object.
|
||||||
func (s *VMState) ExportedHash() *object.Hash {
|
func (s *State) ExportedHash() *object.Hash {
|
||||||
pairs := make(map[object.HashKey]object.HashPair)
|
pairs := make(map[object.HashKey]object.HashPair)
|
||||||
for name, symbol := range s.Symbols.Store {
|
for name, symbol := range s.Symbols.Store {
|
||||||
if unicode.IsUpper(rune(name[0])) {
|
if unicode.IsUpper(rune(name[0])) {
|
||||||
@@ -113,7 +116,7 @@ type VM struct {
|
|||||||
Debug bool
|
Debug bool
|
||||||
Trace bool
|
Trace bool
|
||||||
|
|
||||||
state *VMState
|
state *State
|
||||||
|
|
||||||
dir string
|
dir string
|
||||||
file string
|
file string
|
||||||
@@ -121,16 +124,16 @@ type VM struct {
|
|||||||
stack []object.Object
|
stack []object.Object
|
||||||
sp int // Always points to the next value. Top of stack is stack[sp-1]
|
sp int // Always points to the next value. Top of stack is stack[sp-1]
|
||||||
|
|
||||||
frames []Frame
|
frames []frame
|
||||||
fp int // Always points to the current frame. Current frame is frames[fp-1]
|
fp int // Always points to the current frame. Current frame is frames[fp-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) currentFrame() *Frame {
|
func (vm *VM) currentFrame() *frame {
|
||||||
return &vm.frames[vm.fp-1]
|
return &vm.frames[vm.fp-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) pushFrame(f Frame) {
|
func (vm *VM) pushFrame(f frame) {
|
||||||
if vm.fp >= MaxFrames {
|
if vm.fp >= maxFrames {
|
||||||
panic("frame overflow")
|
panic("frame overflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +141,7 @@ func (vm *VM) pushFrame(f Frame) {
|
|||||||
vm.fp++
|
vm.fp++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) popFrame() Frame {
|
func (vm *VM) popFrame() frame {
|
||||||
if vm.fp == 0 {
|
if vm.fp == 0 {
|
||||||
panic("fame underflow")
|
panic("fame underflow")
|
||||||
}
|
}
|
||||||
@@ -151,18 +154,18 @@ func (vm *VM) popFrame() Frame {
|
|||||||
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
|
||||||
|
|
||||||
state := NewVMState()
|
state := NewState()
|
||||||
state.Constants = bytecode.Constants
|
state.Constants = bytecode.Constants
|
||||||
|
|
||||||
vm := &VM{
|
vm := &VM{
|
||||||
state: state,
|
state: state,
|
||||||
|
|
||||||
stack: make([]object.Object, StackSize),
|
stack: make([]object.Object, maxStackSize),
|
||||||
sp: 0,
|
sp: 0,
|
||||||
|
|
||||||
frames: frames,
|
frames: frames,
|
||||||
@@ -174,12 +177,12 @@ func New(fn string, bytecode *compiler.Bytecode) *VM {
|
|||||||
return vm
|
return vm
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
func NewWithState(fn string, bytecode *compiler.Bytecode, state *State) *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
|
||||||
|
|
||||||
vm := &VM{
|
vm := &VM{
|
||||||
@@ -188,7 +191,7 @@ func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
|||||||
frames: frames,
|
frames: frames,
|
||||||
fp: 1,
|
fp: 1,
|
||||||
|
|
||||||
stack: make([]object.Object, StackSize),
|
stack: make([]object.Object, maxStackSize),
|
||||||
sp: 0,
|
sp: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +201,7 @@ func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vm *VM) push(o object.Object) error {
|
func (vm *VM) push(o object.Object) error {
|
||||||
if vm.sp >= StackSize {
|
if vm.sp >= maxStackSize {
|
||||||
return fmt.Errorf("stack overflow")
|
return fmt.Errorf("stack overflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +715,7 @@ func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame := NewFrame(cl, vm.sp-numArgs)
|
frame := newFrame(cl, vm.sp-numArgs)
|
||||||
vm.pushFrame(frame)
|
vm.pushFrame(frame)
|
||||||
vm.sp = frame.basePointer + cl.Fn.NumLocals
|
vm.sp = frame.basePointer + cl.Fn.NumLocals
|
||||||
|
|
||||||
|
|||||||
@@ -1015,7 +1015,7 @@ func TestTailCalls(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
func TestIntegration(t *testing.T) {
|
||||||
matches, err := filepath.Glob("../testdata/*.monkey")
|
matches, err := filepath.Glob("../testdata/*.m")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -1060,7 +1060,7 @@ func TestExamples(t *testing.T) {
|
|||||||
t.Skip("skipping test in short mode.")
|
t.Skip("skipping test in short mode.")
|
||||||
}
|
}
|
||||||
|
|
||||||
matches, err := filepath.Glob("../examples/*.monkey")
|
matches, err := filepath.Glob("../examples/*.m")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
190
monkey.go
190
monkey.go
@@ -1,3 +1,4 @@
|
|||||||
|
// Package monkey provides a virtual machine for executing Monkey programs.
|
||||||
package monkey
|
package monkey
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -11,42 +12,37 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"monkey/internal/ast"
|
|
||||||
"monkey/internal/compiler"
|
"monkey/internal/compiler"
|
||||||
"monkey/internal/parser"
|
"monkey/internal/parser"
|
||||||
"monkey/internal/vm"
|
"monkey/internal/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MonkeyVersion is the version number for the monkey language virtual machine.
|
||||||
const MonkeyVersion = "v0.0.1"
|
const MonkeyVersion = "v0.0.1"
|
||||||
|
|
||||||
var ErrParseError = errors.New("error: parse error")
|
func fileExists(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
func mustReadFile(fname string) []byte {
|
if os.IsNotExist(err) {
|
||||||
b, err := os.ReadFile(fname)
|
return false
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
return b
|
return !info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(fname string, cont []byte) {
|
func decodeCompiledFile(fn string) (*compiler.Bytecode, error) {
|
||||||
if err := os.WriteFile(fname, cont, 0644); err != nil {
|
b, err := os.ReadFile(fn)
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decode(path string) (*compiler.Bytecode, error) {
|
|
||||||
b, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return compiler.Decode(b), nil
|
return compiler.Decode(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compile(path string, debug bool) (bc *compiler.Bytecode, err error) {
|
func compileFile(fn string, debug bool) (*compiler.Bytecode, error) {
|
||||||
input := string(mustReadFile(path))
|
input, err := os.ReadFile(fn)
|
||||||
res, errs := parser.Parse(path, input)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, errs := parser.Parse(fn, string(input))
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
|
||||||
@@ -63,54 +59,129 @@ func compile(path string, debug bool) (bc *compiler.Bytecode, err error) {
|
|||||||
|
|
||||||
c := compiler.New()
|
c := compiler.New()
|
||||||
c.Debug = debug
|
c.Debug = debug
|
||||||
c.SetFileInfo(path, input)
|
c.SetFileInfo("<stdin>", string(input))
|
||||||
if err = c.Compile(res); err != nil {
|
if err := c.Compile(res); err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Bytecode(), nil
|
return c.Bytecode(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecFileVM(f string, args []string, debug, trace bool) (err error) {
|
func compileString(input string, debug bool) (bc *compiler.Bytecode, err error) {
|
||||||
var bytecode *compiler.Bytecode
|
res, errs := parser.Parse("<stdin>", input)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
var buf strings.Builder
|
||||||
|
|
||||||
object.Args = args
|
for _, e := range errs {
|
||||||
|
buf.WriteString(e)
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
}
|
||||||
|
return nil, errors.New(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
if filepath.Ext(f) == ".mc" {
|
if debug {
|
||||||
bytecode, err = decode(f)
|
log.Printf("AST:\n%s\n", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := compiler.New()
|
||||||
|
c.Debug = debug
|
||||||
|
c.SetFileInfo("<stdin>", input)
|
||||||
|
if err := c.Compile(res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Bytecode(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecFile executes a Monkey program from a file on disk.
|
||||||
|
func ExecFile(fn string, args []string, debug, trace bool) error {
|
||||||
|
var (
|
||||||
|
bytecode *compiler.Bytecode
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
ext := filepath.Ext(fn)
|
||||||
|
mc := fn[:len(fn)-len(ext)] + ".mc"
|
||||||
|
|
||||||
|
if ext == ".mc" {
|
||||||
|
bytecode, err = decodeCompiledFile(fn)
|
||||||
|
} else if ext == ".m" && fileExists(mc) {
|
||||||
|
bytecode, err = decodeCompiledFile(mc)
|
||||||
} else {
|
} else {
|
||||||
bytecode, err = compile(f, debug)
|
input, err := os.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytecode, err = compileString(string(input), debug)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
log.Printf("Bytecode:\n%s\n", bytecode)
|
log.Printf("Bytecode:\n%s\n", bytecode)
|
||||||
}
|
}
|
||||||
|
|
||||||
mvm := vm.New(f, bytecode)
|
if ext == ".m" && !fileExists(mc) {
|
||||||
|
data, err := bytecode.Encode()
|
||||||
|
if err == nil {
|
||||||
|
os.WriteFile(mc, data, os.FileMode(0644))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object.Args = args
|
||||||
|
|
||||||
|
mvm := vm.New(fn, bytecode)
|
||||||
|
mvm.Debug = debug
|
||||||
|
mvm.Trace = trace
|
||||||
|
if err := mvm.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecString executes a Monkey program from a string.
|
||||||
|
func ExecString(input string, debug, trace bool) error {
|
||||||
|
bytecode, err := compileString(input, debug)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("Bytecode:\n%s\n", bytecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
mvm := vm.New("<stdin>", bytecode)
|
||||||
mvm.Debug = debug
|
mvm.Debug = debug
|
||||||
mvm.Trace = trace
|
mvm.Trace = trace
|
||||||
if err = mvm.Run(); err != nil {
|
if err = mvm.Run(); err != nil {
|
||||||
fmt.Println(err)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompileFiles(files []string, debug bool) error {
|
// CompileFiles compiles multiple Monkey files and returns any errors encountered during compilation.
|
||||||
for _, f := range files {
|
func CompileFiles(fns []string, debug bool) error {
|
||||||
b := mustReadFile(f)
|
for _, fn := range fns {
|
||||||
|
ext := filepath.Ext(fn)
|
||||||
|
mc := fn[:len(fn)-len(ext)] + ".mc"
|
||||||
|
|
||||||
res, errs := parser.Parse(f, string(b))
|
input, err := os.ReadFile(fn)
|
||||||
if len(errs) != 0 {
|
if err != nil {
|
||||||
for _, e := range errs {
|
return err
|
||||||
fmt.Println(e)
|
|
||||||
}
|
}
|
||||||
return ErrParseError
|
|
||||||
|
res, errs := parser.Parse(fn, string(input))
|
||||||
|
if len(errs) > 0 {
|
||||||
|
var buf strings.Builder
|
||||||
|
|
||||||
|
for _, e := range errs {
|
||||||
|
buf.WriteString(e)
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
}
|
||||||
|
return errors.New(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
@@ -120,44 +191,27 @@ func CompileFiles(files []string, debug bool) error {
|
|||||||
c := compiler.New()
|
c := compiler.New()
|
||||||
c.Debug = debug
|
c.Debug = debug
|
||||||
if err := c.Compile(res); err != nil {
|
if err := c.Compile(res); err != nil {
|
||||||
fmt.Println(err)
|
return err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
log.Printf("Bytecode:\n%s\n", c.Bytecode())
|
log.Printf("Bytecode:\n%s\n", c.Bytecode())
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt, err := c.Bytecode().Encode()
|
data, err := c.Bytecode().Encode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := filepath.Ext(f)
|
if err := os.WriteFile(mc, data, os.FileMode(0644)); err != nil {
|
||||||
writeFile(f[:len(f)-len(ext)]+".mc", cnt)
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrintVersionInfo prints information about the current version of the monkey virtual machine to the given writer.
|
||||||
func PrintVersionInfo(w io.Writer) {
|
func PrintVersionInfo(w io.Writer) {
|
||||||
fmt.Fprintf(w, "Monkey %s on %s\n", MonkeyVersion, strings.Title(runtime.GOOS))
|
fmt.Fprintf(w, "Monkey %s on %s\n", MonkeyVersion, runtime.GOOS)
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(src string) (ast.Node, error) {
|
|
||||||
tree, errs := parser.Parse("<input>", src)
|
|
||||||
if len(errs) > 0 {
|
|
||||||
var buf strings.Builder
|
|
||||||
|
|
||||||
buf.WriteString("parser error:\n")
|
|
||||||
for _, e := range errs {
|
|
||||||
buf.WriteString(e)
|
|
||||||
buf.WriteByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New(buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
repl.go
10
repl.go
@@ -20,8 +20,9 @@ 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, trace bool) error {
|
// REPL provides a read-eval-print loop for the monkey virtual machine.
|
||||||
var state = vm.NewVMState()
|
func REPL(args []string, debug, trace bool) error {
|
||||||
|
var state = vm.NewState()
|
||||||
|
|
||||||
object.Args = args
|
object.Args = args
|
||||||
|
|
||||||
@@ -142,9 +143,10 @@ func acceptUntil(t *term.Terminal, start, end string) (string, error) {
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SimpleVmREPL(args []string, debug, trace bool) {
|
// SimpleREPL provides a simple read-eval-print loop for the monkey virtual machine.
|
||||||
|
func SimpleREPL(args []string, debug, trace bool) {
|
||||||
var (
|
var (
|
||||||
state = vm.NewVMState()
|
state = vm.NewState()
|
||||||
reader = bufio.NewReader(os.Stdin)
|
reader = bufio.NewReader(os.Stdin)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user