about summary refs log tree commit diff stats
path: root/linux/advent2020/4b.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-03-03 22:09:50 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-03-03 22:21:03 -0800
commit71e4f3812982dba2efb471283d310224e8db363e (patch)
treeea111a1acb8b8845dbda39c0e1b4bac1d198143b /linux/advent2020/4b.mu
parentc6b928be29ac8cdb4e4d6e1eaa20420ff03e5a4c (diff)
downloadmu-71e4f3812982dba2efb471283d310224e8db363e.tar.gz
7842 - new directory organization
Baremetal is now the default build target and therefore has its sources
at the top-level. Baremetal programs build using the phase-2 Mu toolchain
that requires a Linux kernel. This phase-2 codebase which used to be at
the top-level is now under the linux/ directory. Finally, the phase-2 toolchain,
while self-hosting, has a way to bootstrap from a C implementation, which
is now stored in linux/bootstrap. The bootstrap C implementation uses some
literate programming tools that are now in linux/bootstrap/tools.

So the whole thing has gotten inverted. Each directory should build one
artifact and include the main sources (along with standard library). Tools
used for building it are relegated to sub-directories, even though those
tools are often useful in their own right, and have had lots of interesting
programs written using them.

A couple of things have gotten dropped in this process:
  - I had old ways to run on just a Linux kernel, or with a Soso kernel.
    No more.
  - I had some old tooling for running a single test at the cursor. I haven't
    used that lately. Maybe I'll bring it back one day.

The reorg isn't done yet. Still to do:
  - redo documentation everywhere. All the README files, all other markdown,
    particularly vocabulary.md.
  - clean up how-to-run comments at the start of programs everywhere
  - rethink what to do with the html/ directory. Do we even want to keep
    supporting it?

