a gleam implementation of a CS assignment originally written in cpp
1import gleam/int
2import gleam/io
3import gleam/list
4import gleam/result
5import gleam/string
6import input.{input}
7import simplifile
8
9const filepath = "./OT.txt"
10
11pub type Reference {
12 Reference(book: String, chapter: Int, verse: Int)
13}
14
15pub type Phase {
16 Book
17 Chapter
18 Verse
19 Done
20}
21
22pub type ScanState {
23 ScanState(
24 phase: Phase,
25 ref: Reference,
26 found_book: Bool,
27 found_chapter: Bool,
28 verse_text: Result(String, String),
29 )
30}
31
32pub type ScanResult {
33 ScanResult(ref: Reference, verse: Result(String, String))
34}
35
36pub fn parse_reference(input: String) -> Result(Reference, String) {
37 let parts = string.split(input, on: " ")
38
39 case parts {
40 [book, chapter_s, verse_s] ->
41 case int.parse(chapter_s) {
42 Ok(chapter) ->
43 case int.parse(verse_s) {
44 Ok(verse) ->
45 Ok(Reference(book: string.uppercase(book), chapter:, verse:))
46 Error(_) -> Error("Invalid verse: " <> verse_s)
47 }
48 Error(_) -> Error("Invalid chapter: " <> chapter_s)
49 }
50 [book_p1, book_p2, chapter_s, verse_s] ->
51 case int.parse(chapter_s) {
52 Ok(chapter) ->
53 case int.parse(verse_s) {
54 Ok(verse) ->
55 Ok(Reference(
56 book: string.uppercase(book_p1 <> " " <> book_p2),
57 chapter:,
58 verse:,
59 ))
60 Error(_) -> Error("Invalid verse: " <> verse_s)
61 }
62 Error(_) -> Error("Invalid chapter: " <> chapter_s)
63 }
64
65 _ -> Error("invalid parse")
66 }
67}
68
69pub fn bible_scan(
70 reference reference: Reference,
71 bible bible: String,
72) -> ScanResult {
73 let inital_state =
74 ScanState(
75 phase: Book,
76 ref: reference,
77 found_book: False,
78 found_chapter: False,
79 verse_text: Error("Verse not found"),
80 )
81
82 let bible_lines = string.split(bible, "\n")
83
84 let result =
85 list.fold(bible_lines, inital_state, fn(state, line) -> ScanState {
86 case state.phase {
87 Done -> state
88 Book ->
89 case line {
90 "THE BOOK OF " <> book ->
91 case book == state.ref.book {
92 True -> ScanState(..state, phase: Chapter, found_book: True)
93 False -> state
94 }
95 _ -> state
96 }
97 Chapter ->
98 case line {
99 "CHAPTER " <> chapter ->
100 case result.unwrap(int.parse(chapter), 0) == state.ref.chapter {
101 True -> ScanState(..state, phase: Verse, found_chapter: True)
102 False -> state
103 }
104 _ -> state
105 }
106 Verse ->
107 fn() {
108 let parts = string.split(line, " ")
109 case parts {
110 [first, ..rest] ->
111 case result.unwrap(int.parse(first), 0) == state.ref.verse {
112 True ->
113 ScanState(
114 ..state,
115 phase: Done,
116 verse_text: Ok(string.join(rest, " ")),
117 )
118 False -> state
119 }
120 _ -> state
121 }
122 }()
123 }
124 })
125
126 ScanResult(ref: reference, verse: result.verse_text)
127}
128
129pub fn main() -> Nil {
130 let assert Ok(bible) = simplifile.read(from: filepath)
131
132 let assert Ok(search) = input(prompt: "> ")
133
134 let assert Ok(reference) = parse_reference(search)
135
136 let scan = bible_scan(reference, bible)
137
138 case scan.verse {
139 Ok(text) ->
140 io.println(
141 scan.ref.book
142 <> " "
143 <> int.to_string(scan.ref.chapter)
144 <> ":"
145 <> int.to_string(scan.ref.verse)
146 <> " "
147 <> text,
148 )
149 Error(e) -> io.println_error(e)
150 }
151}