a gleam implementation of a CS assignment originally written in cpp

feat: add state machine parser and 2-word book support

Changed files
+99 -8
src
+99 -8
src/bible_search.gleam
···
import gleam/int
import gleam/io
import gleam/list
+
import gleam/result
import gleam/string
import input.{input}
import simplifile
···
Reference(book: String, chapter: Int, verse: Int)
}
+
pub type Phase {
+
Book
+
Chapter
+
Verse
+
Done
+
}
+
+
pub type ScanState {
+
ScanState(
+
phase: Phase,
+
ref: Reference,
+
found_book: Bool,
+
found_chapter: Bool,
+
verse_text: Result(String, String),
+
)
+
}
+
pub fn parse_reference(input: String) -> Result(Reference, String) {
let parts = string.split(input, on: " ")
···
case int.parse(verse_s) {
Ok(verse) ->
Ok(Reference(book: string.uppercase(book), chapter:, verse:))
-
Error(_) -> Error("Invalid verse" <> verse_s)
+
Error(_) -> Error("Invalid verse: " <> verse_s)
}
-
Error(_) -> Error("Invalid chapter" <> chapter_s)
+
Error(_) -> Error("Invalid chapter: " <> chapter_s)
}
+
[book_p1, book_p2, chapter_s, verse_s] ->
+
case int.parse(chapter_s) {
+
Ok(chapter) ->
+
case int.parse(verse_s) {
+
Ok(verse) ->
+
Ok(Reference(
+
book: string.uppercase(book_p1 <> " " <> book_p2),
+
chapter:,
+
verse:,
+
))
+
Error(_) -> Error("Invalid verse: " <> verse_s)
+
}
+
Error(_) -> Error("Invalid chapter: " <> chapter_s)
+
}
+
_ -> Error("invalid parse")
}
}
···
let assert Ok(reference) = parse_reference(search)
-
list.each(biblelines, fn(line) {
-
case line {
-
"THE BOOK OF " <> book if book == reference.book -> io.println(book)
-
_ -> io.print("")
-
}
-
})
+
let inital =
+
ScanState(
+
phase: Book,
+
ref: reference,
+
found_book: False,
+
found_chapter: False,
+
verse_text: Error("Verse not found"),
+
)
+
+
let result =
+
list.fold(biblelines, inital, fn(state, line) -> ScanState {
+
case state.phase {
+
Done -> state
+
Book ->
+
case line {
+
"THE BOOK OF " <> book ->
+
case book == state.ref.book {
+
True -> ScanState(..state, phase: Chapter, found_book: True)
+
False -> state
+
}
+
_ -> state
+
}
+
Chapter ->
+
case line {
+
"CHAPTER " <> chapter ->
+
case result.unwrap(int.parse(chapter), 0) == state.ref.chapter {
+
True -> ScanState(..state, phase: Verse, found_chapter: True)
+
False -> state
+
}
+
_ -> state
+
}
+
Verse ->
+
fn() {
+
let parts = string.split(line, " ")
+
case parts {
+
[first, ..rest] ->
+
case result.unwrap(int.parse(first), 0) == state.ref.verse {
+
True ->
+
ScanState(
+
..state,
+
phase: Done,
+
verse_text: Ok(string.join(rest, " ")),
+
)
+
False -> state
+
}
+
_ -> state
+
}
+
}()
+
}
+
})
+
+
case result.verse_text {
+
Ok(text) ->
+
io.println(
+
result.ref.book
+
<> " "
+
<> int.to_string(result.ref.chapter)
+
<> ":"
+
<> int.to_string(result.ref.chapter)
+
<> " "
+
<> text,
+
)
+
Error(e) -> io.println_error(e)
+
}
}