https://github.com/akkartik/mu/blob/main/shell/parse.mu
  1 fn parse-input tokens: (addr stream cell), out: (addr handle cell), trace: (addr trace) {
  2   rewind-stream tokens
  3   var empty?/eax: boolean <- stream-empty? tokens
  4   compare empty?, 0/false
  5   {
  6     break-if-=
  7     error trace, "nothing to parse"
  8     return
  9   }
 10   var close-paren?/eax: boolean <- parse-sexpression tokens, out, trace
 11   {
 12     compare close-paren?, 0/false
 13     break-if-=
 14     error trace, "')' is not a valid expression"
 15     return
 16   }
 17   {
 18     var empty?/eax: boolean <- stream-empty? tokens
 19     compare empty?, 0/false
 20     break-if-!=
 21     error trace, "unexpected tokens at end; only type in a single expression at a time"
 22   }
 23 }
 24 
 25 # return value: true if close-paren was encountered
 26 fn parse-sexpression tokens: (addr stream cell), _out: (addr handle cell), trace: (addr trace) -> _/eax: boolean {
 27   trace-text trace, "read", "parse"
 28   trace-lower trace
 29   var curr-token-storage: cell
 30   var curr-token/ecx: (addr cell) <- address curr-token-storage
 31   var empty?/eax: boolean <- stream-empty? tokens
 32   compare empty?, 0/false
 33   {
 34     break-if-=
 35     error trace, "end of stream; never found a balancing ')'"
 36     return 1/true
 37   }
 38   read-from-stream tokens, curr-token
 39   $parse-sexpression:type-check: {
 40     # single quote -> parse as list with a special car
 41     var quote-token?/eax: boolean <- quote-token? curr-token
 42     compare quote-token?, 0/false
 43     {
 44       break-if-=
 45       var out/edi: (addr handle cell) <- copy _out
 46       allocate-pair out
 47       var out-addr/eax: (addr cell) <- lookup *out
 48       var left-ah/ecx: (addr handle cell) <- get out-addr, left
 49       new-symbol left-ah, "'"
 50       var right-ah/ecx: (addr handle cell) <- get out-addr, right
 51       var result/eax: boolean <- parse-sexpression tokens, right-ah, trace
 52       return result
 53     }
 54     # not bracket -> parse atom
 55     var bracket-token?/eax: boolean <- bracket-token? curr-token
 56     compare bracket-token?, 0/false
 57     {
 58       break-if-!=
 59       parse-atom curr-token, _out, trace
 60       break $parse-sexpression:type-check
 61     }
 62     # open paren -> parse list
 63     var open-paren?/eax: boolean <- open-paren-token? curr-token
 64     compare open-paren?, 0/false
 65     {
 66       break-if-=
 67       var curr/esi: (addr handle cell) <- copy _out
 68       $parse-sexpression:list-loop: {
 69         allocate-pair curr
 70         var curr-addr/eax: (addr cell) <- lookup *curr
 71         var left/ecx: (addr handle cell) <- get curr-addr, left
 72         {
 73           var close-paren?/eax: boolean <- parse-sexpression tokens, left, trace
 74           compare close-paren?, 0/false
 75           break-if-!= $parse-sexpression:list-loop
 76         }
 77         #
 78         curr <- get curr-addr, right
 79         loop
 80       }
 81       break $parse-sexpression:type-check
 82     }
 83     # close paren -> parse list
 84     var close-paren?/eax: boolean <- close-paren-token? curr-token
 85     compare close-paren?, 0/false
 86     {
 87       break-if-=
 88       trace-higher trace
 89       return 1/true
 90     }
 91     # otherwise abort
 92     var stream-storage: (stream byte 0x40)
 93     var stream/edx: (addr stream byte) <- address stream-storage
 94     write stream, "unexpected token "
 95     var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data
 96     var curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah
 97     rewind-stream curr-token-data
 98     write-stream stream, curr-token-data
 99     trace trace, "error", stream
100   }
101   trace-higher trace
102   return 0/false
103 }
104 
105 fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr trace) {
106   trace-text trace, "read", "parse atom"
107   var curr-token/ecx: (addr cell) <- copy _curr-token
108   var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data
109   var _curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah
110   var curr-token-data/esi: (addr stream byte) <- copy _curr-token-data
111   trace trace, "read", curr-token-data
112   # number
113   var number-token?/eax: boolean <- number-token? curr-token
114   compare number-token?, 0/false
115   {
116     break-if-=
117     rewind-stream curr-token-data
118     var _val/eax: int <- parse-decimal-int-from-stream curr-token-data
119     var val/ecx: int <- copy _val
120     var val-float/xmm0: float <- convert val
121     allocate-number _out
122     var out/eax: (addr handle cell) <- copy _out
123     var out-addr/eax: (addr cell) <- lookup *out
124     var dest/edi: (addr float) <- get out-addr, number-data
125     copy-to *dest, val-float
126     {
127       var stream-storage: (stream byte 0x40)
128       var stream/ecx: (addr stream byte) <- address stream-storage
129       write stream, "=> number "
130       print-number out-addr, stream, 0/no-trace
131       trace trace, "read", stream
132     }
133     return
134   }
135   # default: symbol
136   # just copy token data
137   allocate-symbol _out
138   var out/eax: (addr handle cell) <- copy _out
139   var out-addr/eax: (addr cell) <- lookup *out
140   var curr-token-data-ah/ecx: (addr handle stream byte) <- get curr-token, text-data
141   var dest-ah/edx: (addr handle stream byte) <- get out-addr, text-data
142   copy-object curr-token-data-ah, dest-ah
143   {
144     var stream-storage: (stream byte 0x40)
145     var stream/ecx: (addr stream byte) <- address stream-storage
146     write stream, "=> symbol "
147     print-symbol out-addr, stream, 0/no-trace
148     trace trace, "read", stream
149   }
150 }