import std/[strutils, tables] import ./offline_data import ./reference_parser const kjvRows = embeddedTranslationData("kjv") type BibleIndex = object verses: Table[string, string] lastVerseByChapter: Table[string, int] lastChapterByBook: Table[string, int] proc verseKey(code: string, chapter, verse: int): string = code & "\t" & $chapter & "\t" & $verse proc chapterKey(code: string, chapter: int): string = code & "\t" & $chapter proc loadBibleIndex(): BibleIndex = for line in kjvRows.splitLines: if line.strip.len == 0: continue let parts = line.split('\t', maxsplit = 3) if parts.len != 4: raise newException(ValueError, "invalid embedded KJV 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 proc requireLastChapter(index: BibleIndex, code: string): int = if not index.lastChapterByBook.hasKey(code): raise newException(ValueError, "no embedded KJV data for " & code) index.lastChapterByBook[code] proc requireLastVerse(index: BibleIndex, code: string, chapter: int): int = let cKey = chapterKey(code, chapter) if not index.lastVerseByChapter.hasKey(cKey): raise newException(ValueError, "no embedded KJV data for " & bookInfo(code).name & " " & $chapter) index.lastVerseByChapter[cKey] proc requireVerse(index: BibleIndex, code: string, chapter, verse: int): string = let vKey = verseKey(code, chapter, verse) if not index.verses.hasKey(vKey): raise newException(ValueError, "no embedded KJV data for " & bookInfo(code).name & " " & $chapter & ":" & $verse) index.verses[vKey] proc addVerseLines( lines: var seq[string], index: BibleIndex, reference: PassageReference, range: RefRange) = let code = reference.book.code discard index.requireLastChapter(code) for chapter in range.start.chapter .. range.finish.chapter: let startVerse = if chapter == range.start.chapter and range.start.verse > 0: range.start.verse else: 1 let endVerse = if chapter == range.finish.chapter and range.finish.verse > 0: range.finish.verse else: index.requireLastVerse(code, chapter) if startVerse > endVerse: raise newException(ValueError, "reference range starts after it ends") for verse in startVerse .. endVerse: lines.add(" [" & $verse & "] " & index.requireVerse(code, chapter, verse)) 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)) else: for range in reference.ranges: lines.addVerseLines(index, reference, range) lines.join("\n") proc fetchPassages*(reference: string): seq[string] = let index = loadBibleIndex() for parsedReference in parseReferences(reference): result.add(fetchReference(index, parsedReference))