97 lines
2.2 KiB
Gleam
97 lines
2.2 KiB
Gleam
|
import gleam/int
|
||
|
import gleam/io
|
||
|
import gleam/iterator
|
||
|
import gleam/list.{reverse, take_while}
|
||
|
import gleam/option.{type Option, None, Some}
|
||
|
import gleam/string.{join}
|
||
|
import stdin.{stdin}
|
||
|
|
||
|
pub fn main() {
|
||
|
stdin()
|
||
|
|> read_input
|
||
|
|> find_and_calculate(0, can_mul: True)
|
||
|
|> io.debug
|
||
|
}
|
||
|
|
||
|
type FindResult {
|
||
|
Expression(Int, Int, String)
|
||
|
Nothing
|
||
|
}
|
||
|
|
||
|
fn find_and_calculate(s: String, acc: Int, can_mul mul_enabled: Bool) -> Int {
|
||
|
case s {
|
||
|
"" -> acc
|
||
|
"do()" <> rest -> find_and_calculate(rest, acc, can_mul: True)
|
||
|
"don't()" <> rest -> find_and_calculate(rest, acc, can_mul: False)
|
||
|
"mul(" <> rest if mul_enabled -> {
|
||
|
case find_expression(rest) {
|
||
|
Expression(a, b, rest) ->
|
||
|
find_and_calculate(rest, acc + a * b, can_mul: mul_enabled)
|
||
|
Nothing -> find_and_calculate(rest, acc, can_mul: mul_enabled)
|
||
|
}
|
||
|
}
|
||
|
_ -> find_and_calculate(string.drop_start(s, 1), acc, can_mul: mul_enabled)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn find_expression(str: String) -> FindResult {
|
||
|
case
|
||
|
str
|
||
|
|> read_num
|
||
|
|> then(read_char(","))
|
||
|
|> then(read_num)
|
||
|
|> then(read_char(")"))
|
||
|
{
|
||
|
Some(#(#(#(#(a, _), b), _), rest)) -> Expression(a, b, rest)
|
||
|
None -> Nothing
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn read_char(char: String) {
|
||
|
fn(str: String) -> Option(#(String, String)) {
|
||
|
case string.pop_grapheme(str) {
|
||
|
Ok(#(ch, rest)) if ch == char -> Some(#(char, rest))
|
||
|
_ -> None
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn then(
|
||
|
current: Option(#(a, String)),
|
||
|
next: fn(String) -> Option(#(b, String)),
|
||
|
) -> Option(#(#(a, b), String)) {
|
||
|
case current {
|
||
|
None -> None
|
||
|
Some(#(a, s)) ->
|
||
|
next(s)
|
||
|
|> option.map(fn(result) {
|
||
|
let #(b, rest) = result
|
||
|
#(#(a, b), rest)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const numbers = "0123456789"
|
||
|
|
||
|
fn read_num(str: String) -> Option(#(Int, String)) {
|
||
|
let to_parse =
|
||
|
str
|
||
|
|> string.to_graphemes
|
||
|
|> take_while(fn(c) { string.contains(does: numbers, contain: c) })
|
||
|
|> join(with: "")
|
||
|
|
||
|
to_parse
|
||
|
|> int.parse
|
||
|
|> option.from_result
|
||
|
|> option.map(fn(n) {
|
||
|
#(n, string.drop_start(str, up_to: string.length(to_parse)))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
fn read_input(it: iterator.Iterator(String)) -> String {
|
||
|
it
|
||
|
|> iterator.fold([], fn(acc, line) { [line, ..acc] })
|
||
|
|> reverse
|
||
|
|> join(with: "")
|
||
|
}
|