// The compiler package main import ( "log" "os" "strconv" "strings" ) const ERROR_LOG_LINES = 5 func countTabs(line string) int { tabs := 0 for _, rune := range line { if rune == '\t' { tabs++ } } return tabs } func printCompilerError(file string, source string, err CompilerError) { sourceRunes := []rune(source) lines := strings.Split(source, "\n") line := 0 col := 0 var i uint64 for i = 0; i < err.Position; i++ { col++ if sourceRunes[i] == '\n' { line++ col = 0 } } log.Println("Failed to compile: " + err.Message + " (at " + file + ":" + strconv.Itoa(line+1) + ":" + strconv.Itoa(col+1) + ")") linesStart := max(0, line-ERROR_LOG_LINES) linesEnd := min(len(lines), line+ERROR_LOG_LINES+1) for _, line := range lines[linesStart:line] { println(strings.Replace(line, "\t", " ", -1)) } tabs := countTabs(lines[line]) println(strings.Repeat(" ", col+tabs*3) + "v--- error occurs here ---") for _, line := range lines[line:linesEnd] { println(strings.Replace(line, "\t", " ", -1)) } } func main() { if len(os.Args) != 2 { log.Fatalln("Usage: " + os.Args[0] + " ") } file := os.Args[1] content, err := os.ReadFile(file) if err != nil { log.Fatalln("Cannot open input file.", err) } source := string(content) tokens, err := lexer(source) if err != nil { if c, ok := err.(CompilerError); ok { printCompilerError(file, source, c) } else { log.Println(err) } return } log.Printf("Tokens:\n%+#v\n\n", tokens) parser := Parser{Tokens: tokens} parsed, err := parser.parseFile() if err != nil { if c, ok := err.(CompilerError); ok { printCompilerError(file, source, c) } else { log.Println(err) } return } log.Printf("Parsed:\n%+#v\n\n", parsed) validator := Validator{file: parsed} errors := validator.validate() if len(errors) != 0 { for _, err = range errors { if c, ok := err.(CompilerError); ok { printCompilerError(file, source, c) } else { log.Println(err) } } if len(errors) != 0 { return } } log.Printf("Validated:\n%+#v\n\n", parsed) wat, err := backendWAT(*parsed) if err != nil { if c, ok := err.(CompilerError); ok { printCompilerError(file, source, c) } else { log.Println(err) } return } log.Println("WAT: " + wat) os.WriteFile("out.wat", []byte(wat), 0o644) }