package monkey import ( "errors" "fmt" "io" "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) (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()) } c := compiler.New() c.SetFileInfo(path, input) if err = c.Compile(res); err != nil { return } return c.Bytecode(), nil } func ExecFileVM(f string) (err error) { var bytecode *compiler.Bytecode if filepath.Ext(f) == ".monkeyc" { bytecode, err = decode(f) } else { bytecode, err = compile(f) } if err != nil { fmt.Println(err) return } mvm := vm.New(f, bytecode) if err = mvm.Run(); err != nil { fmt.Println(err) return } return } func CompileFiles(files []string) 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 } c := compiler.New() if err := c.Compile(res); err != nil { fmt.Println(err) continue } cnt, err := c.Bytecode().Encode() if err != nil { fmt.Println(err) continue } ext := filepath.Ext(f) writeFile(f[:len(f)-len(ext)]+".monkeyc", 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("", 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 }