diff --git a/.gitignore b/.gitignore index a3a52e1..dc22dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ fabric.properties *~ *-e +*.mc *.so *.bak *.out diff --git a/Makefile b/Makefile index 6e81d74..5085e87 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ compare: ## Run benchmarks comparing Monkey with other languages 'go run examples/fib.go' \ 'tengo examples/fib.tengo' \ 'python3 examples/fib.py' \ - './monkey examples/fib.monkey' + './monkey examples/fib.m' bench: # Run test benchmarks @go test -v -benchmem -bench=. ./... diff --git a/bench.sh b/bench.sh index 10acd90..13af5ea 100644 --- a/bench.sh +++ b/bench.sh @@ -2,8 +2,10 @@ currentBranch="$(git branch --show-current)" +N="${1:-35}" + hyperfine \ -w 5 \ -p "make build" \ -n "$currentBranch" \ - './monkey examples/fib.monkey' \ No newline at end of file + "./monkey examples/fib.m $N" \ No newline at end of file diff --git a/cmd/monkey/main.go b/cmd/monkey/main.go index 692db03..84902b5 100644 --- a/cmd/monkey/main.go +++ b/cmd/monkey/main.go @@ -31,12 +31,12 @@ func main() { monkey.PrintVersionInfo(os.Stdout) 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: - monkey.SimpleVmREPL(flag.Args(), debug, trace) + monkey.SimpleREPL(flag.Args(), debug, trace) default: - monkey.VmREPL(flag.Args(), debug, trace) + monkey.REPL(flag.Args(), debug, trace) } } diff --git a/compare.sh b/compare-branch.sh similarity index 81% rename from compare.sh rename to compare-branch.sh index 021edcf..c31e257 100644 --- a/compare.sh +++ b/compare-branch.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -if [ $# -ne 1 ]; then +if [ $# -lt 1 ]; then echo "Usage: $(basename "$0") " exit 1 fi @@ -8,6 +8,8 @@ fi compareWith="$1" currentBranch="$(git branch --show-current)" +N="${2:-35}" + if [ -n "$(git status --porcelain)" ]; then echo "$currentBranch is not clean, please stash or commit your changes!" exit 1 @@ -20,5 +22,5 @@ hyperfine \ -p "git checkout $compareWith; make build" \ -n "$currentBranch" \ -n "$compareWith" \ - './monkey examples/fib.monkey' \ - './monkey examples/fib.monkey' \ No newline at end of file + "./monkey examples/fib.m $N" \ + "./monkey examples/fib.m $N" \ No newline at end of file diff --git a/examples/bf.monkey b/examples/bf.m similarity index 95% rename from examples/bf.monkey rename to examples/bf.m index 5f0f3b7..8c96a1b 100644 --- a/examples/bf.monkey +++ b/examples/bf.m @@ -54,7 +54,7 @@ read := fn() { } write := fn(x) { - print(chr(x)) + println(chr(x)) } VM := fn(program) { @@ -99,12 +99,12 @@ VM := fn(program) { ip = ip + 1 } - print("memory:") - print(memory) - print("ip:") - print(ip) - print("dp:") - print(dp) + println("memory:") + println(memory) + println("ip:") + println(ip) + println("dp:") + println(dp) } // Hello World diff --git a/examples/demo.monkey b/examples/demo.m similarity index 95% rename from examples/demo.monkey rename to examples/demo.m index e107beb..1f5c5d8 100644 --- a/examples/demo.monkey +++ b/examples/demo.m @@ -10,7 +10,7 @@ book := { printBookName := fn(book) { title := book["title"]; author := book["author"]; - print(author + " - " + title); + println(author + " - " + title); }; printBookName(book); diff --git a/examples/echoclient.monkey b/examples/echoclient.m similarity index 88% rename from examples/echoclient.monkey rename to examples/echoclient.m index 20a3626..e33486b 100644 --- a/examples/echoclient.monkey +++ b/examples/echoclient.m @@ -4,5 +4,5 @@ fd := socket("tcp4") bind(fd, "127.0.0.1:32535") connect(fd, "127.0.0.1:8000") write(fd, "Hello World") -print(read(fd)) +println(read(fd)) close(fd) \ No newline at end of file diff --git a/examples/echoserver.monkey b/examples/echoserver.m similarity index 100% rename from examples/echoserver.monkey rename to examples/echoserver.m diff --git a/examples/embed.go b/examples/embed.go new file mode 100644 index 0000000..64bf717 --- /dev/null +++ b/examples/embed.go @@ -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") + } +} diff --git a/examples/fact.monkey b/examples/fact.m similarity index 100% rename from examples/fact.monkey rename to examples/fact.m diff --git a/examples/factt.monkey b/examples/factt.m similarity index 81% rename from examples/factt.monkey rename to examples/factt.m index afe0bcf..e35c635 100644 --- a/examples/factt.monkey +++ b/examples/factt.m @@ -5,4 +5,4 @@ fact := fn(n, a) { return fact(n - 1, a * n) } -print(fact(5, 1)) \ No newline at end of file +println(fact(5, 1)) \ No newline at end of file diff --git a/examples/fib.monkey b/examples/fib.m similarity index 90% rename from examples/fib.monkey rename to examples/fib.m index 9999a60..0faff48 100644 --- a/examples/fib.monkey +++ b/examples/fib.m @@ -11,4 +11,4 @@ if (len(args()) > 0) { N = int(args()[0]) } -print(fib(N)) \ No newline at end of file +println(fib(N)) \ No newline at end of file diff --git a/examples/fibi.monkey b/examples/fibi.m similarity index 94% rename from examples/fibi.monkey rename to examples/fibi.m index f5af75b..0f43ad0 100644 --- a/examples/fibi.monkey +++ b/examples/fibi.m @@ -21,4 +21,4 @@ if (len(args()) > 0) { N = int(args()[0]) } -print(fib(N)) \ No newline at end of file +println(fib(N)) \ No newline at end of file diff --git a/examples/fibt.monkey b/examples/fibt.m similarity index 89% rename from examples/fibt.monkey rename to examples/fibt.m index 6ffa629..2eb2a10 100644 --- a/examples/fibt.monkey +++ b/examples/fibt.m @@ -14,4 +14,4 @@ if (len(args()) > 0) { N = int(args()[0]) } -print(fib(N, 0, 1)) \ No newline at end of file +println(fib(N, 0, 1)) \ No newline at end of file diff --git a/examples/fizzbuzz.monkey b/examples/fizzbuzz.m similarity index 92% rename from examples/fizzbuzz.monkey rename to examples/fizzbuzz.m index ccfbf0e..d555435 100644 --- a/examples/fizzbuzz.monkey +++ b/examples/fizzbuzz.m @@ -14,6 +14,6 @@ test := fn(n) { n := 1 while (n <= 100) { - print(test(n)) + println(test(n)) n = n + 1 } \ No newline at end of file diff --git a/examples/hello.m b/examples/hello.m new file mode 100644 index 0000000..3f4ab44 --- /dev/null +++ b/examples/hello.m @@ -0,0 +1 @@ +println("Hello World!") \ No newline at end of file diff --git a/examples/hello.monkey b/examples/hello.monkey deleted file mode 100644 index 1dc45ac..0000000 --- a/examples/hello.monkey +++ /dev/null @@ -1 +0,0 @@ -print("Hello World!") \ No newline at end of file diff --git a/examples/input.monkey b/examples/input.m similarity index 61% rename from examples/input.monkey rename to examples/input.m index 8c9e9bd..79c33ed 100644 --- a/examples/input.monkey +++ b/examples/input.m @@ -1,3 +1,3 @@ name := input("What is your name? ") -print("Hello " + name) \ No newline at end of file +println("Hello " + name) \ No newline at end of file diff --git a/examples/primes.monkey b/examples/primes.m similarity index 97% rename from examples/primes.monkey rename to examples/primes.m index ef9cdf5..036cd0a 100644 --- a/examples/primes.monkey +++ b/examples/primes.m @@ -35,7 +35,7 @@ if (len(args()) == 1) { n := 1 while (n < N) { if (prime(n)) { - print(n) + println(n) } n = n + 1 } \ No newline at end of file diff --git a/internal/builtins/builtins.go b/internal/builtins/builtins.go index d0e271b..c50b9bc 100644 --- a/internal/builtins/builtins.go +++ b/internal/builtins/builtins.go @@ -11,6 +11,7 @@ var Builtins = map[string]object.Builtin{ "len": {Name: "len", Fn: Len}, "input": {Name: "input", Fn: Input}, "print": {Name: "print", Fn: Print}, + "println": {Name: "println", Fn: Println}, "first": {Name: "first", Fn: First}, "last": {Name: "last", Fn: Last}, "rest": {Name: "rest", Fn: Rest}, diff --git a/internal/builtins/print.go b/internal/builtins/print.go index 1ad4fcb..49cc517 100644 --- a/internal/builtins/print.go +++ b/internal/builtins/print.go @@ -16,7 +16,21 @@ func Print(args ...object.Object) object.Object { 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 } diff --git a/internal/compiler/bytecode.go b/internal/compiler/bytecode.go index 1123dbb..430223e 100644 --- a/internal/compiler/bytecode.go +++ b/internal/compiler/bytecode.go @@ -5,23 +5,22 @@ import ( "encoding/binary" "errors" "fmt" - "math" "monkey/internal/code" "monkey/internal/object" "strings" ) -func Indent(text, ident string) string { +func indent(text, indent string) string { if text[len(text)-1:] == "\n" { result := "" for _, j := range strings.Split(text[:len(text)-1], "\n") { - result += ident + j + "\n" + result += indent + j + "\n" } return result } result := "" for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") { - result += ident + j + "\n" + result += indent + j + "\n" } return result[:len(result)-1] } @@ -38,7 +37,7 @@ func (b *Bytecode) String() string { for i, c := range b.Constants { s.WriteString(fmt.Sprintf("%02d %s\n", i, c)) 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 } -func (e *encoder) Write(b []byte) (err error) { - _, err = e.Buffer.Write(b) - return +func (e *encoder) WriteBytes(b []byte) error { + if err := e.WriteValue(len(b)); err != nil { + return err + } + if _, err := e.Buffer.Write(b); err != nil { + return err + } + return nil } -func (e *encoder) WriteString(s string) (err error) { - _, err = e.Buffer.WriteString(s) - return +func (e *encoder) WriteString(s string) error { + if err := e.WriteValue(len(s)); err != nil { + return err + } + if _, err := e.Buffer.WriteString(s); err != nil { + return err + } + return nil } func (e *encoder) WriteValue(data any) error { - return binary.Write(&e.Buffer, binary.BigEndian, data) + 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) + } } -func (e *encoder) WriteObjects(objs ...object.Object) (err error) { - for _, obj := range objs { +func (e *encoder) WriteObjects(objects ...object.Object) (err error) { + err = errors.Join(err, e.WriteValue(len(objects))) + for _, obj := range objects { err = errors.Join(err, e.WriteValue(obj.Type())) switch o := obj.(type) { @@ -77,13 +94,11 @@ func (e *encoder) WriteObjects(objs ...object.Object) (err error) { case object.Integer: err = errors.Join(err, e.WriteValue(o.Value)) case object.String: - err = errors.Join(err, e.WriteValue(len(o.Value))) - err = errors.Join(err, e.WriteValue(o.Value)) + err = errors.Join(err, e.WriteString(o.Value)) case *object.CompiledFunction: err = errors.Join(err, e.WriteValue(o.NumParameters)) err = errors.Join(err, e.WriteValue(o.NumLocals)) - err = errors.Join(err, e.WriteValue(len(o.Instructions))) - err = errors.Join(err, e.Write(o.Instructions)) + err = errors.Join(err, e.WriteBytes(o.Instructions)) } } return @@ -92,9 +107,7 @@ func (e *encoder) WriteObjects(objs ...object.Object) (err error) { func (b Bytecode) Encode() (data []byte, err error) { var e encoder - err = errors.Join(err, e.WriteValue(len(b.Instructions))) - err = errors.Join(err, e.Write(b.Instructions)) - err = errors.Join(err, e.WriteValue(len(b.Constants))) + err = errors.Join(err, e.WriteBytes(b.Instructions)) err = errors.Join(err, e.WriteObjects(b.Constants...)) return e.Bytes(), err } @@ -110,10 +123,8 @@ func (d *decoder) Byte() (b byte) { return } -func (d *decoder) Int() (i int) { - i = int(binary.BigEndian.Uint32(d.b[d.pos:])) - d.pos += 4 - return +func (d *decoder) Int() int { + return int(d.Int64()) } func (d *decoder) Uint64() (i uint64) { @@ -122,14 +133,10 @@ func (d *decoder) Uint64() (i uint64) { return } -func (d *decoder) Int64() (i int64) { +func (d *decoder) Int64() int64 { return int64(d.Uint64()) } -func (d *decoder) Float64() (f float64) { - return math.Float64frombits(d.Uint64()) -} - func (d *decoder) Bytes(len int) (b []byte) { b = d.b[d.pos : d.pos+len] d.pos += len @@ -161,7 +168,7 @@ func (d *decoder) Objects(len int) (o []object.Object) { Instructions: d.Bytes(d.Int()), }) default: - panic(fmt.Sprintf("decoder: unsupported decoding for type %d", t)) + panic(fmt.Sprintf("decoder: unsupported decoding for type %d (%s)", t, t)) } } return diff --git a/internal/compiler/bytecode_test.go b/internal/compiler/bytecode_test.go new file mode 100644 index 0000000..59df1e1 --- /dev/null +++ b/internal/compiler/bytecode_test.go @@ -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("= MaxFrames { +func (vm *VM) pushFrame(f frame) { + if vm.fp >= maxFrames { panic("frame overflow") } @@ -138,7 +141,7 @@ func (vm *VM) pushFrame(f Frame) { vm.fp++ } -func (vm *VM) popFrame() Frame { +func (vm *VM) popFrame() frame { if vm.fp == 0 { panic("fame underflow") } @@ -151,18 +154,18 @@ func (vm *VM) popFrame() Frame { func New(fn string, bytecode *compiler.Bytecode) *VM { mainFn := object.CompiledFunction{Instructions: bytecode.Instructions} mainClosure := object.Closure{Fn: &mainFn} - mainFrame := NewFrame(&mainClosure, 0) + mainFrame := newFrame(&mainClosure, 0) - frames := make([]Frame, MaxFrames) + frames := make([]frame, maxFrames) frames[0] = mainFrame - state := NewVMState() + state := NewState() state.Constants = bytecode.Constants vm := &VM{ state: state, - stack: make([]object.Object, StackSize), + stack: make([]object.Object, maxStackSize), sp: 0, frames: frames, @@ -174,12 +177,12 @@ func New(fn string, bytecode *compiler.Bytecode) *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} mainClosure := object.Closure{Fn: &mainFn} - mainFrame := NewFrame(&mainClosure, 0) + mainFrame := newFrame(&mainClosure, 0) - frames := make([]Frame, MaxFrames) + frames := make([]frame, maxFrames) frames[0] = mainFrame vm := &VM{ @@ -188,7 +191,7 @@ func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM { frames: frames, fp: 1, - stack: make([]object.Object, StackSize), + stack: make([]object.Object, maxStackSize), sp: 0, } @@ -198,7 +201,7 @@ func NewWithState(fn string, bytecode *compiler.Bytecode, state *VMState) *VM { } func (vm *VM) push(o object.Object) error { - if vm.sp >= StackSize { + if vm.sp >= maxStackSize { 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.sp = frame.basePointer + cl.Fn.NumLocals diff --git a/internal/vm/vm_test.go b/internal/vm/vm_test.go index b4af1e8..c54ff28 100644 --- a/internal/vm/vm_test.go +++ b/internal/vm/vm_test.go @@ -1015,7 +1015,7 @@ func TestTailCalls(t *testing.T) { } func TestIntegration(t *testing.T) { - matches, err := filepath.Glob("../testdata/*.monkey") + matches, err := filepath.Glob("../testdata/*.m") if err != nil { t.Error(err) } @@ -1060,7 +1060,7 @@ func TestExamples(t *testing.T) { t.Skip("skipping test in short mode.") } - matches, err := filepath.Glob("../examples/*.monkey") + matches, err := filepath.Glob("../examples/*.m") if err != nil { t.Error(err) } diff --git a/monkey.go b/monkey.go index f73e708..e6ac612 100644 --- a/monkey.go +++ b/monkey.go @@ -1,3 +1,4 @@ +// Package monkey provides a virtual machine for executing Monkey programs. package monkey import ( @@ -11,42 +12,37 @@ import ( "runtime" "strings" - "monkey/internal/ast" "monkey/internal/compiler" "monkey/internal/parser" "monkey/internal/vm" ) +// MonkeyVersion is the version number for the monkey language virtual machine. const MonkeyVersion = "v0.0.1" -var ErrParseError = errors.New("error: parse error") - -func mustReadFile(fname string) []byte { - b, err := os.ReadFile(fname) - if err != nil { - panic(err) +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false } - return b + return !info.IsDir() } -func writeFile(fname string, cont []byte) { - if err := os.WriteFile(fname, cont, 0644); err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func decode(path string) (*compiler.Bytecode, error) { - b, err := os.ReadFile(path) +func decodeCompiledFile(fn string) (*compiler.Bytecode, error) { + b, err := os.ReadFile(fn) if err != nil { return nil, err } return compiler.Decode(b), nil } -func compile(path string, debug bool) (bc *compiler.Bytecode, err error) { - input := string(mustReadFile(path)) - res, errs := parser.Parse(path, input) +func compileFile(fn string, debug bool) (*compiler.Bytecode, error) { + input, err := os.ReadFile(fn) + if err != nil { + return nil, err + } + + res, errs := parser.Parse(fn, string(input)) if len(errs) > 0 { var buf strings.Builder @@ -63,54 +59,129 @@ func compile(path string, debug bool) (bc *compiler.Bytecode, err error) { c := compiler.New() c.Debug = debug - c.SetFileInfo(path, input) - if err = c.Compile(res); err != nil { - return + c.SetFileInfo("", string(input)) + if err := c.Compile(res); err != nil { + return nil, err } return c.Bytecode(), nil } -func ExecFileVM(f string, args []string, debug, trace bool) (err error) { - var bytecode *compiler.Bytecode +func compileString(input string, debug bool) (bc *compiler.Bytecode, err error) { + res, errs := parser.Parse("", 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" { - bytecode, err = decode(f) + if debug { + log.Printf("AST:\n%s\n", res) + } + + c := compiler.New() + c.Debug = debug + c.SetFileInfo("", 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 { - bytecode, err = compile(f, debug) + input, err := os.ReadFile(fn) + if err != nil { + return err + } + bytecode, err = compileString(string(input), debug) } if err != nil { - fmt.Println(err) - return + return err } if debug { 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("", bytecode) mvm.Debug = debug mvm.Trace = trace if err = mvm.Run(); err != nil { - fmt.Println(err) - return + return err } - return + return nil } -func CompileFiles(files []string, debug bool) error { - for _, f := range files { - b := mustReadFile(f) +// CompileFiles compiles multiple Monkey files and returns any errors encountered during compilation. +func CompileFiles(fns []string, debug bool) error { + for _, fn := range fns { + ext := filepath.Ext(fn) + mc := fn[:len(fn)-len(ext)] + ".mc" + + input, err := os.ReadFile(fn) + if err != nil { + return err + } + + res, errs := parser.Parse(fn, string(input)) + if len(errs) > 0 { + var buf strings.Builder - res, errs := parser.Parse(f, string(b)) - if len(errs) != 0 { for _, e := range errs { - fmt.Println(e) + buf.WriteString(e) + buf.WriteByte('\n') } - return ErrParseError + return errors.New(buf.String()) } if debug { @@ -120,44 +191,27 @@ func CompileFiles(files []string, debug bool) error { c := compiler.New() c.Debug = debug if err := c.Compile(res); err != nil { - fmt.Println(err) - continue + return err } if debug { log.Printf("Bytecode:\n%s\n", c.Bytecode()) } - cnt, err := c.Bytecode().Encode() + data, err := c.Bytecode().Encode() if err != nil { - fmt.Println(err) - continue + return err } - ext := filepath.Ext(f) - writeFile(f[:len(f)-len(ext)]+".mc", cnt) + if err := os.WriteFile(mc, data, os.FileMode(0644)); err != nil { + return err + } } return nil } +// PrintVersionInfo prints information about the current version of the monkey virtual machine to the given writer. func PrintVersionInfo(w io.Writer) { - fmt.Fprintf(w, "Monkey %s on %s\n", MonkeyVersion, strings.Title(runtime.GOOS)) -} - -func Parse(src string) (ast.Node, error) { - tree, errs := parser.Parse("", 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 + fmt.Fprintf(w, "Monkey %s on %s\n", MonkeyVersion, runtime.GOOS) } diff --git a/repl.go b/repl.go index 6263b19..d8a0828 100644 --- a/repl.go +++ b/repl.go @@ -20,8 +20,9 @@ import ( // Package repl implements the Read-Eval-Print-Loop or interactive console // by lexing, parsing and evaluating the input in the interpreter -func VmREPL(args []string, debug, trace bool) error { - var state = vm.NewVMState() +// REPL provides a read-eval-print loop for the monkey virtual machine. +func REPL(args []string, debug, trace bool) error { + var state = vm.NewState() object.Args = args @@ -142,9 +143,10 @@ func acceptUntil(t *term.Terminal, start, end string) (string, error) { 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 ( - state = vm.NewVMState() + state = vm.NewState() reader = bufio.NewReader(os.Stdin) )