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 }