trial-by-jury-book/edit-chapter.go

112 lines
2.3 KiB
Go
Raw Permalink Normal View History

2023-08-03 21:50:47 +00:00
// This is all because I don't want to deal with double-escaping regex special
// characters in sh.
package main
import (
2023-08-04 00:46:41 +00:00
"bytes"
2023-08-04 01:19:07 +00:00
"fmt"
2023-08-03 21:50:47 +00:00
"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`},
2023-08-03 21:50:47 +00:00
// Section titles
// {`(?ms)SECTION [IVX]+\.\n\n_([^_]+)\._`, `## $1`},
2023-08-03 21:50:47 +00:00
// Untitled sections
// {`SECTION [IVX]+\.`, `\section{}`},
2023-08-03 21:50:47 +00:00
// 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`},
2023-08-04 00:46:41 +00:00
// Footnote superscript
{`\[(\d+)\]`, `[^$1]`},
// {`\[(\d+)\]`, `\footnotemark[$1]`},
// experimental footnote
// {`\[Footnote (\d+): ((?:[^\[\]]|(?:\[\^\d+\]))+)\]`, `\footnotetext[$1]{$2}`},
2023-08-03 21:50:47 +00:00
})
2023-08-04 00:46:41 +00:00
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]
}
2023-08-03 21:50:47 +00:00
func run() error {
2023-08-04 01:19:07 +00:00
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)
2023-08-03 21:50:47 +00:00
if err != nil {
return err
}
2023-08-04 01:19:07 +00:00
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
2023-08-03 21:50:47 +00:00
}
2023-08-04 01:19:07 +00:00
2023-08-03 21:50:47 +00:00
return nil
}
func main() {
err := run()
if err != nil {
2023-08-04 01:19:07 +00:00
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
2023-08-03 21:50:47 +00:00
}
}