https://github.com/akkartik/mu/blob/main/linux/apps/advent2020/4b.mu

      1 # https://adventofcode.com/2020/day/4
      2 #
      3 # To run (on Linux):
      4 #   $ git clone https://github.com/akkartik/mu
      5 #   $ cd mu
      6 #   $ ./translate apps/advent2020/4b.mu
      7 #   $ ./a.elf < input
      8 #
      9 # You'll need to register to download the 'input' file for yourself.
     10
     11 fn main -> _/ebx: int {
     12   var curr-passport-field-count/esi: int <- copy 0
     13   var valid-passport-count/edi: int <- copy 0
     14   var line-storage: (stream byte 0x100)  # 256 bytes
     15   var line/ecx: (addr stream byte) <- address line-storage
     16   var key-slice-storage: slice
     17   var key-slice/edx: (addr slice) <- address key-slice-storage
     18   var val-slice-storage: slice
     19   var val-slice/ebx: (addr slice) <- address val-slice-storage
     20   $main:line-loop: {
     21     # read line from stdin
     22     clear-stream line
     23     read-line-from-real-keyboard line
     24     # if line is empty (not even a newline), quit
     25     var done?/eax: boolean <- stream-empty? line
     26     compare done?, 0/false
     27     break-if-!=
     28     print-stream-to-real-screen line
     29     # if line has just a newline, process passport
     30     skip-chars-matching-whitespace line
     31     var new-passport?/eax: boolean <- stream-empty? line
     32     {
     33       compare new-passport?, 0/false
     34       break-if-=
     35       compare curr-passport-field-count, 7
     36       {
     37         break-if-!=
     38         valid-passport-count <- increment
     39         print-string 0, "=> "
     40         print-int32-decimal 0, valid-passport-count
     41         print-string 0, "\n"
     42       }
     43       curr-passport-field-count <- copy 0
     44       loop $main:line-loop
     45     }
     46     $main:word-loop: {
     47       skip-chars-matching-whitespace line
     48       var done?/eax: boolean <- stream-empty? line
     49       compare done?, 0/false
     50       break-if-!=
     51       next-token line, 0x3a, key-slice  # ':'
     52       var dummy/eax: byte <- read-byte line  # skip ':'
     53       next-raw-word line, val-slice
     54       print-slice-to-real-screen key-slice
     55       print-string 0, " : "
     56       print-slice-to-real-screen val-slice
     57       print-string 0, "\n"
     58       # treat cid as optional
     59       var cid?/eax: boolean <- slice-equal? key-slice, "cid"
     60       compare cid?, 0/false
     61       loop-if-!=
     62       # increment field count
     63       curr-passport-field-count <- increment
     64       # - validate fields one by one, setting curr-passport-field-count to impossibly high value to signal invalid
     65       # byr
     66       {
     67         var byr?/eax: boolean <- slice-equal? key-slice, "byr"
     68         compare byr?, 0/false
     69         break-if-=
     70         # 1920 <= byr <= 2002
     71         var byr/eax: int <- parse-decimal-int-from-slice val-slice
     72         compare byr, 0x780  # 1920
     73         {
     74           break-if->=
     75           print-string 0, "invalid\n"
     76           curr-passport-field-count <- copy 8
     77         }
     78         compare byr, 0x7d2  # 2002
     79         {
     80           break-if-<=
     81           print-string 0, "invalid\n"
     82           curr-passport-field-count <- copy 8
     83         }
     84       }
     85       # iyr
     86       {
     87         var iyr?/eax: boolean <- slice-equal? key-slice, "iyr"
     88         compare iyr?, 0/false
     89         break-if-=
     90         # 2010 <= iyr <= 2020
     91         var iyr/eax: int <- parse-decimal-int-from-slice val-slice
     92         compare iyr, 0x7da  # 2010
     93         {
     94           break-if->=
     95           print-string 0, "invalid\n"
     96           curr-passport-field-count <- copy 8
     97         }
     98         compare iyr, 0x7e4  # 2020
     99         {
    100           break-if-<=
    101           print-string 0, "invalid\n"
    102           curr-passport-field-count <- copy 8
    103         }
    104       }
    105       # eyr
    106       {
    107         var eyr?/eax: boolean <- slice-equal? key-slice, "eyr"
    108         compare eyr?, 0/false
    109         break-if-=
    110         # 2020 <= eyr <= 2030
    111         var eyr/eax: int <- parse-decimal-int-from-slice val-slice
    112         compare eyr, 0x7e4  # 2020
    113         {
    114           break-if->=
    115           print-string 0, "invalid\n"
    116           curr-passport-field-count <- copy 8
    117         }
    118         compare eyr, 0x7ee  # 2030
    119         {
    120           break-if-<=
    121           print-string 0, "invalid\n"
    122           curr-passport-field-count <- copy 8
    123         }
    124       }
    125       # hgt
    126       {
    127         var hgt?/eax: boolean <- slice-equal? key-slice, "hgt"
    128         compare hgt?, 0/false
    129         break-if-=
    130         # convert val
    131         var s: (handle array byte)
    132         var s2/eax: (addr handle array byte) <- address s
    133         _slice-to-string val-slice, s2
    134         var s3/eax: (addr array byte) <- lookup *s2
    135         var s4/ebx: (addr array byte) <- copy s3
    136         # check suffix
    137         var start/edx: int <- length s4
    138         start <- subtract 2  # luckily both 'in' and 'cm' have the same length
    139         {
    140           var suffix-h: (handle array byte)
    141           var suffix-ah/ecx: (addr handle array byte) <- address suffix-h
    142           substring s4, start, 2, suffix-ah
    143           var suffix/eax: (addr array byte) <- lookup *suffix-ah
    144           {
    145             var match?/eax: boolean <- string-equal? suffix, "in"
    146             compare match?, 0/false
    147             break-if-=
    148             # if suffix is "in", 59 <= val <= 96
    149             var num-h: (handle array byte)
    150             var num-ah/ecx: (addr handle array byte) <- address num-h
    151             substring s4, 0, start, num-ah
    152             var num/eax: (addr array byte) <- lookup *num-ah
    153             var val/eax: int <- parse-decimal-int num
    154             compare val, 0x3b  # 59
    155             {
    156               break-if->=
    157           print-string 0, "invalid\n"
    158               curr-passport-field-count <- copy 8
    159             }
    160             compare val, 0x60  # 96
    161             {
    162               break-if-<=
    163           print-string 0, "invalid\n"
    164               curr-passport-field-count <- copy 8
    165             }
    166             loop $main:word-loop
    167           }
    168           {
    169             var match?/eax: boolean <- string-equal? suffix, "cm"
    170             compare match?, 0/false
    171             break-if-=
    172             # if suffix is "cm", 150 <= val <= 193
    173             var num-h: (handle array byte)
    174             var num-ah/ecx: (addr handle array byte) <- address num-h
    175             substring s4, 0, start, num-ah
    176             var num/eax: (addr array byte) <- lookup *num-ah
    177             var val/eax: int <- parse-decimal-int num
    178             compare val, 0x96  # 150
    179             {
    180               break-if->=
    181           print-string 0, "invalid\n"
    182               curr-passport-field-count <- copy 8
    183             }
    184             compare val, 0xc1  # 193
    185             {
    186               break-if-<=
    187           print-string 0, "invalid\n"
    188               curr-passport-field-count <- copy 8
    189             }
    190             loop $main:word-loop
    191           }
    192           print-string 0, "invalid\n"
    193           curr-passport-field-count <- copy 8
    194           loop $main:word-loop
    195         }
    196       }
    197       # hcl
    198       {
    199         var hcl?/eax: boolean <- slice-equal? key-slice, "hcl"
    200         compare hcl?, 0/false
    201         break-if-=
    202         # convert val
    203         var s: (handle array byte)
    204         var s2/eax: (addr handle array byte) <- address s
    205         _slice-to-string val-slice, s2
    206         var s3/eax: (addr array byte) <- lookup *s2
    207         # check length
    208         var len/ebx: int <- length s3
    209         compare len, 7
    210         {
    211           break-if-=
    212           print-string 0, "invalid\n"
    213           curr-passport-field-count <- copy 8
    214           loop $main:word-loop
    215         }
    216         # check first byte
    217         {
    218           var c/eax: (addr byte) <- index s3, 0
    219           var c2/eax: byte <- copy-byte *c
    220           compare c2, 0x23/hash
    221           break-if-=
    222           print-string 0, "invalid2\n"
    223           curr-passport-field-count <- copy 8
    224           loop $main:word-loop
    225         }
    226         # check remaining bytes
    227         var i/ebx: int <- copy 1  # skip 0
    228         {
    229           compare i, 7
    230           break-if->=
    231           var c/eax: (addr byte) <- index s3, i
    232           {
    233             var c2/eax: byte <- copy-byte *c
    234             var valid?/eax: boolean <- hex-digit? c2
    235             compare valid?, 0
    236             loop-if-= $main:word-loop
    237           }
    238           i <- increment
    239           loop
    240         }
    241       }
    242       # ecl
    243       {
    244         var ecl?/eax: boolean <- slice-equal? key-slice, "ecl"
    245         compare ecl?, 0/false
    246         break-if-=
    247         var amb?/eax: boolean <- slice-equal? val-slice, "amb"
    248         compare amb?, 0/false
    249         loop-if-!= $main:word-loop
    250         var blu?/eax: boolean <- slice-equal? val-slice, "blu"
    251         compare blu?, 0/false
    252         loop-if-!= $main:word-loop
    253         var brn?/eax: boolean <- slice-equal? val-slice, "brn"
    254         compare brn?, 0/false
    255         loop-if-!= $main:word-loop
    256         var gry?/eax: boolean <- slice-equal? val-slice, "gry"
    257         compare gry?, 0/false
    258         loop-if-!= $main:word-loop
    259         var grn?/eax: boolean <- slice-equal? val-slice, "grn"
    260         compare grn?, 0/false
    261         loop-if-!= $main:word-loop
    262         var hzl?/eax: boolean <- slice-equal? val-slice, "hzl"
    263         compare hzl?, 0/false
    264         loop-if-!= $main:word-loop
    265         var oth?/eax: boolean <- slice-equal? val-slice, "oth"
    266         compare oth?, 0/false
    267         loop-if-!= $main:word-loop
    268         print-string 0, "invalid\n"
    269         curr-passport-field-count <- copy 8
    270       }
    271       # pid
    272       {
    273         var pid?/eax: boolean <- slice-equal? key-slice, "pid"
    274         compare pid?, 0/false
    275         break-if-=
    276         # convert val
    277         var s: (handle array byte)
    278         var s2/eax: (addr handle array byte) <- address s
    279         _slice-to-string val-slice, s2
    280         var s3/eax: (addr array byte) <- lookup *s2
    281         # check length
    282         var len/eax: int <- length s3
    283         compare len, 9
    284         {
    285           break-if-=
    286           print-string 0, "invalid\n"
    287           curr-passport-field-count <- copy 8
    288           loop $main:word-loop
    289         }
    290         # check valid decimal int
    291         # parse-decimal-int-from-slice currently returns 0 on invalid parse,
    292         # which isn't ideal but suffices for our purposes
    293         var val/eax: int <- parse-decimal-int-from-slice val-slice
    294         compare val, 0
    295         {
    296           break-if->
    297           print-string 0, "invalid\n"
    298           curr-passport-field-count <- copy 8
    299         }
    300       }
    301       loop
    302     }
    303     loop
    304   }
    305   # process final passport
    306   compare curr-passport-field-count, 7
    307   {
    308     break-if-!=
    309     valid-passport-count <- increment
    310   }
    311   print-int32-decimal 0, valid-passport-count
    312   print-string 0, "\n"
    313   return 0
    314 }