160 lines
2.7 KiB
Go
160 lines
2.7 KiB
Go
package monkey
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"monkey/internal/object"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"monkey/internal/ast"
|
|
"monkey/internal/compiler"
|
|
"monkey/internal/parser"
|
|
"monkey/internal/vm"
|
|
)
|
|
|
|
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)
|
|
}
|
|
return b
|
|
}
|
|
|
|
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)
|
|
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)
|
|
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(path, input)
|
|
if err = c.Compile(res); err != nil {
|
|
return
|
|
}
|
|
|
|
return c.Bytecode(), nil
|
|
}
|
|
|
|
func ExecFileVM(f string, args []string, debug bool) (err error) {
|
|
var bytecode *compiler.Bytecode
|
|
|
|
object.Args = args
|
|
|
|
if filepath.Ext(f) == ".mc" {
|
|
bytecode, err = decode(f)
|
|
} else {
|
|
bytecode, err = compile(f, debug)
|
|
}
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
mvm := vm.New(f, bytecode)
|
|
mvm.Debug = debug
|
|
if err = mvm.Run(); err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func CompileFiles(files []string, debug bool) error {
|
|
for _, f := range files {
|
|
b := mustReadFile(f)
|
|
|
|
res, errs := parser.Parse(f, string(b))
|
|
if len(errs) != 0 {
|
|
for _, e := range errs {
|
|
fmt.Println(e)
|
|
}
|
|
return ErrParseError
|
|
}
|
|
|
|
if debug {
|
|
log.Printf("AST:\n%s\n", res)
|
|
}
|
|
|
|
c := compiler.New()
|
|
c.Debug = debug
|
|
if err := c.Compile(res); err != nil {
|
|
fmt.Println(err)
|
|
continue
|
|
}
|
|
|
|
if debug {
|
|
log.Printf("Bytecode:\n%s\n", c.Bytecode())
|
|
}
|
|
|
|
cnt, err := c.Bytecode().Encode()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
continue
|
|
}
|
|
|
|
ext := filepath.Ext(f)
|
|
writeFile(f[:len(f)-len(ext)]+".mc", cnt)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|