https://github.com/akkartik/mu/blob/main/linux/apps/arith.mu

      1 # Integer arithmetic using conventional precedence.
      2 #
      3 # Follows part 2 of Jack Crenshaw's "Let's build a compiler!"
      4 #   https://compilers.iecc.com/crenshaw
      5 #
      6 # Limitations:
      7 #   No division yet.
      8 #
      9 # To build:
     10 #   $ ./translate apps/arith.mu
     11 #
     12 # Example session:
     13 #   $ ./a.elf
     14 #   press ctrl-c or ctrl-d to exit
     15 #   > 1
     16 #   1
     17 #   > 1+1
     18 #   2
     19 #   > 1 + 1
     20 #   2
     21 #   > 1+2 +3
     22 #   6
     23 #   > 1+2 *3
     24 #   7
     25 #   > (1+2) *3
     26 #   9
     27 #   > 1 + 3*4
     28 #   13
     29 #   > ^D
     30 #   $
     31 #
     32 # Error handling is non-existent. This is just a prototype.
     33
     34 fn main -> _/ebx: int {
     35   enable-keyboard-immediate-mode
     36   var look/esi: grapheme <- copy 0  # lookahead
     37   var n/eax: int <- copy 0  # result of each expression
     38   print-string 0/screen, "press ctrl-c or ctrl-d to exit\n"
     39   # read-eval-print loop
     40   {
     41     # print prompt
     42     print-string 0/screen, "> "
     43     # read and eval
     44     n, look <- simplify  # we explicitly thread 'look' everywhere
     45     # if (look == 0) break
     46     compare look, 0
     47     break-if-=
     48     # print
     49     print-int32-decimal 0/screen, n
     50     print-string 0/screen, "\n"
     51     #
     52     loop
     53   }
     54   enable-keyboard-type-mode
     55   return 0
     56 }
     57
     58 fn simplify -> _/eax: int, _/esi: grapheme {
     59   # prime the pump
     60   var look/esi: grapheme <- get-char
     61   # do it
     62   var result/eax: int <- copy 0
     63   result, look <- expression look
     64   return result, look
     65 }
     66
     67 fn expression _look: grapheme -> _/eax: int, _/esi: grapheme {
     68   var look/esi: grapheme <- copy _look
     69   # read arg
     70   var result/eax: int <- copy 0
     71   result, look <- term look
     72   $expression:loop: {
     73     # while next non-space char in ['+', '-']
     74     look <- skip-spaces look
     75     {
     76       var continue?/eax: boolean <- add-or-sub? look
     77       compare continue?, 0/false
     78       break-if-= $expression:loop
     79     }
     80     # read operator
     81     var op/ecx: grapheme <- copy 0
     82     op, look <- operator look
     83     # read next arg
     84     var second/edx: int <- copy 0
     85     look <- skip-spaces look
     86     {
     87       var tmp/eax: int <- copy 0
     88       tmp, look <- term look
     89       second <- copy tmp
     90     }
     91     # reduce
     92     $expression:perform-op: {
     93       {
     94         compare op, 0x2b/+
     95         break-if-!=
     96         result <- add second
     97         break $expression:perform-op
     98       }
     99       {
    100         compare op, 0x2d/minus
    101         break-if-!=
    102         result <- subtract second
    103         break $expression:perform-op
    104       }
    105     }
    106     loop
    107   }
    108   look <- skip-spaces look
    109   return result, look
    110 }
    111
    112 fn term _look: grapheme -> _/eax: int, _/esi: grapheme {
    113   var look/esi: grapheme <- copy _look
    114   # read arg
    115   look <- skip-spaces look
    116   var result/eax: int <- copy 0
    117   result, look <- factor look
    118   $term:loop: {
    119     # while next non-space char in ['*', '/']
    120     look <- skip-spaces look
    121     {
    122       var continue?/eax: boolean <- mul-or-div? look
    123       compare continue?, 0/false
    124       break-if-= $term:loop
    125     }
    126     # read operator
    127     var op/ecx: grapheme <- copy 0
    128     op, look <- operator look
    129     # read next arg
    130     var second/edx: int <- copy 0
    131     look <- skip-spaces look
    132     {
    133       var tmp/eax: int <- copy 0
    134       tmp, look <- factor look
    135       second <- copy tmp
    136     }
    137     # reduce
    138     $term:perform-op: {
    139       {
    140         compare op, 0x2a/*
    141         break-if-!=
    142         result <- multiply second
    143         break $term:perform-op
    144       }
    145 #?       {
    146 #?         compare op, 0x2f/slash
    147 #?         break-if-!=
    148 #?         result <- divide second  # not in Mu yet
    149 #?         break $term:perform-op
    150 #?       }
    151     }
    152     loop
    153   }
    154   return result, look
    155 }
    156
    157 fn factor _look: grapheme -> _/eax: int, _/esi: grapheme {
    158   var look/esi: grapheme <- copy _look  # should be a no-op
    159   look <- skip-spaces look
    160   # if next char is not '(', parse a number
    161   compare look, 0x28/open-paren
    162   {
    163     break-if-=
    164     var result/eax: int <- copy 0
    165     result, look <- num look
    166     return result, look
    167   }
    168   # otherwise recurse
    169   look <- get-char  # '('
    170   var result/eax: int <- copy 0
    171   result, look <- expression look
    172   look <- skip-spaces look
    173   look <- get-char  # ')'
    174   return result, look
    175 }
    176
    177 fn mul-or-div? c: grapheme -> _/eax: boolean {
    178   compare c, 0x2a/*
    179   {
    180     break-if-!=
    181     return 1/true
    182   }
    183   compare c, 0x2f/slash
    184   {
    185     break-if-!=
    186     return 1/true
    187   }
    188   return 0/false
    189 }
    190
    191 fn add-or-sub? c: grapheme -> _/eax: boolean {
    192   compare c, 0x2b/+
    193   {
    194     break-if-!=
    195     return 1/true
    196   }
    197   compare c, 0x2d/minus
    198   {
    199     break-if-!=
    200     return 1/true
    201   }
    202   return 0/false
    203 }
    204
    205 fn operator _look: grapheme -> _/ecx: grapheme, _/esi: grapheme {
    206   var op/ecx: grapheme <- copy _look
    207   var look/esi: grapheme <- get-char
    208   return op, look
    209 }
    210
    211 fn num _look: grapheme -> _/eax: int, _/esi: grapheme {
    212   var look/esi: grapheme <- copy _look
    213   var result/edi: int <- copy 0
    214   {
    215     var first-digit/eax: int <- to-decimal-digit look
    216     result <- copy first-digit
    217   }
    218   {
    219     look <- get-char
    220     # done?
    221     var digit?/eax: boolean <- decimal-digit? look
    222     compare digit?, 0/false
    223     break-if-=
    224     # result *= 10
    225     {
    226       var ten/eax: int <- copy 0xa
    227       result <- multiply ten
    228     }
    229     # result += digit(look)
    230     var digit/eax: int <- to-decimal-digit look
    231     result <- add digit
    232     loop
    233   }
    234   return result, look
    235 }
    236
    237 fn skip-spaces _look: grapheme -> _/esi: grapheme {
    238   var look/esi: grapheme <- copy _look  # should be a no-op
    239   {
    240     compare look, 0x20
    241     break-if-!=
    242     look <- get-char
    243     loop
    244   }
    245   return look
    246 }
    247
    248 fn get-char -> _/esi: grapheme {
    249   var look/eax: grapheme <- read-key-from-real-keyboard
    250   print-grapheme-to-real-screen look
    251   compare look, 4
    252   {
    253     break-if-!=
    254     print-string 0/screen, "^D\n"
    255     syscall_exit
    256   }
    257   return look
    258 }