108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
// This is all because I don't want to deal with double-escaping regex special
|
||
// characters in sh.
|
||
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"log"
|
||
"os"
|
||
"path/filepath"
|
||
"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]`},
|
||
})
|
||
|
||
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 {
|
||
paths, err := filepath.Glob("original/*.markdown")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for _, path := range paths {
|
||
b, err := os.ReadFile(path)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
var edited = b
|
||
for _, r := range replacers {
|
||
edited = r.re.ReplaceAll(edited, []byte(r.replacement))
|
||
}
|
||
edited = footnoteRE.ReplaceAllFunc(edited, replaceFootnote)
|
||
|
||
outpath := filepath.Join("edited", filepath.Base(path))
|
||
err = os.WriteFile(outpath, edited, 0666)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func main() {
|
||
err := run()
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|