about summary refs log tree commit diff stats
path: root/linux/arith.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/arith.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/arith.mu')
-rw-r--r--linux/arith.mu258
1 files changed, 258 insertions, 0 deletions
diff --git a/linux/arith.mu b/linux/arith.mu
new file mode 100644
index 00000000..3183cd31
--- /dev/null
+++ b/linux/arith.mu
@@ -0,0 +1,258 @@
+# Integer arithmetic using conventional precedence.
+#
+# Follows part 2 of Jack Crenshaw's "Let's build a compiler!"
+#   https://compilers.iecc.com/crenshaw
+#
+# Limitations:
+#   No division yet.
+#
+# To build:
+#   $ ./translate_mu apps/arith.mu
+#
+# Example session:
+#   $ ./a.elf
+#   press ctrl-c or ctrl-d to exit
+#   > 1
+#   1
+#   > 1+1
+#   2
+#   > 1 + 1
+#   2
+#   > 1+2 +3
+#   6
+#   > 1+2 *3
+#   7
+#   > (1+2) *3
+#   9
+#   > 1 + 3*4
+#   13
+#   > ^D
+#   $
+#
+# Error handling is non-existent. This is just a prototype.
+
+fn main -> _/ebx: int {
+  enable-keyboard-immediate-mode
+  var look/esi: grapheme <- copy 0  # lookahead
+  var n/eax: int <- copy 0  # result of each expression
+  print-string 0/screen, "press ctrl-c or ctrl-d to exit\n"
+  # read-eval-print loop
+  {
+    # print prompt
+    print-string 0/screen, "> "
+    # read and eval
+    n, look <- simplify  # we explicitly thread 'look' everywhere
+    # if (look == 0) break
+    compare look, 0
+    break-if-=
+    # print
+    print-int32-decimal 0/screen, n
+    print-string 0/screen, "\n"
+    #
+    loop
+  }
+  enable-keyboard-type-mode
+  return 0
+}
+
+fn simplify -> _/eax: int, _/esi: grapheme {
+  # prime the pump
+  var look/esi: grapheme <- get-char
+  # do it
+  var result/eax: int <- copy 0
+  result, look <- expression look
+  return result, look
+}
+
+fn expression _look: grapheme -> _/eax: int, _/esi: grapheme {
+  var look/esi: grapheme <- copy _look
+  # read arg
+  var result/eax: int <- copy 0
+  result, look <- term look
+  $expression:loop: {
+    # while next non-space char in ['+', '-']
+    look <- skip-spaces look
+    {
+      var continue?/eax: boolean <- is-add-or-sub? look
+      compare continue?, 0/false
+      break-if-= $expression:loop
+    }
+    # read operator
+    var op/ecx: grapheme <- copy 0
+    op, look <- operator look
+    # read next arg
+    var second/edx: int <- copy 0
+    look <- skip-spaces look
+    {
+      var tmp/eax: int <- copy 0
+      tmp, look <- term look
+      second <- copy tmp
+    }
+    # reduce
+    $expression:perform-op: {
+      {
+        compare op, 0x2b/+
+        break-if-!=
+        result <- add second
+        break $expression:perform-op
+      }
+      {
+        compare op, 0x2d/minus
+        break-if-!=
+        result <- subtract second
+        break $expression:perform-op
+      }
+    }
+    loop
+  }
+  look <- skip-spaces look
+  return result, look
+}
+
+fn term _look: grapheme -> _/eax: int, _/esi: grapheme {
+  var look/esi: grapheme <- copy _look
+  # read arg
+  look <- skip-spaces look
+  var result/eax: int <- copy 0
+  result, look <- factor look
+  $term:loop: {
+    # while next non-space char in ['*', '/']
+    look <- skip-spaces look
+    {
+      var continue?/eax: boolean <- is-mul-or-div? look
+      compare continue?, 0/false
+      break-if-= $term:loop
+    }
+    # read operator
+    var op/ecx: grapheme <- copy 0
+    op, look <- operator look
+    # read next arg
+    var second/edx: int <- copy 0
+    look <- skip-spaces look
+    {
+      var tmp/eax: int <- copy 0
+      tmp, look <- factor look
+      second <- copy tmp
+    }
+    # reduce
+    $term:perform-op: {
+      {
+        compare op, 0x2a/*
+        break-if-!=
+        result <- multiply second
+        break $term:perform-op
+      }
+#?       {
+#?         compare op, 0x2f/slash
+#?         break-if-!=
+#?         result <- divide second  # not in Mu yet
+#?         break $term:perform-op
+#?       }
+    }
+    loop
+  }
+  return result, look
+}
+
+fn factor _look: grapheme -> _/eax: int, _/esi: grapheme {
+  var look/esi: grapheme <- copy _look  # should be a no-op
+  look <- skip-spaces look
+  # if next char is not '(', parse a number
+  compare look, 0x28/open-paren
+  {
+    break-if-=
+    var result/eax: int <- copy 0
+    result, look <- num look
+    return result, look
+  }
+  # otherwise recurse
+  look <- get-char  # '('
+  var result/eax: int <- copy 0
+  result, look <- expression look
+  look <- skip-spaces look
+  look <- get-char  # ')'
+  return result, look
+}
+
+fn is-mul-or-div? c: grapheme -> _/eax: boolean {
+  compare c, 0x2a/*
+  {
+    break-if-!=
+    return 1/true
+  }
+  compare c, 0x2f/slash
+  {
+    break-if-!=
+    return 1/true
+  }
+  return 0/false
+}
+
+fn is-add-or-sub? c: grapheme -> _/eax: boolean {
+  compare c, 0x2b/+
+  {
+    break-if-!=
+    return 1/true
+  }
+  compare c, 0x2d/minus
+  {
+    break-if-!=
+    return 1/true
+  }
+  return 0/false
+}
+
+fn operator _look: grapheme -> _/ecx: grapheme, _/esi: grapheme {
+  var op/ecx: grapheme <- copy _look
+  var look/esi: grapheme <- get-char
+  return op, look
+}
+
+fn num _look: grapheme -> _/eax: int, _/esi: grapheme {
+  var look/esi: grapheme <- copy _look
+  var result/edi: int <- copy 0
+  {
+    var first-digit/eax: int <- to-decimal-digit look
+    result <- copy first-digit
+  }
+  {
+    look <- get-char
+    # done?
+    var digit?/eax: boolean <- is-decimal-digit? look
+    compare digit?, 0/false
+    break-if-=
+    # result *= 10
+    {
+      var ten/eax: int <- copy 0xa
+      result <- multiply ten
+    }
+    # result += digit(look)
+    var digit/eax: int <- to-decimal-digit look
+    result <- add digit
+    loop
+  }
+  return result, look
+}
+
+fn skip-spaces _look: grapheme -> _/esi: grapheme {
+  var look/esi: grapheme <- copy _look  # should be a no-op
+  {
+    compare look, 0x20
+    break-if-!=
+    look <- get-char
+    loop
+  }
+  return look
+}
+
+fn get-char -> _/esi: grapheme {
+  var look/eax: grapheme <- read-key-from-real-keyboard
+  print-grapheme-to-real-screen look
+  compare look, 4
+  {
+    break-if-!=
+    print-string 0/screen, "^D\n"
+    syscall_exit
+  }
+  return look
+}