package main import ( "errors" "fmt" "regexp" "strconv" ) var BibleRegex = regexp.MustCompile(`(?P\w*?)? *(?P[a-zA-Z]+?)\s?(?P\d{1,3})(?::(?P\d{1,3})(?:-(?P\d{1,3})?)?)`) var MatchError = errors.New("Error matching verse") var CanonicalName = map[int]string{ 1: "Genesis", 2: "Exodus", 3: "Leviticus", 4: "Numbers", 5: "Deuteronomy", 6: "Joshua", 7: "Judges", 8: "Ruth", 9: "1 Samuel", 10: "2 Samuel", 11: "1 Kings", 12: "2 Kings", 13: "1 Chronicles", 14: "2 Chronicles", 15: "Ezra", 16: "Nehemiah", 17: "Esther", 18: "Job", 19: "Psalms", 20: "Proverbs", 21: "Ecclesiastes", 22: "Song of Solomon", 23: "Isaiah", 24: "Jeremiah", 25: "Lamentations", 26: "Ezekiel", 27: "Daniel", 28: "Hosea", 29: "Joel", 30: "Amos", 31: "Obadiah", 32: "Jonah", 33: "Micah", 34: "Nahum", 35: "Habakkuk", 36: "Zephaniah", 37: "Haggai", 38: "Zechariah", 39: "Malachi", 40: "Matthew", 41: "Mark", 42: "Luke", 43: "John", 44: "Acts", 45: "Romans", 46: "1 Corinthians", 47: "2 Corinthians", 48: "Galatians", 49: "Ephesians", 50: "Philippians", 51: "Colossians", 52: "1 Thessalonians", 53: "2 Thessalonians", 54: "1 Timothy", 55: "2 Timothy", 56: "Titus", 57: "Philemon", 58: "Hebrews", 59: "James", 60: "1 Peter", 61: "2 Peter", 62: "1 John", 63: "2 John", 64: "3 John", 65: "Jude", 66: "Revelation", } var Books = map[string]int{ "GEN": 1, "EXO": 2, "EX": 2, "LEV": 3, "LV": 3, "NUM": 4, "DEU": 5, "DT": 5, "JOS": 6, "JUDGES": 7, "JDG": 7, "JDGS": 7, "JG": 7, "RUT": 8, "1 SAM": 9, "2 SAM": 10, "1 KIN": 11, "2 KIN": 12, "1 CHR": 13, "2 CHR": 14, "EZR": 15, "NEH": 16, "EST": 17, "JOB": 18, "PSA": 19, "PS": 19, "PRO": 20, "PRV": 20, "ECC": 21, "SOL": 22, "SON": 22, "ISA": 23, "JER": 24, "LAM": 25, "EZE": 26, "DAN": 27, "HOS": 28, "JOE": 29, "AMO": 30, "OBA": 31, "JON": 32, "MIC": 33, "NAH": 34, "HAB": 35, "ZEP": 36, "HAG": 37, "ZEC": 38, "MAL": 39, "MAT": 40, "MAR": 41, "LUK": 42, "JOH": 43, "ACT": 44, "ROM": 45, "1 COR": 46, "2 COR": 47, "GAL": 48, "EPH": 49, "PHILIPPIANS": 50, "COL": 51, "1 THE": 52, "2 THE": 53, "1 TIM": 54, "2 TIM": 55, "TIT": 56, "PHILEMON": 57, "HEB": 58, "JAM": 59, "1 PET": 60, "2 PET": 61, "1 JOH": 62, "2 JOH": 63, "3 JOH": 64, "JUDE": 65, "REV": 66, } var FirstPrefixes = []string{ "FIRST", "1ST", "1", "I", } var SecondPrefixes = []string{ "SECOND", "2nd", "2", "II", } type BibleReference struct { RegexMatch string Prefix string Book string BookNum int Chapter string StartVerse string EndVerse string StartKey string // key format is BBCCCVVV EndKey string } func GetReferences(text string) []*BibleReference { matches := BibleRegex.FindAllStringSubmatch(text, -1) refs := make([]*BibleReference, 0, len(matches)) for _, match := range matches { ref, err := NewBibleReference(match) if err != nil { fmt.Printf(`Couldn't parse "%s" `, match[0]) continue } fmt.Printf(`parsed "%s": %s - %s `, ref.RegexMatch, ref.StartKey, ref.EndKey) refs = append(refs, ref) } return refs } func NewBibleReference(match []string) (*BibleReference, error) { if len(match) < 6 { return nil, MatchError } ref := &BibleReference{ RegexMatch: match[0], Prefix: match[1], Book: match[2], Chapter: match[3], StartVerse: match[4], EndVerse: match[5], } err := ref.Validate() return ref, err } func contains(s []string, str string) bool { for _, v := range s { if v == str { return true } } return false } func (b *BibleReference) CanonicalName() (string, error) { book, ok := CanonicalName[b.BookNum] if !ok { return "", errors.New("Can't find book, maybe you're Catholic?") } if b.StartKey == b.EndKey { return fmt.Sprintf("%s %s:%s", book, b.Chapter, b.StartVerse), nil } return fmt.Sprintf("%s %s:%s-%s", book, b.Chapter, b.StartVerse, b.EndVerse), nil } func (b *BibleReference) Validate() error { if contains(FirstPrefixes, b.Prefix) { b.Prefix = "1" } else if contains(SecondPrefixes, b.Prefix) { b.Prefix = "2" } else { b.Prefix = "" } if b.Prefix != "" { b.Book = fmt.Sprintf("%s %s", b.Prefix, b.Book) } num, ok := Books[b.Book] if !ok { num, ok = Books[fmt.Sprintf("%.5s", b.Book)] } if !ok { num, ok = Books[fmt.Sprintf("%.3s", b.Book)] } if !ok { return MatchError } b.BookNum = num chapter, err := strconv.Atoi(b.Chapter) if err != nil { return err } startverse, err := strconv.Atoi(b.StartVerse) if err != nil { return err } endverse, err := strconv.Atoi(b.EndVerse) if err != nil { endverse = startverse } b.StartKey = fmt.Sprintf("%02d%03d%03d", num, chapter, startverse) b.EndKey = fmt.Sprintf("%02d%03d%03d", num, chapter, endverse) return nil }