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.
100 lines
2.9 KiB
Go
100 lines
2.9 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",
|
|
"Call : Callee Expr, Paren token.Token, Arguments []Expr",
|
|
"Grouping : Expression Expr",
|
|
"Literal : Value any",
|
|
"Logical : Left Expr, Operator token.Token, Right Expr",
|
|
"Unary : Operator token.Token, Right Expr",
|
|
"Variable : Name token.Token",
|
|
})
|
|
|
|
defineAst(d, "Stmt", []string{
|
|
"Error : Value string",
|
|
"Block : Statements []Stmt",
|
|
"Expression : Expression Expr",
|
|
"Function : Name token.Token, Params []token.Token, Body []Stmt",
|
|
"If : Condition Expr, ThenBranch Stmt, ElseBranch Stmt",
|
|
"Print : Expression Expr",
|
|
"Return : Keyword token.Token, Value Expr",
|
|
"Var : Name token.Token, Initializer Expr",
|
|
"While : Condition Expr, Body Stmt",
|
|
})
|
|
}
|
|
|
|
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")
|
|
}
|