You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
2.6 KiB
Go

package main
import (
"fmt"
"os"
"strings"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: astgen <output_directory>")
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",
"Assign : Name token.Token, Value Expr",
"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",
"Block : Statements []Stmt",
"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")
}