Files
monkey/monkey.go
csmith 862119e90e
Some checks failed
Build / build (push) Successful in 9m50s
Publish Image / publish (push) Failing after 49s
Test / build (push) Successful in 10m55s
lots o fixes
2024-04-01 18:18:45 -04:00

218 lines
4.1 KiB
Go

// Package monkey provides a virtual machine for executing Monkey programs.
package monkey
import (
"errors"
"fmt"
"io"
"log"
"monkey/internal/object"
"os"
"path/filepath"
"runtime"
"strings"
"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"
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func decodeCompiledFile(fn string) (*compiler.Bytecode, error) {
b, err := os.ReadFile(fn)
if err != nil {
return nil, err
}
return compiler.Decode(b), nil
}
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
for _, e := range errs {
buf.WriteString(e)
buf.WriteByte('\n')
}
return nil, errors.New(buf.String())
}
if debug {
log.Printf("AST:\n%s\n", res)
}
c := compiler.New()
c.Debug = debug
c.SetFileInfo("<stdin>", string(input))
if err := c.Compile(res); err != nil {
return nil, err
}
return c.Bytecode(), nil
}
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
for _, e := range errs {
buf.WriteString(e)
buf.WriteByte('\n')
}
return nil, errors.New(buf.String())
}
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 {
input, err := os.ReadFile(fn)
if err != nil {
return err
}
bytecode, err = compileString(string(input), debug)
}
if err != nil {
return err
}
if debug {
log.Printf("Bytecode:\n%s\n", 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 {
return err
}
return nil
}
// 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
for _, e := range errs {
buf.WriteString(e)
buf.WriteByte('\n')
}
return errors.New(buf.String())
}
if debug {
log.Printf("AST:\n%s\n", res)
}
c := compiler.New()
c.Debug = debug
if err := c.Compile(res); err != nil {
return err
}
if debug {
log.Printf("Bytecode:\n%s\n", c.Bytecode())
}
data, err := c.Bytecode().Encode()
if err != nil {
return err
}
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, runtime.GOOS)
}