In spite of these shortcomings, all the scripts at the top-level, linux/
and linux/bootstrap are working. The names of the scripts also feel reasonable.
This is a good milestone to take stock at.
Diffstat (limited to 'linux/advent2020/4b.mu')
-rw-r--r--linux/advent2020/4b.mu314
1 files changed, 314 insertions, 0 deletions
diff --git a/linux/advent2020/4b.mu b/linux/advent2020/4b.mu
new file mode 100644
index 00000000..1ab29456
--- /dev/null
+++ b/linux/advent2020/4b.mu
@@ -0,0 +1,314 @@
+# https://adventofcode.com/2020/day/4
+#
+# To run (on Linux):
+#   $ git clone https://github.com/akkartik/mu
+#   $ cd mu
+#   $ ./translate_mu apps/advent2020/4b.mu
+#   $ ./a.elf < input
+#
+# You'll need to register to download the 'input' file for yourself.
+
+fn main -> _/ebx: int {
+  var curr-passport-field-count/esi: int <- copy 0
+  var valid-passport-count/edi: int <- copy 0
+  var line-storage: (stream byte 0x100)  # 256 bytes
+  var line/ecx: (addr stream byte) <- address line-storage
+  var key-slice-storage: slice
+  var key-slice/edx: (addr slice) <- address key-slice-storage
+  var val-slice-storage: slice
+  var val-slice/ebx: (addr slice) <- address val-slice-storage
+  $main:line-loop: {
+    # read line from stdin
+    clear-stream line
+    read-line-from-real-keyboard line
+    # if line is empty (not even a newline), quit
+    var done?/eax: boolean <- stream-empty? line
+    compare done?, 0/false
+    break-if-!=
+    print-stream-to-real-screen line
+    # if line has just a newline, process passport
+    skip-chars-matching-whitespace line
+    var new-passport?/eax: boolean <- stream-empty? line
+    {
+      compare new-passport?, 0/false
+      break-if-=
+      compare curr-passport-field-count, 7
+      {
+        break-if-!=
+        valid-passport-count <- increment
+        print-string 0, "=> "
+        print-int32-decimal 0, valid-passport-count
+        print-string 0, "\n"
+      }
+      curr-passport-field-count <- copy 0
+      loop $main:line-loop
+    }
+    $main:word-loop: {
+      skip-chars-matching-whitespace line
+      var done?/eax: boolean <- stream-empty? line
+      compare done?, 0/false
+      break-if-!=
+      next-token line, 0x3a, key-slice  # ':'
+      var dummy/eax: byte <- read-byte line  # skip ':'
+      next-raw-word line, val-slice
+      print-slice-to-real-screen key-slice
+      print-string 0, " : "
+      print-slice-to-real-screen val-slice
+      print-string 0, "\n"
+      # treat cid as optional
+      var cid?/eax: boolean <- slice-equal? key-slice, "cid"
+      compare cid?, 0/false
+      loop-if-!=
+      # increment field count
+      curr-passport-field-count <- increment
+      # - validate fields one by one, setting curr-passport-field-count to impossibly high value to signal invalid
+      # byr
+      {
+        var byr?/eax: boolean <- slice-equal? key-slice, "byr"
+        compare byr?, 0/false
+        break-if-=
+        # 1920 <= byr <= 2002
+        var byr/eax: int <- parse-decimal-int-from-slice val-slice
+        compare byr, 0x780  # 1920
+        {
+          break-if->=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+        compare byr, 0x7d2  # 2002
+        {
+          break-if-<=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+      }
+      # iyr
+      {
+        var iyr?/eax: boolean <- slice-equal? key-slice, "iyr"
+        compare iyr?, 0/false
+        break-if-=
+        # 2010 <= iyr <= 2020
+        var iyr/eax: int <- parse-decimal-int-from-slice val-slice
+        compare iyr, 0x7da  # 2010
+        {
+          break-if->=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+        compare iyr, 0x7e4  # 2020
+        {
+          break-if-<=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+      }
+      # eyr
+      {
+        var eyr?/eax: boolean <- slice-equal? key-slice, "eyr"
+        compare eyr?, 0/false
+        break-if-=
+        # 2020 <= eyr <= 2030
+        var eyr/eax: int <- parse-decimal-int-from-slice val-slice
+        compare eyr, 0x7e4  # 2020
+        {
+          break-if->=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+        compare eyr, 0x7ee  # 2030
+        {
+          break-if-<=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+      }
+      # hgt
+      {
+        var hgt?/eax: boolean <- slice-equal? key-slice, "hgt"
+        compare hgt?, 0/false
+        break-if-=
+        # convert val
+        var s: (handle array byte)
+        var s2/eax: (addr handle array byte) <- address s
+        _slice-to-string val-slice, s2
+        var s3/eax: (addr array byte) <- lookup *s2
+        var s4/ebx: (addr array byte) <- copy s3
+        # check suffix
+        var start/edx: int <- length s4
+        start <- subtract 2  # luckily both 'in' and 'cm' have the same length
+        {
+          var suffix-h: (handle array byte)
+          var suffix-ah/ecx: (addr handle array byte) <- address suffix-h
+          substring s4, start, 2, suffix-ah
+          var suffix/eax: (addr array byte) <- lookup *suffix-ah
+          {
+            var match?/eax: boolean <- string-equal? suffix, "in"
+            compare match?, 0/false
+            break-if-=
+            # if suffix is "in", 59 <= val <= 96
+            var num-h: (handle array byte)
+            var num-ah/ecx: (addr handle array byte) <- address num-h
+            substring s4, 0, start, num-ah
+            var num/eax: (addr array byte) <- lookup *num-ah
+            var val/eax: int <- parse-decimal-int num
+            compare val, 0x3b  # 59
+            {
+              break-if->=
+          print-string 0, "invalid\n"
+              curr-passport-field-count <- copy 8
+            }
+            compare val, 0x60  # 96
+            {
+              break-if-<=
+          print-string 0, "invalid\n"
+              curr-passport-field-count <- copy 8
+            }
+            loop $main:word-loop
+          }
+          {
+            var match?/eax: boolean <- string-equal? suffix, "cm"
+            compare match?, 0/false
+            break-if-=
+            # if suffix is "cm", 150 <= val <= 193
+            var num-h: (handle array byte)
+            var num-ah/ecx: (addr handle array byte) <- address num-h
+            substring s4, 0, start, num-ah
+            var num/eax: (addr array byte) <- lookup *num-ah
+            var val/eax: int <- parse-decimal-int num
+            compare val, 0x96  # 150
+            {
+              break-if->=
+          print-string 0, "invalid\n"
+              curr-passport-field-count <- copy 8
+            }
+            compare val, 0xc1  # 193
+            {
+              break-if-<=
+          print-string 0, "invalid\n"
+              curr-passport-field-count <- copy 8
+            }
+            loop $main:word-loop
+          }
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+          loop $main:word-loop
+        }
+      }
+      # hcl
+      {
+        var hcl?/eax: boolean <- slice-equal? key-slice, "hcl"
+        compare hcl?, 0/false
+        break-if-=
+        # convert val
+        var s: (handle array byte)
+        var s2/eax: (addr handle array byte) <- address s
+        _slice-to-string val-slice, s2
+        var s3/eax: (addr array byte) <- lookup *s2
+        # check length
+        var len/ebx: int <- length s3
+        compare len, 7
+        {
+          break-if-=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+          loop $main:word-loop
+        }
+        # check first byte
+        {
+          var c/eax: (addr byte) <- index s3, 0
+          var c2/eax: byte <- copy-byte *c
+          compare c2, 0x23/hash
+          break-if-=
+          print-string 0, "invalid2\n"
+          curr-passport-field-count <- copy 8
+          loop $main:word-loop
+        }
+        # check remaining bytes
+        var i/ebx: int <- copy 1  # skip 0
+        {
+          compare i, 7
+          break-if->=
+          var c/eax: (addr byte) <- index s3, i
+          {
+            var c2/eax: byte <- copy-byte *c
+            var valid?/eax: boolean <- is-hex-digit? c2
+            compare valid?, 0
+            loop-if-= $main:word-loop
+          }
+          i <- increment
+          loop
+        }
+      }
+      # ecl
+      {
+        var ecl?/eax: boolean <- slice-equal? key-slice, "ecl"
+        compare ecl?, 0/false
+        break-if-=
+        var amb?/eax: boolean <- slice-equal? val-slice, "amb"
+        compare amb?, 0/false
+        loop-if-!= $main:word-loop
+        var blu?/eax: boolean <- slice-equal? val-slice, "blu"
+        compare blu?, 0/false
+        loop-if-!= $main:word-loop
+        var brn?/eax: boolean <- slice-equal? val-slice, "brn"
+        compare brn?, 0/false
+        loop-if-!= $main:word-loop
+        var gry?/eax: boolean <- slice-equal? val-slice, "gry"
+        compare gry?, 0/false
+        loop-if-!= $main:word-loop
+        var grn?/eax: boolean <- slice-equal? val-slice, "grn"
+        compare grn?, 0/false
+        loop-if-!= $main:word-loop
+        var hzl?/eax: boolean <- slice-equal? val-slice, "hzl"
+        compare hzl?, 0/false
+        loop-if-!= $main:word-loop
+        var oth?/eax: boolean <- slice-equal? val-slice, "oth"
+        compare oth?, 0/false
+        loop-if-!= $main:word-loop
+        print-string 0, "invalid\n"
+        curr-passport-field-count <- copy 8
+      }
+      # pid
+      {
+        var pid?/eax: boolean <- slice-equal? key-slice, "pid"
+        compare pid?, 0/false
+        break-if-=
+        # convert val
+        var s: (handle array byte)
+        var s2/eax: (addr handle array byte) <- address s
+        _slice-to-string val-slice, s2
+        var s3/eax: (addr array byte) <- lookup *s2
+        # check length
+        var len/eax: int <- length s3
+        compare len, 9
+        {
+          break-if-=
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+          loop $main:word-loop
+        }
+        # check valid decimal int
+        # parse-decimal-int-from-slice currently returns 0 on invalid parse,
+        # which isn't ideal but suffices for our purposes
+        var val/eax: int <- parse-decimal-int-from-slice val-slice
+        compare val, 0
+        {
+          break-if->
+          print-string 0, "invalid\n"
+          curr-passport-field-count <- copy 8
+        }
+      }
+      loop
+    }
+    loop
+  }
+  # process final passport
+  compare curr-passport-field-count, 7
+  {
+    break-if-!=
+    valid-passport-count <- increment
+  }
+  print-int32-decimal 0, valid-passport-count
+  print-string 0, "\n"
+  return 0
+}