lots o fixes
This commit is contained in:
188
monkey.go
188
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("<stdin>", 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("<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" {
|
||||
bytecode, err = decode(f)
|
||||
if debug {
|
||||
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 {
|
||||
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("<stdin>", 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("<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
|
||||
fmt.Fprintf(w, "Monkey %s on %s\n", MonkeyVersion, runtime.GOOS)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user