package main import ( "fmt" "os" "strings" ) func main() { if len(os.Args) != 2 { fmt.Println("Usage: astgen ") os.Exit(64) } d := os.Args[1] if _, err := os.Stat(d); os.IsNotExist(err) { fmt.Println("Directory does not exist") os.Exit(74) } fmt.Println("Generating AST classes in", d) defineAst(d, "Expr", []string{ "Error : Value string", "Binary : Left Expr, Operator token.Token, Right Expr", "Grouping : Expression Expr", "Literal : Value any", "Unary : Operator token.Token, Right Expr", "Variable : Name token.Token", }) defineAst(d, "Stmt", []string{ "Error : Value string", "Expression : Expression Expr", "Print : Expression Expr", "Var : Name token.Token, Initializer Expr", }) } func defineAst(outputDir, baseName string, types []string) { path := outputDir + "/" + strings.ToLower(baseName) + ".go" file, err := os.Create(path) if err != nil { fmt.Println("Error creating file", path) os.Exit(74) } defer file.Close() file.WriteString("package ast\n\n") file.WriteString("import \"golox/token\"\n\n") defineVisitor(file, baseName, types) file.WriteString("type " + baseName + " interface {\n") file.WriteString(" Accept(visitor " + baseName + "Visitor[any]) any\n") file.WriteString("}\n\n") for _, t := range types { defineType(file, baseName, t) } } func defineVisitor(file *os.File, baseName string, types []string) { file.WriteString("type " + baseName + "Visitor[T any] interface {\n") for _, t := range types { typeName := strings.TrimSpace(t[:strings.Index(t, ":")-1]) paramName := strings.ToLower(typeName[:1]) + strings.ToLower(baseName[:1]) file.WriteString(" Visit" + typeName + baseName + "(" + paramName + " *" + typeName + baseName + ") T\n") } file.WriteString("}\n\n") } func defineType(file *os.File, baseName, typeString string) { typeName := strings.TrimSpace(typeString[:strings.Index(typeString, ":")-1]) fields := strings.TrimSpace(typeString[strings.Index(typeString, ":")+1:]) file.WriteString("type " + typeName + baseName + " struct {\n") for _, field := range strings.Split(fields, ", ") { file.WriteString(" " + field + "\n") } varName := strings.ToLower(typeName[:1]) + strings.ToLower(baseName[:1]) file.WriteString("}\n\n") file.WriteString("func (" + varName + " *" + typeName + baseName + ") Accept(v " + baseName + "Visitor[any]) any {\n") file.WriteString(" return v.Visit" + typeName + baseName + "(" + varName + ")\n") file.WriteString("}\n\n") }