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, trace 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 mvm.Trace = trace 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("", 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 }