package main import ( _ "embed" "fmt" "io" "iter" "strings" aoc "go.sour.is/advent-of-code" ) // var log = aoc.Log func main() { aoc.MustResult(aoc.RunnerReader(run)) } type result struct { valuePT1 int valuePT2 int } func (r result) String() string { return fmt.Sprintf("%#v", r) } func run(read io.Reader) (*result, error) { sum := 0 sum2 := 0 lexer, stop := Lexer(read) defer stop() active := true for lexer.NextTok() { switch lexer.token { case "TokMUL": if m := readMul(lexer); m != 0 { sum += m if active { sum2 += m } } case "TokDONT": if readDont(lexer) { active = false } case "TokDO": if readDo(lexer) { active = true } } } return &result{sum, sum2}, nil } func Lexer(in io.Reader) (*lexer, func()) { seq := func(yield func(rune) bool) { buf := make([]byte, 256) s, _ := in.Read(buf) buf = buf[:s] for len(buf) > 0 { for _, r := range string(buf) { if !yield(r) { return } } s, _ := in.Read(buf) buf = buf[:s] } for !yield(-1) { } } next, stop := iter.Pull(seq) lex := lexer{iter: next} lex.readRune() lex.readRune() return &lex, stop } type lexer struct { rune rune next rune iter func() (rune, bool) buf []rune token string literal []rune } func (l *lexer) readRune() { if l.rune == -1 { return } if r, ok := l.iter(); ok { l.rune, l.next = l.next, r } else { l.rune, l.next = l.next, -1 } } func (l *lexer) loadRune(tok string) { l.token = tok l.literal = append(l.literal, l.rune) l.readRune() } func (l *lexer) loadNumber() { l.token = "TokNUMBER" for strings.ContainsRune("0123456789", l.rune) { l.literal = append(l.literal, l.rune) l.readRune() } } func (l *lexer) loadString(accept string) { l.token = "TokSTRING" for !(!strings.ContainsRune(accept, l.rune) || l.rune == 0 || l.rune == -1) { l.literal = append(l.literal, l.rune) l.readRune() } } func (l *lexer) NextTok() bool { l.literal = l.literal[:0] switch l.rune { case 'm': l.loadString("mul") l.token = "TokMUL" if string(l.literal) != "mul" { l.token = "TokILLEGAL" } return true case 'd': l.loadString("don't") switch string(l.literal) { case "don't": l.token = "TokDONT" case "do": l.token = "TokDO" default: l.token = "TokILLEGAL" } return true case '(': l.loadRune("TokLPAREN") return true case ')': l.loadRune("TokRPAREN") return true case ',': l.loadRune("TokCOMMA") return true case -1: l.loadRune("TokEOF") return false default: if '0' <= l.rune && l.rune <= '9' { l.loadNumber() return true } } l.loadRune("TokILLEGAL") return true } func readMul(lex *lexer) int { if lex.token != "TokMUL" || string(lex.literal) != "mul" { return 0 } var a, b = -1, -1 if !lex.NextTok() || lex.token != "TokLPAREN" { return 0 } if !lex.NextTok() || lex.token != "TokNUMBER" { return 0 } a = aoc.Atoi(string(lex.literal)) if !lex.NextTok() || lex.token != "TokCOMMA" { return 0 } if !lex.NextTok() || lex.token != "TokNUMBER" { return 0 } b = aoc.Atoi(string(lex.literal)) if !lex.NextTok() || lex.token != "TokRPAREN" { return 0 } return a * b } func readDont(lex *lexer) bool { if lex.token != "TokDONT" || string(lex.literal) != "don't" { return false } if !lex.NextTok() || lex.token != "TokLPAREN" { return false } if !lex.NextTok() || lex.token != "TokRPAREN" { return false } return true } func readDo(lex *lexer) bool { if lex.token != "TokDO" || string(lex.literal) != "do" { return false } if !lex.NextTok() || lex.token != "TokLPAREN" { return false } if !lex.NextTok() || lex.token != "TokRPAREN" { return false } return true }