diff options
Diffstat (limited to 'cmd/extwiki/main.go')
-rw-r--r-- | cmd/extwiki/main.go | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/cmd/extwiki/main.go b/cmd/extwiki/main.go new file mode 100644 index 0000000..76e0f3a --- /dev/null +++ b/cmd/extwiki/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "flag" + "fmt" + "maps" + "os" + "path/filepath" + "slices" + "sort" + "text/template/parse" +) + +var ( + titles = make(map[string]struct{}) + configs = map[string]int{"Wikipedia": 1} +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n", + filepath.Base(os.Args[0])) + flag.PrintDefaults() +} + +func main() { + try(os.Chdir(filepath.Dir(os.Args[0]))) + + outpath := flag.String("out", "-", "output file") + flag.Usage = usage + flag.Parse() + + if flag.NArg() < 1 { + flag.Usage() + os.Exit(1) + } + + var outfile *os.File + if *outpath == "-" { + outfile = os.Stdout + } else { + outfile = try2(os.Create(*outpath)) + } + + for _, f := range flag.Args() { + process(f) + } + + fmt.Fprint(outfile, `// Code generated by extwiki. DO NOT EDIT. + +package wikipedia + +var extractedTitles = [...]string{ +`) + xs := slices.Collect(maps.Keys(titles)) + sort.Strings(xs) + for _, t := range xs { + fmt.Fprintf(outfile, "%q,\n", t) + } + fmt.Fprint(outfile, "}\n") +} + +func process(path string) { + currentFile := try2(os.ReadFile(path)) + trees := make(map[string]*parse.Tree) + t := parse.New(path) + t.Mode |= parse.ParseComments | parse.SkipFuncCheck + try2(t.Parse(string(currentFile), "", "", trees)) + for _, t := range trees { + processNode(t.Root) + } +} + +func processNode(node parse.Node) { + switch n := node.(type) { + case *parse.ListNode: + for _, m := range n.Nodes { + processNode(m) + } + case *parse.PipeNode: + for _, m := range n.Cmds { + processNode(m) + } + case *parse.TemplateNode: + processNode(n.Pipe) + case *parse.IfNode: + processBranch(n.BranchNode) + case *parse.RangeNode: + processBranch(n.BranchNode) + case *parse.WithNode: + processBranch(n.BranchNode) + case *parse.BranchNode: + processBranch(*n) + case *parse.ActionNode: + processNode(n.Pipe) + case *parse.CommandNode: + if len(n.Args) == 0 { + break + } + + var funcname string + f, ok := n.Args[0].(*parse.FieldNode) + if !ok || len(f.Ident) == 0 { + ff, ok := n.Args[0].(*parse.VariableNode) + if !ok || len(ff.Ident) == 0 { + fff, ok := n.Args[0].(*parse.IdentifierNode) + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + funcname = fff.Ident + } else { + funcname = ff.Ident[len(ff.Ident)-1] + } + } else { + funcname = f.Ident[len(f.Ident)-1] + } + + i, ok := configs[funcname] + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + + if sn, ok := n.Args[i].(*parse.StringNode); ok { + titles[sn.Text] = struct{}{} + } + } +} + +func processBranch(n parse.BranchNode) { + processNode(n.List) + if n.ElseList != nil { + processNode(n.ElseList) + } +} + +func try(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err) + os.Exit(1) + } +} + +func try2[T any](val T, err error) T { + try(err) + return val +} |