pj1/pj1-go/gen-ast.go

113 lines
2.8 KiB
Go
Raw Normal View History

//go:build generate
//go:generate go run ./gen-ast.go ./
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"unicode"
"unicode/utf8"
)
// returns a string which contains the current module path, keep in mind this
// program is meant to be run in the root of the module using go generate
//
// This function is no longer useful because the lexer package has been
// deleted, however, it may become useful again soon
func getModulePath() string {
goModFile, err := os.Open("./go.mod")
if err != nil {
fmt.Fprintln(os.Stderr, "Could not open go.mod file, is this command being run in the module root folder?")
os.Exit(1)
}
scanner := bufio.NewScanner(goModFile)
scanner.Scan()
fstLine := scanner.Text()
if fstLine[:7] != "module " {
fmt.Fprintln(os.Stderr, "The first line of go.mod seems malformed")
os.Exit(1)
}
goModFile.Close()
path := strings.TrimSpace(fstLine[7:])
return path
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: go run gen-ast.go <output directory>")
os.Exit(64)
}
outputDir := os.Args[1]
defineAst(outputDir, "Expr",
[]string{"Binary : left Expr, operator Token, right Expr",
"Grouping : expression Expr",
"Literal : value interface{}",
// a literal can be any value that can be printed
"Unary : operator Token, right Expr"})
}
func lowerFirst(s string) string {
if s == "" {
return ""
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToLower(r)) + s[n:]
}
func defineAst(outputDir string, baseName string, types []string) {
fileName := "ast"
var path string = outputDir + "/" + fileName + ".go"
f, err := os.Create(path)
if err != nil {
fmt.Fprintln(os.Stderr, "Could not open file \""+path+"\"")
return
}
defer f.Close()
fmt.Fprintln(f, "// Code generated by tools/gen-ast.go DO NOT EDIT.")
fmt.Fprintln(f, "package main")
fmt.Fprintln(f)
// Creates a dummy interface just to limit types which can be
// considered an "Expr"
fmt.Fprintln(f, "type "+baseName+" interface {")
fmt.Fprintln(f, "\tis"+baseName+"()")
fmt.Fprintln(f, "}")
fmt.Fprintln(f)
// The AST types.
for _, t := range types {
tSplit := strings.Split(t, ":")
typeName := strings.TrimSpace(tSplit[0])
fields := strings.TrimSpace(tSplit[1])
defineType(f, baseName, typeName, fields)
}
}
func defineType(f io.Writer, baseName string, typeName string, fieldList string) {
fmt.Fprintln(f, "type "+typeName+" struct {")
// Fields.
var fields []string = strings.Split(fieldList, ", ")
for _, field := range fields {
fmt.Fprintln(f, "\t"+field)
}
fmt.Fprintln(f, "}")
fmt.Fprintln(f)
// Interface dummy function
fmt.Fprintln(f, "func (x "+typeName+") is"+baseName+"() {}")
fmt.Fprintln(f)
// TODO: may have to generate constructor, according to top of
// page 110, right now it seems that defining structs without
// the constructor does the job
}