fn parse-input tokens: (addr stream token), out: (addr handle cell), trace: (addr trace) { rewind-stream tokens var empty?/eax: boolean <- stream-empty? tokens compare empty?, 0/false { break-if-= error trace, "nothing to parse" return } var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, out, trace { compare close-paren?, 0/false break-if-= error trace, "')' is not a valid expression" return } { compare dot?, 0/false break-if-= error trace, "'.' is not a valid expression" return } { var empty?/eax: boolean <- stream-empty? tokens compare empty?, 0/false break-if-!= error trace, "unexpected tokens at end; only type in a single expression at a time" } } # return values: # unmatched close-paren encountered? # dot encountered? (only used internally by recursive calls) fn parse-sexpression tokens: (addr stream token), _out: (addr handle cell), trace: (addr trace) -> _/eax: boolean, _/ecx: boolean { trace-text trace, "parse", "parse" trace-lower trace var curr-token-storage: token var curr-token/ecx: (addr token) <- address curr-token-storage var empty?/eax: boolean <- stream-empty? tokens compare empty?, 0/false { break-if-= error trace, "end of stream; never found a balancing ')'" trace-higher trace return 1/true, 0/false } read-from-stream tokens, curr-token $parse-sexpression:type-check: { # single quote -> parse as list with a special car var quote-token?/eax: boolean <- quote-token? curr-token compare quote-token?, 0/false { break-if-= var out/edi: (addr handle cell) <- copy _out allocate-pair out var out-addr/eax: (addr cell) <- lookup *out var left-ah/edx: (addr handle cell) <- get out-addr, left new-symbol left-ah, "'" var right-ah/edx: (addr handle cell) <- get out-addr, right var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, right-ah, trace trace-higher trace return close-paren?, dot? } # backquote quote -> parse as list with a special car var backquote-token?/eax: boolean <- backquote-token? curr-token compare backquote-token?, 0/false { break-if-= var out/edi: (addr handle cell) <- copy _out allocate-pair out var out-addr/eax: (addr cell) <- lookup *out var left-ah/edx: (addr handle cell) <- get out-addr, left new-symbol left-ah, "`" var right-ah/edx: (addr handle cell) <- get out-addr, right var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, right-ah, trace trace-higher trace return close-paren?, dot? } # unquote -> parse as list with a special car var unquote-token?/eax: boolean <- unquote-token? curr-token compare unquote-token?, 0/false { break-if-= var out/edi: (addr handle cell) <- copy _out allocate-pair out var out-addr/eax: (addr cell) <- lookup *out var left-ah/edx: (addr handle cell) <- get out-addr, left new-symbol left-ah, "," var right-ah/edx: (addr handle cell) <- get out-addr, right var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, right-ah, trace trace-higher trace return close-paren?, dot? } # unquote-splice -> parse as list with a special car var unquote-splice-token?/eax: boolean <- unquote-splice-token? curr-token compare unquote-splice-token?, 0/false { break-if-= var out/edi: (addr handle cell) <- copy _out allocate-pair out var out-addr/eax: (addr cell) <- lookup *out var left-ah/edx: (addr handle cell) <- get out-addr, left new-symbol left-ah, ",@" var right-ah/edx: (addr handle cell) <- get out-addr, right var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, right-ah, trace trace-higher trace return close-paren?, dot? } # dot -> return var dot?/eax: boolean <- dot-token? curr-token compare dot?, 0/false { break-if-= trace-higher trace return 0/false, 1/true } # not bracket -> parse atom var bracket-token?/eax: boolean <- bracket-token? curr-token compare bracket-token?, 0/false { break-if-!= parse-atom curr-token, _out, trace break $parse-sexpression:type-check } # open paren -> parse list var open-paren?/eax: boolean <- open-paren-token? curr-token compare open-paren?, 0/false { break-if-= var curr/esi: (addr handle cell) <- copy _out allocate-pair curr var curr-addr/eax: (addr cell) <- lookup *curr var left/edx: (addr handle cell) <- get curr-addr, left { var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, left, trace { compare dot?, 0/false break-if-= error trace, "'.' cannot be at the start of a list" return 1/true, dot? } compare close-paren?, 0/false break-if-!= var curr-addr/eax: (addr cell) <- lookup *curr curr <- get curr-addr, right var tmp-storage: (handle cell) var tmp/edx: (addr handle cell) <- address tmp-storage $parse-sexpression:list-loop: { var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, tmp, trace # '.' -> clean up right here and return compare dot?, 0/false { break-if-= parse-dot-tail tokens, curr, trace return 0/false, 0/false } allocate-pair curr # ')' -> return compare close-paren?, 0/false break-if-!= var curr-addr/eax: (addr cell) <- lookup *curr var left/ecx: (addr handle cell) <- get curr-addr, left copy-object tmp, left # curr <- get curr-addr, right loop } } break $parse-sexpression:type-check } # close paren -> return var close-paren?/eax: boolean <- close-paren-token? curr-token compare close-paren?, 0/false { break-if-= trace-higher trace return 1/true, 0/false } # otherwise abort var stream-storage: (stream byte 0x400) var stream/edx: (addr stream byte) <- address stream-storage write stream, "unexpected token " var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data var curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah rewind-stream curr-token-data write-stream stream, curr-token-data error-stream trace, stream } trace-higher trace return 0/false, 0/false } fn parse-atom _curr-token: (addr token), _out: (addr handle cell), trace: (addr trace) { trace-text trace, "parse", "parse atom" var curr-token/ecx: (addr token) <- copy _curr-token var curr-token-data-ah/eax: (addr handle stream byte) <- get curr-token, text-data var _curr-token-data/eax: (addr stream byte) <- lookup *curr-token-data-ah var curr-token-data/esi: (addr stream byte) <- copy _curr-token-data trace trace, "parse", curr-token-data # number var number-token?/eax: boolean <- number-token? curr-token compare number-token?, 0/false { break-if-= rewind-stream curr-token-data var _val/eax: int <- parse-decimal-int-from-stream curr-token-data var val/ecx: int <- copy _val var val-float/xmm0: float <- convert val allocate-number _out var out/eax: (addr handle cell) <- copy _out var out-addr/eax: (addr cell) <- lookup *out var dest/edi: (addr float) <- get out-addr, number-data copy-to *dest, val-float { { var should-trace?/eax: boolean <- should-trace? trace compare should-trace?, 0/false } break-if-= var stream-storage: (stream byte 0x400) var stream/ecx: (addr stream byte) <- address stream-storage write stream, "=> number " var nested-trace-storage: trace var nested-trace/edi: (addr trace) <- address nested-trace-storage initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible print-number out-addr, stream, nested-trace trace trace, "parse", stream } return } # default: copy either to a symbol or a stream # stream token -> literal var stream-token?/eax: boolean <- stream-token? curr-token compare stream-token?, 0/false { break-if-= allocate-stream _out } compare stream-token?, 0/false { break-if-!= allocate-symbol _out } # copy token data var out/eax: (addr handle cell) <- copy _out var out-addr/eax: (addr cell) <- lookup *out var curr-token-data-ah/ecx: (addr handle stream byte) <- get curr-token, text-data var dest-ah/edx: (addr handle stream byte) <- get out-addr, text-data copy-object curr-token-data-ah, dest-ah { { var should-trace?/eax: boolean <- should-trace? trace compare should-trace?, 0/false } break-if-= var stream-storage: (stream byte 0x40000) var stream/ecx: (addr stream byte) <- address stream-storage write stream, "=> symbol " var nested-trace-storage: trace var nested-trace/edi: (addr trace) <- address nested-trace-storage initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible print-symbol out-addr, stream, nested-trace trace trace, "parse", stream } } fn parse-dot-tail tokens: (addr stream token), _out: (addr handle cell), trace: (addr trace) { var out/edi: (addr handle cell) <- copy _out var close-paren?/eax: boolean <- copy 0/false var dot?/ecx: boolean <- copy 0/false close-paren?, dot? <- parse-sexpression tokens, out, trace compare close-paren?, 0/false { break-if-= error trace, "'. )' makes no sense" return } compare dot?, 0/false { break-if-= error trace, "'. .' makes no sense" return } # var dummy: (handle cell) var dummy-ah/edi: (addr handle cell) <- address dummy close-paren?, dot? <- parse-sexpression tokens, dummy-ah, trace compare close-paren?, 0/false { break-if-!= error trace, "cannot have multiple expressions between '.' and ')'" return } compare dot?, 0/false { break-if-= error trace, "cannot have two dots in a single list" return } }