// This is all because I don't want to deal with double-escaping regex special // characters in sh. package main import ( "bytes" "fmt" "os" "regexp" ) func mapSlice[A, B any](f func(A) B, a []A) []B { b := make([]B, len(a)) for i := range b { b[i] = f(a[i]) } return b } type Replacer struct { re *regexp.Regexp replacement string } func compile(raw [2]string) Replacer { return Replacer{ re: regexp.MustCompile(raw[0]), replacement: raw[1], } } var replacers = mapSlice(compile, [][2]string{ // Chapter titles // {`CHAPTER [IVX]+\.` + "\n\n" + `(.*)\.`, `# $1`}, // Section titles // {`(?ms)SECTION [IVX]+\.\n\n_([^_]+)\._`, `## $1`}, // Untitled sections // {`SECTION [IVX]+\.`, `\section{}`}, // Em dashes {"--", "—"}, // Left double typographical quote {`"(\w|_\w)`, `“$1`}, // Right double typographical quote {`"`, `”`}, // Left single typographical quote {`([^\pL])'(\pL|_)`, `$1‘$2`}, {`(?m)^'`, `‘`}, // Right single typographical quote {`'`, `’`}, // Block quotes {`(?m)^ +(\S)`, `> $1`}, // Footnote superscript {`\[(\d+)\]`, `[^$1]`}, // {`\[(\d+)\]`, `\footnotemark[$1]`}, // experimental footnote // {`\[Footnote (\d+): ((?:[^\[\]]|(?:\[\^\d+\]))+)\]`, `\footnotetext[$1]{$2}`}, }) var footnoteRE = regexp.MustCompile(`\[Footnote \d+: ([^\[\]]|(\[\^\d+\]))+\]`) var footnoteNumberRE = regexp.MustCompile(`\[Footnote (\d+):`) func replaceFootnote(src []byte) []byte { var formatted = footnoteNumberRE.ReplaceAll(src, []byte("[^$1]:")) // Indent footnote paragraphs. // This only works because footnotes are stacked at the end of each chapter. formatted = bytes.ReplaceAll(formatted, []byte("\n"), []byte("\n ")) // Strip terminal "]". return formatted[:len(formatted)-1] } func run() error { if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, "usage: %s \n", os.Args[0]) os.Exit(64) // command line usage error } inpath := os.Args[1] outpath := os.Args[2] text, err := os.ReadFile(inpath) if err != nil { return err } for _, r := range replacers { text = r.re.ReplaceAll(text, []byte(r.replacement)) } text = footnoteRE.ReplaceAllFunc(text, replaceFootnote) err = os.WriteFile(outpath, text, 0666) if err != nil { return err } return nil } func main() { err := run() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }