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: "") }