Build with make
This commit is contained in:
107
edit-chapter.go
Normal file
107
edit-chapter.go
Normal file
@@ -0,0 +1,107 @@
|
||||
// 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]`},
|
||||
})
|
||||
|
||||
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 <infile> <outfile>\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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user