Preserve layout in embedded Bible data
This commit is contained in:
+117
-32
@@ -2,11 +2,27 @@ import std/[strutils, tables]
|
||||
|
||||
import ./reference_parser
|
||||
|
||||
type BibleIndex = object
|
||||
verses: Table[string, string]
|
||||
lastVerseByChapter: Table[string, int]
|
||||
lastChapterByBook: Table[string, int]
|
||||
translationName: string
|
||||
type
|
||||
SegmentBreak = enum
|
||||
sbSame = "same"
|
||||
sbLine = "line"
|
||||
sbParagraph = "paragraph"
|
||||
|
||||
BibleSegment = object
|
||||
code: string
|
||||
chapter: int
|
||||
verse: int
|
||||
segment: int
|
||||
breakKind: SegmentBreak
|
||||
indent: int
|
||||
text: string
|
||||
|
||||
BibleIndex = object
|
||||
segments: seq[BibleSegment]
|
||||
verses: Table[string, bool]
|
||||
lastVerseByChapter: Table[string, int]
|
||||
lastChapterByBook: Table[string, int]
|
||||
translationName: string
|
||||
|
||||
proc verseKey(code: string, chapter, verse: int): string =
|
||||
code & "\t" & $chapter & "\t" & $verse
|
||||
@@ -14,6 +30,27 @@ proc verseKey(code: string, chapter, verse: int): string =
|
||||
proc chapterKey(code: string, chapter: int): string =
|
||||
code & "\t" & $chapter
|
||||
|
||||
proc parseSegmentBreak(s: string): SegmentBreak =
|
||||
case s
|
||||
of "same": sbSame
|
||||
of "line": sbLine
|
||||
of "paragraph": sbParagraph
|
||||
else:
|
||||
raise newException(ValueError, "invalid embedded Bible segment break: " & s)
|
||||
|
||||
proc addSegment(index: var BibleIndex, segment: BibleSegment) =
|
||||
index.segments.add(segment)
|
||||
index.verses[verseKey(segment.code, segment.chapter, segment.verse)] = true
|
||||
|
||||
let cKey = chapterKey(segment.code, segment.chapter)
|
||||
if not index.lastVerseByChapter.hasKey(cKey) or
|
||||
segment.verse > index.lastVerseByChapter[cKey]:
|
||||
index.lastVerseByChapter[cKey] = segment.verse
|
||||
|
||||
if not index.lastChapterByBook.hasKey(segment.code) or
|
||||
segment.chapter > index.lastChapterByBook[segment.code]:
|
||||
index.lastChapterByBook[segment.code] = segment.chapter
|
||||
|
||||
proc loadBibleIndex(rows, translationName: string): BibleIndex =
|
||||
result.translationName = translationName
|
||||
|
||||
@@ -21,26 +58,29 @@ proc loadBibleIndex(rows, translationName: string): BibleIndex =
|
||||
if line.strip.len == 0:
|
||||
continue
|
||||
|
||||
let parts = line.split('\t', maxsplit = 3)
|
||||
if parts.len != 4:
|
||||
let parts = line.split('\t', maxsplit = 6)
|
||||
if parts.len != 4 and parts.len != 7:
|
||||
raise newException(ValueError,
|
||||
"invalid embedded " & translationName & " row: " & line)
|
||||
|
||||
let code = parts[0]
|
||||
let chapter = parseInt(parts[1])
|
||||
let verse = parseInt(parts[2])
|
||||
let text = parts[3]
|
||||
|
||||
result.verses[verseKey(code, chapter, verse)] = text
|
||||
|
||||
let cKey = chapterKey(code, chapter)
|
||||
if not result.lastVerseByChapter.hasKey(cKey) or
|
||||
verse > result.lastVerseByChapter[cKey]:
|
||||
result.lastVerseByChapter[cKey] = verse
|
||||
|
||||
if not result.lastChapterByBook.hasKey(code) or
|
||||
chapter > result.lastChapterByBook[code]:
|
||||
result.lastChapterByBook[code] = chapter
|
||||
if parts.len == 4:
|
||||
result.addSegment(BibleSegment(
|
||||
code: parts[0],
|
||||
chapter: parseInt(parts[1]),
|
||||
verse: parseInt(parts[2]),
|
||||
segment: 0,
|
||||
breakKind: sbLine,
|
||||
indent: 0,
|
||||
text: parts[3]))
|
||||
else:
|
||||
result.addSegment(BibleSegment(
|
||||
code: parts[0],
|
||||
chapter: parseInt(parts[1]),
|
||||
verse: parseInt(parts[2]),
|
||||
segment: parseInt(parts[3]),
|
||||
breakKind: parseSegmentBreak(parts[4]),
|
||||
indent: parseInt(parts[5]),
|
||||
text: parts[6]))
|
||||
|
||||
proc requireLastChapter(index: BibleIndex, code: string): int =
|
||||
if not index.lastChapterByBook.hasKey(code):
|
||||
@@ -62,13 +102,10 @@ proc requireVerse(index: BibleIndex, code: string, chapter, verse: int): string
|
||||
raise newException(ValueError,
|
||||
"no embedded " & index.translationName & " data for " &
|
||||
bookInfo(code).name & " " & $chapter & ":" & $verse)
|
||||
index.verses[vKey]
|
||||
""
|
||||
|
||||
proc addVerseLines(
|
||||
lines: var seq[string],
|
||||
index: BibleIndex,
|
||||
reference: PassageReference,
|
||||
range: RefRange) =
|
||||
proc selectedVerses(index: BibleIndex, reference: PassageReference, range: RefRange):
|
||||
Table[string, bool] =
|
||||
|
||||
let code = reference.book.code
|
||||
discard index.requireLastChapter(code)
|
||||
@@ -90,16 +127,64 @@ proc addVerseLines(
|
||||
raise newException(ValueError, "reference range starts after it ends")
|
||||
|
||||
for verse in startVerse .. endVerse:
|
||||
lines.add(" [" & $verse & "] " & index.requireVerse(code, chapter, verse))
|
||||
discard index.requireVerse(code, chapter, verse)
|
||||
result[verseKey(code, chapter, verse)] = true
|
||||
|
||||
proc addFormattedSegment(
|
||||
lines: var seq[string],
|
||||
segment: BibleSegment,
|
||||
emittedVerses: var Table[string, bool]) =
|
||||
|
||||
let vKey = verseKey(segment.code, segment.chapter, segment.verse)
|
||||
let marker =
|
||||
if emittedVerses.hasKey(vKey): ""
|
||||
else:
|
||||
emittedVerses[vKey] = true
|
||||
"[" & $segment.verse & "] "
|
||||
|
||||
let prefix = repeat(" ", segment.indent + 1)
|
||||
let content = marker & segment.text
|
||||
let hasPassageLine = lines.len > 1 and lines[^1].len > 0
|
||||
|
||||
if segment.breakKind == sbSame and hasPassageLine:
|
||||
if lines[^1].len > 0 and not lines[^1][^1].isSpaceAscii:
|
||||
lines[^1].add(" ")
|
||||
lines[^1].add(content)
|
||||
else:
|
||||
if segment.breakKind == sbParagraph and hasPassageLine:
|
||||
lines.add("")
|
||||
lines.add(prefix & content)
|
||||
|
||||
proc addVerseLines(
|
||||
lines: var seq[string],
|
||||
index: BibleIndex,
|
||||
reference: PassageReference,
|
||||
range: RefRange) =
|
||||
|
||||
let code = reference.book.code
|
||||
let selected = index.selectedVerses(reference, range)
|
||||
var emittedVerses: Table[string, bool]
|
||||
var firstSelected = true
|
||||
|
||||
for segment in index.segments:
|
||||
if segment.code == code and
|
||||
selected.hasKey(verseKey(segment.code, segment.chapter, segment.verse)):
|
||||
var adjusted = segment
|
||||
if firstSelected and adjusted.breakKind == sbSame:
|
||||
adjusted.breakKind = sbLine
|
||||
lines.addFormattedSegment(adjusted, emittedVerses)
|
||||
firstSelected = false
|
||||
|
||||
proc fetchReference(index: BibleIndex, reference: PassageReference): string =
|
||||
var lines = @[$reference]
|
||||
let code = reference.book.code
|
||||
|
||||
if reference.ranges.len == 0:
|
||||
for chapter in 1 .. index.requireLastChapter(code):
|
||||
for verse in 1 .. index.requireLastVerse(code, chapter):
|
||||
lines.add(" [" & $verse & "] " & index.requireVerse(code, chapter, verse))
|
||||
var emittedVerses: Table[string, bool]
|
||||
discard index.requireLastChapter(code)
|
||||
for segment in index.segments:
|
||||
if segment.code == code:
|
||||
lines.addFormattedSegment(segment, emittedVerses)
|
||||
else:
|
||||
for range in reference.ranges:
|
||||
lines.addVerseLines(index, reference, range)
|
||||
|
||||
Reference in New Issue
Block a user