USING: continuations io.encodings.ascii io.files kernel literals math.order math.parser multiline peg.ebnf prettyprint qw regexp sequences sets splitting ; IN: aoc.2020.04 CONSTANT: input $[ "resource:work/aoc/2020/04/input.txt" ascii file-contents ] CONSTANT: required qw{ byr iyr eyr hgt hcl ecl pid } ! Change that monstrosity of an input file to something like ! { { "byr:2014" "pid:120312" ... } { "cid:79" ... } }, ! with a sequence of fields for each passport. : normalize ( seq -- newseq ) R/ \n\n/ re-split [ "\n" split " " join " " split harvest ] map ; ! Filter for passports with all required fields. : passports ( seq -- newseq ) [ [ 3 head ] map required swap subset? ] filter ; : part1 ( -- ) input normalize passports length . ; ! ======= PART 2, aka parsing is fun ======= EBNF: field [=[ num = [0-9]+ => [[ string>number ]] cm = num ?[ 150 193 between? ]? "cm" in = num ?[ 59 76 between? ]? "in" byr = "byr" ":"~ num ?[ 1920 2002 between? ]? iyr = "iyr" ":"~ num ?[ 2010 2020 between? ]? eyr = "eyr" ":"~ num ?[ 2020 2030 between? ]? hgt = "hgt" ":"~ (cm|in) hcl = "hcl" ":"~ "#"~ [0-9a-f]+ ?[ length 6 = ]? ecl = "ecl" ":"~ ("amb"|"blu"|"brn"|"gry"|"grn"|"hzl"|"oth") pid = "pid" ":"~ [0-9]+ ?[ length 9 = ]? cid = "cid" ":"~ num field = (byr|iyr|eyr|hgt|hcl|ecl|pid|cid) ]=] : part2 ( -- ) input normalize passports [ [ [ field ] map drop t ] [ 2drop f ] recover ] count . ;