aboutsummaryrefslogtreecommitdiff
path: root/internal/bible/ref.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/bible/ref.go')
-rw-r--r--internal/bible/ref.go62
1 files changed, 54 insertions, 8 deletions
diff --git a/internal/bible/ref.go b/internal/bible/ref.go
index c88e722..d0a615d 100644
--- a/internal/bible/ref.go
+++ b/internal/bible/ref.go
@@ -8,8 +8,24 @@ import (
var refRe = regexp.MustCompile(`^(.*?)\s+(\d+):(.+)$`)
+// crossChapRangeRe matches a verse group whose range crosses a chapter
+// boundary: "M-P:Q" -- from verse M of the chapter it opens in, through verse
+// Q of chapter P (e.g. "30-28:7" inside "Sirach 27:30-28:7"). Capture groups:
+// 1=M (from-verse), 2=P (to-chapter), 3=Q (to-verse).
+var crossChapRangeRe = regexp.MustCompile(`^(\d+)-(\d+):(\d+)$`)
+
+// noUpperBound stands in for "through the end of the chapter" when filtering
+// verses in Lookup. It is only ever used to bound a loop over verses that
+// already exist in the corpus -- it never appears in a ref string, so it
+// cannot leak into SplitRef's output or FormatRef's display.
+const noUpperBound = 1<<31 - 1
+
// SplitRef splits a ref whose verse list mixes single verses and ranges into
-// one ref per group (the kjv tools reject a mixed list in a single query).
+// one ref per group (the kjv tools reject a mixed list in a single query). A
+// group may itself be a cross-chapter range ("M-P:Q"); it is kept as one
+// group (Lookup expands it), but it also updates the chapter that later
+// bare-verse groups in the list belong to (e.g. "Malachi 1:14-2:2,8-10" ->
+// ["Malachi 1:14-2:2", "Malachi 2:8-10"]).
func SplitRef(ref string) []string {
m := refRe.FindStringSubmatch(ref)
if m == nil {
@@ -20,16 +36,23 @@ func SplitRef(ref string) []string {
return []string{ref}
}
var out []string
+ cur := chap // chapter the next bare (no ":") group belongs to
for _, g := range strings.Split(verses, ",") {
g = strings.TrimSpace(g)
if g == "" {
continue
}
+ if cm := crossChapRangeRe.FindStringSubmatch(g); cm != nil {
+ out = append(out, book+" "+cur+":"+g)
+ cur = cm[2] // groups after this one belong to chapter P
+ continue
+ }
if strings.Contains(g, ":") {
out = append(out, book+" "+g)
- } else {
- out = append(out, book+" "+chap+":"+g)
+ cur = g[:strings.IndexByte(g, ':')]
+ continue
}
+ out = append(out, book+" "+cur+":"+g)
}
return out
}
@@ -51,12 +74,35 @@ func Lookup(version, ref string) ([]Verse, []string) {
continue
}
chap, _ := strconv.Atoi(m[2])
- from, to := verseRange(m[3])
found := false
- for _, v := range Verses(version, book, chap) {
- if v.Verse >= from && v.Verse <= to {
- verses = append(verses, v)
- found = true
+ if cm := crossChapRangeRe.FindStringSubmatch(m[3]); cm != nil {
+ // "M-P:Q": chapter `chap` from verse M to its end, any whole
+ // chapters in between, then chapter P from verse 1 through Q.
+ from, _ := strconv.Atoi(cm[1])
+ toChap, _ := strconv.Atoi(cm[2])
+ toVerse, _ := strconv.Atoi(cm[3])
+ for c := chap; c <= toChap; c++ {
+ lo, hi := 1, noUpperBound
+ if c == chap {
+ lo = from
+ }
+ if c == toChap {
+ hi = toVerse
+ }
+ for _, v := range Verses(version, book, c) {
+ if v.Verse >= lo && v.Verse <= hi {
+ verses = append(verses, v)
+ found = true
+ }
+ }
+ }
+ } else {
+ from, to := verseRange(m[3])
+ for _, v := range Verses(version, book, chap) {
+ if v.Verse >= from && v.Verse <= to {
+ verses = append(verses, v)
+ found = true
+ }
}
}
if !found {