Extra Features
This commit is contained in:
277
repl/repl.go
277
repl/repl.go
@@ -1,69 +1,29 @@
|
||||
package repl
|
||||
|
||||
// Package repl implements the Read-Eval-Print-Loop or interactive console
|
||||
// by lexing, parsing and evaluating the input in the interpreter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"monkey/compiler"
|
||||
"monkey/evaluator"
|
||||
"monkey/lexer"
|
||||
"monkey/object"
|
||||
"monkey/parser"
|
||||
"monkey/vm"
|
||||
"os"
|
||||
)
|
||||
|
||||
// PROMPT is the REPL prompt displayed for each input
|
||||
const PROMPT = ">> "
|
||||
|
||||
func Start(in io.Reader, out io.Writer) {
|
||||
scanner := bufio.NewScanner(in)
|
||||
|
||||
constants := []object.Object{}
|
||||
globals := make([]object.Object, vm.GlobalsSize)
|
||||
symbolTable := compiler.NewSymbolTable()
|
||||
for i, v := range object.Builtins {
|
||||
symbolTable.DefineBuiltin(i, v.Name)
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Fprintf(out, PROMPT)
|
||||
scanned := scanner.Scan()
|
||||
if !scanned {
|
||||
return
|
||||
}
|
||||
|
||||
line := scanner.Text()
|
||||
l := lexer.New(line)
|
||||
p := parser.New(l)
|
||||
|
||||
program := p.ParseProgram()
|
||||
if len(p.Errors()) != 0 {
|
||||
printParserErrors(out, p.Errors())
|
||||
continue
|
||||
}
|
||||
|
||||
comp := compiler.NewWithState(symbolTable, constants)
|
||||
err := comp.Compile(program)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Woops! Compilation failed:\n %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
code := comp.Bytecode()
|
||||
constants = code.Constants
|
||||
|
||||
machine := vm.NewWithGlobalState(comp.Bytecode(), globals)
|
||||
err = machine.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Woops! Executing bytecode failed:\n %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
stackTop := machine.LastPoppedStackElem()
|
||||
io.WriteString(out, stackTop.Inspect())
|
||||
io.WriteString(out, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
const MONKEY_FACE = ` __,__
|
||||
// MonkeyFace is the REPL's face of shock and horror when you encounter a
|
||||
// parser error :D
|
||||
const MonkeyFace = ` __,__
|
||||
.--. .-" "-. .--.
|
||||
/ .. \/ .-. .-. \/ .. \
|
||||
| | '| / Y \ |' | |
|
||||
@@ -76,11 +36,222 @@ const MONKEY_FACE = ` __,__
|
||||
'-----'
|
||||
`
|
||||
|
||||
type Options struct {
|
||||
Debug bool
|
||||
Engine string
|
||||
Interactive bool
|
||||
}
|
||||
|
||||
type VMState struct {
|
||||
constants []object.Object
|
||||
globals []object.Object
|
||||
symbols *compiler.SymbolTable
|
||||
}
|
||||
|
||||
func NewVMState() *VMState {
|
||||
symbolTable := compiler.NewSymbolTable()
|
||||
for i, v := range object.Builtins {
|
||||
symbolTable.DefineBuiltin(i, v.Name)
|
||||
}
|
||||
|
||||
return &VMState{
|
||||
constants: []object.Object{},
|
||||
globals: make([]object.Object, vm.GlobalsSize),
|
||||
symbols: symbolTable,
|
||||
}
|
||||
}
|
||||
|
||||
type REPL struct {
|
||||
user string
|
||||
args []string
|
||||
opts *Options
|
||||
}
|
||||
|
||||
func New(user string, args []string, opts *Options) *REPL {
|
||||
return &REPL{user, args, opts}
|
||||
}
|
||||
|
||||
// Eval parses and evalulates the program given by f and returns the resulting
|
||||
// environment, any errors are printed to stderr
|
||||
func (r *REPL) Eval(f io.Reader) (env *object.Environment) {
|
||||
env = object.NewEnvironment()
|
||||
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading source file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
l := lexer.New(string(b))
|
||||
p := parser.New(l)
|
||||
|
||||
program := p.ParseProgram()
|
||||
if len(p.Errors()) != 0 {
|
||||
printParserErrors(os.Stderr, p.Errors())
|
||||
return
|
||||
}
|
||||
|
||||
evaluator.Eval(program, env)
|
||||
return
|
||||
}
|
||||
|
||||
// Exec parses, compiles and executes the program given by f and returns
|
||||
// the resulting virtual machine, any errors are printed to stderr
|
||||
func (r *REPL) Exec(f io.Reader) (state *VMState) {
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading source file: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
state = NewVMState()
|
||||
|
||||
l := lexer.New(string(b))
|
||||
p := parser.New(l)
|
||||
|
||||
program := p.ParseProgram()
|
||||
if len(p.Errors()) != 0 {
|
||||
printParserErrors(os.Stderr, p.Errors())
|
||||
return
|
||||
}
|
||||
|
||||
c := compiler.NewWithState(state.symbols, state.constants)
|
||||
err = c.Compile(program)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Woops! Compilation failed:\n %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
code := c.Bytecode()
|
||||
state.constants = code.Constants
|
||||
|
||||
machine := vm.NewWithGlobalState(code, state.globals)
|
||||
err = machine.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Woops! Executing bytecode failed:\n %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// StartEvalLoop starts the REPL in a continious eval loop
|
||||
func (r *REPL) StartEvalLoop(in io.Reader, out io.Writer, env *object.Environment) {
|
||||
scanner := bufio.NewScanner(in)
|
||||
|
||||
if env == nil {
|
||||
env = object.NewEnvironment()
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Printf(PROMPT)
|
||||
scanned := scanner.Scan()
|
||||
if !scanned {
|
||||
return
|
||||
}
|
||||
|
||||
line := scanner.Text()
|
||||
|
||||
l := lexer.New(line)
|
||||
p := parser.New(l)
|
||||
|
||||
program := p.ParseProgram()
|
||||
if len(p.Errors()) != 0 {
|
||||
printParserErrors(out, p.Errors())
|
||||
continue
|
||||
}
|
||||
|
||||
obj := evaluator.Eval(program, env)
|
||||
if obj != nil {
|
||||
io.WriteString(out, obj.Inspect())
|
||||
io.WriteString(out, "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartExecLoop starts the REPL in a continious exec loop
|
||||
func (r *REPL) StartExecLoop(in io.Reader, out io.Writer, state *VMState) {
|
||||
scanner := bufio.NewScanner(in)
|
||||
|
||||
if state == nil {
|
||||
state = NewVMState()
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Printf(PROMPT)
|
||||
scanned := scanner.Scan()
|
||||
if !scanned {
|
||||
return
|
||||
}
|
||||
|
||||
line := scanner.Text()
|
||||
|
||||
l := lexer.New(line)
|
||||
p := parser.New(l)
|
||||
|
||||
program := p.ParseProgram()
|
||||
if len(p.Errors()) != 0 {
|
||||
printParserErrors(out, p.Errors())
|
||||
continue
|
||||
}
|
||||
|
||||
c := compiler.NewWithState(state.symbols, state.constants)
|
||||
err := c.Compile(program)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Woops! Compilation failed:\n %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
code := c.Bytecode()
|
||||
state.constants = code.Constants
|
||||
|
||||
machine := vm.NewWithGlobalState(code, state.globals)
|
||||
err = machine.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Woops! Executing bytecode failed:\n %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
stackTop := machine.LastPoppedStackElem()
|
||||
io.WriteString(out, stackTop.Inspect())
|
||||
io.WriteString(out, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *REPL) Run() {
|
||||
if len(r.args) == 1 {
|
||||
f, err := os.Open(r.args[0])
|
||||
if err != nil {
|
||||
log.Fatalf("could not open source file %s: %s", r.args[0], err)
|
||||
}
|
||||
|
||||
if r.opts.Engine == "eval" {
|
||||
env := r.Eval(f)
|
||||
if r.opts.Interactive {
|
||||
r.StartEvalLoop(os.Stdin, os.Stdout, env)
|
||||
}
|
||||
} else {
|
||||
state := r.Exec(f)
|
||||
if r.opts.Interactive {
|
||||
r.StartExecLoop(os.Stdin, os.Stdout, state)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Hello %s! This is the Monkey programming language!\n", r.user)
|
||||
fmt.Printf("Feel free to type in commands\n")
|
||||
if r.opts.Engine == "eval" {
|
||||
r.StartEvalLoop(os.Stdin, os.Stdout, nil)
|
||||
} else {
|
||||
r.StartExecLoop(os.Stdin, os.Stdout, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printParserErrors(out io.Writer, errors []string) {
|
||||
io.WriteString(out, MONKEY_FACE)
|
||||
io.WriteString(out, MonkeyFace)
|
||||
io.WriteString(out, "Woops! We ran into some monkey business here!\n")
|
||||
io.WriteString(out, " parser errors:\n")
|
||||
for _, msg := range errors {
|
||||
io.WriteString(out, "\t"+msg+"\t")
|
||||
io.WriteString(out, "\t"+msg+"\n")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user