# Function calls in a single line.
#
# To run (on Linux):
# $ ./translate_subx init.linux 0*.subx apps/subx-params.subx apps/calls.subx
# $ mv a.elf apps/calls
#
# Example 1:
# $ echo '(foo %eax)' | apps/calls
# # . (foo %eax) # output has comments
# ff 6/subop/push %eax # push
# e8/call foo/disp32 # call
# 81 0/subop/add %esp 4/imm32 # undo push
#
# Example 2:
# $ echo '(foo Var1 *(eax + 4) "blah")' | apps/calls
# # . (foo Var1 *(eax + 4) "blah")
# 68/push "blah"/imm32
# ff 6/subop/push *(eax + 4) # push args in..
# 68/push Var1/imm32 # ..reverse order
# e8/call foo/disp32
# 81 0/subop/add %esp 4/imm32 # undo pushes
#
# Calls always begin with '(' as the first non-whitespace on a line.
== code
Entry: # run tests if necessary, convert stdin if not
# . prologue
89/<- %ebp 4/r32/esp
# initialize heap
# . Heap = new-segment(Heap-size)
# . . push args
68/push Heap/imm32
ff 6/subop/push *Heap-size
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
81 7/subop/compare *ebp 1/imm32
7e/jump-if-<= $subx-calls-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . if (eax == false) goto run-main
3d/compare-eax-and 0/imm32/false
74/jump-if-= $subx-calls-main:interactive/disp8
# run-tests()
e8/call run-tests/disp32
# syscall(exit, *Num-test-failures)
8b/-> *Num-test-failures 3/r32/ebx
eb/jump $subx-calls-main:end/disp8
$subx-calls-main:interactive:
# - otherwise convert stdin
# subx-calls(Stdin, Stdout)
# . . push args
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call subx-calls/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$subx-calls-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
subx-calls: # in : (addr buffered-file), out : (addr buffered-file)
# pseudocode:
# var line : (stream byte 512)
# var words : (stream slice 16) # at most function name and 15 args
# while true
# clear-stream(line)
# read-line-buffered(in, line)
# if (line->write == 0) break # end of file
# skip-chars-matching(line, ' ')
# if line->data[line->read] != '('
# write-stream-data(out, line)
# continue
# # emit comment
# write-buffered(out, "# . ")
# write-stream-data(out, line)
# # emit code
# ++line->read to skip '('
# clear-stream(words)
# words = parse-line(line)
# emit-call(out, words)
# flush(out)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
56/push-esi
# var line/esi : (stream byte 512)
81 5/subop/subtract %esp 0x200/imm32
68/push 0x200/imm32/length
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %esi 4/r32/esp
# var words/edx : (stream slice 128) # 16 rows * 8 bytes/row
81 5/subop/subtract %esp 0x80/imm32
68/push 0x80/imm32/length
68/push 0/imm32/read
68/push 0/imm32/write
89/<- %edx 4/r32/esp
$subx-calls:loop:
# clear-stream(line)
# . . push args
56/push-esi
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# read-line-buffered(in, line)
# . . push args
56/push-esi
ff 6/subop/push *(ebp+8)
# . . call
e8/call read-line-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$subx-calls:check0:
# if (line->write == 0) break
81 7/subop/compare *esi 0/imm32
0f 84/jump-if-= $subx-calls:break/disp32
# skip-chars-matching(line, ' ')
# . . push args
68/push 0x20/imm32/space
56/push-esi
# . . call
e8/call skip-chars-matching/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# if (line->data[line->read] == '(') goto convert-call
# . ecx = line->read
8b/-> *(esi+4) 1/r32/ecx
# . eax = line->data[line->read]
31/xor %eax 0/r32/eax
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax == '(') goto convert-call
3d/compare-eax-and 0x28/imm32/open-paren
74/jump-if-= $subx-calls:convert-call/disp8
$subx-calls:pass-through:
# write-stream-data(out, line)
# . . push args
56/push-esi
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# continue
eb/jump $subx-calls:loop/disp8
$subx-calls:convert-call:
# - emit comment
# write-buffered(out, "# . ")
# . . push args
68/push "# . "/imm32
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-stream-data(out, line)
# . . push args
56/push-esi
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# - emit code
# ++line->read to skip '('
ff 0/subop/increment *(esi+4)
# clear-stream(words)
# . . push args
52/push-edx
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# words = parse-line(line)
# . . push args
52/push-edx
56/push-esi
# . . call
e8/call parse-line/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# emit-call(out, words)
# . . push args
52/push-edx
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call emit-call/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# loop
e9/jump $subx-calls:loop/disp32
$subx-calls:break:
# flush(out)
# . . push args
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
$subx-calls:end:
# . reclaim locals
81 0/subop/add %esp 0x298/imm32 # 0x20c + 0x8c
# . restore registers
5e/pop-to-esi
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
parse-line: # line : (addr stream byte), words : (addr stream slice)
# pseudocode:
# var word-slice : slice
# while true
# word-slice = next-word-string-or-expression-without-metadata(line)
# if slice-empty?(word-slice)
# break # end of line
# write-int(words, word-slice->start)
# write-int(words, word-slice->end)
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# var word-slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
$parse-line:loop:
# word-slice = next-word-string-or-expression-without-metadata(line)
# . . push args
51/push-ecx
ff 6/subop/push *(ebp+8)
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$parse-line:check1:
# if (slice-empty?(word-slice)) break
# . eax = slice-empty?(word-slice)
# . . push args
51/push-ecx
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $parse-line:end/disp32
#? # dump word-slice {{{
#? # . write(2/stderr, "w: ")
#? # . . push args
#? 68/push "w: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # . clear-stream($Stderr->buffer)
#? # . . push args
#? 68/push $Stderr->buffer/imm32
#? # . . call
#? e8/call clear-stream/disp32
#? # . . discard args
#? 81 0/subop/add %esp 4/imm32
#? # . write-slice-buffered(Stderr, word-slice)
#? # . . push args
#? 51/push-ecx
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-slice-buffered/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add %esp 4/imm32
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # }}}
$parse-line:write-word:
# write-int(words, word-slice->start)
# . . push args
ff 6/subop/push *ecx
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call write-int/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-int(words, word-slice->end)
# . . push args
ff 6/subop/push *(ecx+4)
ff 6/subop/push *(ebp+0xc)
# . . call
e8/call write-int/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# loop
e9/jump $parse-line:loop/disp32
$parse-line:end:
# . reclaim locals
81 0/subop/add %esp 8/imm32
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-call: # out : (addr buffered-file), words : (addr stream slice)
# pseudocode:
# if (words->write < 8) abort
# curr = &words->data[words->write-8]
# min = words->data
# # emit pushes
# while true
# if (curr <= min) break
# if *curr->start in '%' '*'
# write-buffered(out, "ff 6/subop/push ")
# write-slice-buffered(out, curr)
# write-buffered(out, "\n")
# else
# write-buffered(out, "68/push ")
# write-slice-buffered(out, curr)
# write-buffered(out, "/imm32\n")
# curr -= 8
# # emit call
# write-buffered(out, "e8/call ")
# write-slice-buffered(out, curr)
# write-buffered(out, "/disp32\n")
# # emit pops
# write-buffered(out, "81 0/subop/add %esp ")
# print-int32-buffered(out, words->write >> 1 - 4)
# write-buffered(out, "/imm32\n")
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
56/push-esi
# esi = words
8b/-> *(ebp+0xc) 6/r32/esi
# if (words->write < 8) abort
# . ecx = words->write - 8
8b/-> *esi 1/r32/ecx
81 5/subop/subtract %ecx 8/imm32
0f 8c/jump-if-< $emit-call:error1/disp32
# var curr/ecx : (addr slice) = &words->data[words->write-8]
8d/copy-address *(esi+ecx+0xc) 1/r32/ecx
# var min/edx : (addr byte) = words->data
8d/copy-address *(esi+0xc) 2/r32/edx
# - emit pushes
$emit-call:push-loop:
# if (curr <= min) break
39/compare %ecx 2/r32/edx
0f 8e/jump-if-<= $emit-call:call-instruction/disp32
# if (*curr->start in '%' '*') goto push-rm32
# . var start/eax : (addr byte) = curr->start
8b/-> *ecx 0/r32/eax
# . var c/eax : byte = *eax
8b/-> *eax 0/r32/eax
81 4/subop/and %eax 0xff/imm32
# . if (c == '%') goto push-rm32
3d/compare-eax-and 0x25/imm32/percent
74/jump-if-= $emit-call:push-rm32/disp8
# . if (c == '*') goto push-rm32
3d/compare-eax-and 0x2a/imm32/asterisk
74/jump-if-= $emit-call:push-rm32/disp8
$emit-call:push-imm32:
# write-buffered(out, "68/push ")
68/push "68/push "/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-slice-buffered(out, curr)
# . . push args
51/push-ecx
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-buffered(out, "/imm32\n")
68/push "/imm32\n"/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# continue
eb/jump $emit-call:next-push/disp8
$emit-call:push-rm32:
# write-buffered(out, "ff 6/subop/push ")
# . . push args
68/push "ff 6/subop/push "/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-slice-buffered(out, curr)
# . . push args
51/push-ecx
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-buffered(out, "\n")
68/push Newline/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$emit-call:next-push:
# curr -= 8
81 5/subop/subtract %ecx 8/imm32
# loop
e9/jump $emit-call:push-loop/disp32
$emit-call:call-instruction:
# write-buffered(out, "e8/call ")
68/push "e8/call "/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-slice-buffered(out, curr)
# . . push args
51/push-ecx
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-buffered(out, "/disp32\n")
68/push "/disp32\n"/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$emit-call:pop-instruction:
# write-buffered(out, "81 0/subop/add %esp ")
68/push "81 0/subop/add %esp "/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# print-int32-buffered(out, words->write >> 1 - 4)
# . . push args
8b/-> *esi 0/r32/eax
c1/shift 7/subop/arith-right %eax 1/imm8
2d/subtract-from-eax 4/imm32
50/push-eax
ff 6/subop/push *(ebp+8)
# . . call
e8/call print-int32-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# write-buffered(out, "/imm32\n")
68/push "/imm32\n"/imm32
ff 6/subop/push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$emit-call:end:
# . restore registers
5e/pop-to-esi
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$emit-call:error1:
# print(stderr, "error: calls.subx: '()' is not a valid call")
# . write-buffered(Stderr, "error: calls.subx: '()' is not a valid call")
# . . push args
68/push "error: calls.subx: '()' is not a valid call"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
test-subx-calls-passes-most-lines-through:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream($_test-input-buffered-file->buffer)
# . . push args
68/push $_test-input-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . write(_test-input-stream, "== abcd 0x1\n")
# . . push args
68/push "== abcd 0x1\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# subx-calls(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call subx-calls/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check that the line just passed through
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . check-stream-equal(_test-output-stream, "== abcd 0x1\n", msg)
# . . push args
68/push "F - test-subx-calls-passes-most-lines-through"/imm32
68/push "== abcd 0x1\n"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-subx-calls-processes-calls:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream($_test-input-buffered-file->buffer)
# . . push args
68/push $_test-input-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . write(_test-input-stream, "(foo %eax)\n")
# . . push args
68/push "(foo %eax)\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# subx-calls(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call subx-calls/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check that the line just passed through
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
#? # dump _test-output-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add %esp 8/imm32
#? # . rewind-stream(_test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add %esp 4/imm32
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "# . (foo %eax)", msg)
# . . push args
68/push "F - test-subx-calls-processes-calls: comment"/imm32
68/push "# . (foo %eax)"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . check-next-stream-line-equal(_test-output-stream, "ff 6/subop/push %eax", msg)
# . . push args
68/push "F - test-subx-calls-processes-calls: arg 0"/imm32
68/push "ff 6/subop/push %eax"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . check-next-stream-line-equal(_test-output-stream, "e8/call foo/disp32", msg)
# . . push args
68/push "F - test-subx-calls-processes-calls: call"/imm32
68/push "e8/call foo/disp32"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . check-next-stream-line-equal(_test-output-stream, "81 0/subop/add %esp 4/imm32", msg)
# . . push args
68/push "F - test-subx-calls-processes-calls: pops"/imm32
68/push "81 0/subop/add %esp 0x00000004/imm32"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
next-word-string-or-expression-without-metadata: # line : (addr stream byte), out : (addr slice)
# pseudocode:
# skip-chars-matching(line, ' ')
# if line->read >= line->write # end of line
# out = {0, 0}
# return
# out->start = &line->data[line->read]
# if line->data[line->read] == '#' # comment
# out->end = &line->data[line->write] # skip to end of line
# return
# if line->data[line->read] == '"' # string literal
# skip-string(line)
# out->end = &line->data[line->read] # no metadata
# return
# if line->data[line->read] == '*' # expression
# if line->data[line->read + 1] == ' '
# abort
# if line->data[line->read + 1] == '('
# skip-until-close-paren(line)
# if (line->data[line->read] != ')'
# abort
# ++line->read to skip ')'
# out->end = &line->data[line->read]
# return
# if line->data[line->read] == ')'
# ++line->read to skip ')'
# # make sure there's nothing else of importance
# if line->read >= line->write
# out = {0, 0}
# return
# if line->data[line->read] != ' '
# abort
# skip-chars-matching-whitespace(line)
# if line->read >= line->write
# out = {0, 0}
# return
# if line->data[line->read] == '#' # only thing permitted after ')' is a comment
# out = {0, 0}
# return
# abort
# # default case: read a word -- but no metadata
# while true
# if line->read >= line->write
# break
# if line->data[line->read] == ' '
# break
# if line->data[line->read] == ')'
# break
# if line->data[line->read] == '/'
# abort
# ++line->read
# out->end = &line->data[line->read]
#
# registers:
# ecx: often line->read
# eax: often line->data[line->read]
#
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
50/push-eax
51/push-ecx
56/push-esi
57/push-edi
# esi = line
8b/-> *(ebp+8) 6/r32/esi
# edi = out
8b/-> *(ebp+0xc) 7/r32/edi
# skip-chars-matching(line, ' ')
# . . push args
68/push 0x20/imm32/space
ff 6/subop/push *(ebp+8)
# . . call
e8/call skip-chars-matching/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
$next-word-string-or-expression-without-metadata:check0:
# if (line->read >= line->write) return out = {0, 0}
# . ecx = line->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= line->write) return out = {0, 0}
3b/compare 1/r32/ecx *esi
0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/disp32
$next-word-string-or-expression-without-metadata:check-for-comment:
# out->start = &line->data[line->read]
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *edi 0/r32/eax
# if (line->data[line->read] != '#') goto next check
# . var eax : byte = line->data[line->read]
31/xor %eax 0/r32/eax
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax != '#') goto next check
3d/compare-eax-and 0x23/imm32/pound
75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-string-literal/disp8
$next-word-string-or-expression-without-metadata:comment:
# out->end = &line->data[line->write]
8b/-> *esi 0/r32/eax
8d/copy-address *(esi+eax+0xc) 0/r32/eax
89/<- *(edi+4) 0/r32/eax
# line->read = line->write # skip rest of line
8b/-> *esi 0/r32/eax
89/<- *(esi+4) 0/r32/eax
# return
e9/jump $next-word-string-or-expression-without-metadata:end/disp32
$next-word-string-or-expression-without-metadata:check-for-string-literal:
# if (line->data[line->read] != '"') goto next check
3d/compare-eax-and 0x22/imm32/dquote
75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-expression/disp8
$next-word-string-or-expression-without-metadata:string-literal:
# skip-string(line)
# . . push args
56/push-esi
# . . call
e8/call skip-string/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# out->end = &line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *(edi+4) 0/r32/eax
# return
e9/jump $next-word-string-or-expression-without-metadata:end/disp32
$next-word-string-or-expression-without-metadata:check-for-expression:
# if (line->data[line->read] != '*') goto next check
3d/compare-eax-and 0x2a/imm32/asterisk
75/jump-if-!= $next-word-string-or-expression-without-metadata:check-for-end-of-call/disp8
# if (line->data[line->read + 1] == ' ') goto error1
8a/copy-byte *(esi+ecx+0xd) 0/r32/AL
3d/compare-eax-and 0x20/imm32/space
0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error1/disp32
# if (line->data[line->read + 1] != '(') goto regular-word
3d/compare-eax-and 0x28/imm32/open-paren
0f 85/jump-if-!= $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
$next-word-string-or-expression-without-metadata:paren:
# skip-until-close-paren(line)
# . . push args
56/push-esi
# . . call
e8/call skip-until-close-paren/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# if (line->data[line->read] != ')') goto error2
# . eax = line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax != ')') goto error2
3d/compare-eax-and 0x29/imm32/close-paren
0f 85/jump-if-!= $next-word-string-or-expression-without-metadata:error2/disp32
# ++line->read to skip ')'
ff 0/subop/increment *(esi+4)
# out->end = &line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *(edi+4) 0/r32/eax
# return
e9/jump $next-word-string-or-expression-without-metadata:end/disp32
$next-word-string-or-expression-without-metadata:check-for-end-of-call:
# if (line->data[line->read] != ')') goto next check
3d/compare-eax-and 0x29/imm32/close-paren
75/jump-if-!= $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp8
# ++line->read to skip ')'
ff 0/subop/increment *(esi+4)
# - error checking: make sure there's nothing else of importance on the line
# if (line->read >= line->write) return out = {0, 0}
# . ecx = line->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= line->write) return {0, 0}
3b/compare 1/r32/ecx *esi
0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/disp32
# if (line->data[line->read] == '/') goto error3
# . eax = line->data[line->read]
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax == '/') goto error3
3d/compare-eax-and 0x2f/imm32/slash
0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error3/disp32
# skip-chars-matching-whitespace(line)
# . . push args
56/push-esi
# . . call
e8/call skip-chars-matching-whitespace/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# if (line->read >= line->write) return out = {0, 0}
# . ecx = line->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= line->write) return {0, 0}
3b/compare 1/r32/ecx *esi
0f 8d/jump-if->= $next-word-string-or-expression-without-metadata:return-eol/disp32
# if (line->data[line->read] == '#') return out = {0, 0}
# . eax = line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax == '#') return out = {0, 0}
3d/compare-eax-and 0x23/imm32/pound
74/jump-if-= $next-word-string-or-expression-without-metadata:return-eol/disp8
# otherwise goto error4
e9/jump $next-word-string-or-expression-without-metadata:error4/disp32
$next-word-string-or-expression-without-metadata:regular-word-without-metadata:
# if (line->read >= line->write) break
# . ecx = line->read
8b/-> *(esi+4) 1/r32/ecx
# . if (ecx >= line->write) break
3b/compare *esi 1/r32/ecx
7d/jump-if->= $next-word-string-or-expression-without-metadata:regular-word-break/disp8
# if (line->data[line->read] == ' ') break
# . eax = line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
# . if (eax == ' ') break
3d/compare-eax-and 0x20/imm32/space
74/jump-if-= $next-word-string-or-expression-without-metadata:regular-word-break/disp8
# if (line->data[line->read] == ')') break
3d/compare-eax-and 0x29/imm32/close-paren
0f 84/jump-if-= $next-word-string-or-expression-without-metadata:regular-word-break/disp32
# if (line->data[line->read] == '/') goto error5
3d/compare-eax-and 0x2f/imm32/slash
0f 84/jump-if-= $next-word-string-or-expression-without-metadata:error5/disp32
# ++line->read
ff 0/subop/increment *(esi+4)
# loop
e9/jump $next-word-string-or-expression-without-metadata:regular-word-without-metadata/disp32
$next-word-string-or-expression-without-metadata:regular-word-break:
# out->end = &line->data[line->read]
8b/-> *(esi+4) 1/r32/ecx
8d/copy-address *(esi+ecx+0xc) 0/r32/eax
89/<- *(edi+4) 0/r32/eax
eb/jump $next-word-string-or-expression-without-metadata:end/disp8
$next-word-string-or-expression-without-metadata:return-eol:
# return out = {0, 0}
c7 0/subop/copy *edi 0/imm32
c7 0/subop/copy *(edi+4) 0/imm32
$next-word-string-or-expression-without-metadata:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
$next-word-string-or-expression-without-metadata:error1:
# print(stderr, "error: no space allowed after '*' in '" line "'")
# . write-buffered(Stderr, "error: no space allowed after '*' in '")
# . . push args
68/push "error: no space allowed after '*' in '"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-stream-data(Stderr, line)
# . . push args
56/push-esi
68/push Stderr/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-buffered(Stderr, "'")
# . . push args
68/push "'"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$next-word-string-or-expression-without-metadata:error2:
# print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
# . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
# . . push args
68/push "error: *(...) expression must be all on a single line in '"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-stream-data(Stderr, line)
# . . push args
56/push-esi
68/push Stderr/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-buffered(Stderr, "'")
# . . push args
68/push "'"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$next-word-string-or-expression-without-metadata:error3:
# print(stderr, "error: no metadata after calls; just use a comment (in '" line "')")
# . write-buffered(Stderr, "error: no metadata after calls; just use a comment (in '")
# . . push args
68/push "error: no metadata after calls; just use a comment (in '"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-stream-data(Stderr, line)
# . . push args
56/push-esi
68/push Stderr/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-buffered(Stderr, "')")
# . . push args
68/push "')"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$next-word-string-or-expression-without-metadata:error4:
# print(stderr, "error: unexpected text after end of call in '" line "'")
# . write-buffered(Stderr, "error: unexpected text after end of call in '")
# . . push args
68/push "error: unexpected text after end of call in '"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-stream-data(Stderr, line)
# . . push args
56/push-esi
68/push Stderr/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-buffered(Stderr, "'")
# . . push args
68/push "'"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
$next-word-string-or-expression-without-metadata:error5:
# print(stderr, "error: no metadata anywhere in calls (in '" line "')")
# . write-buffered(Stderr, "error: no metadata anywhere in calls (in '")
# . . push args
68/push "error: no metadata anywhere in calls (in '"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-stream-data(Stderr, line)
# . . push args
56/push-esi
68/push Stderr/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . write-buffered(Stderr, "')")
# . . push args
68/push "')"/imm32
68/push Stderr/imm32
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# never gets here
test-next-word-string-or-expression-without-metadata:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " ab")
# . . push args
68/push " ab"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(_test-input-stream->read, 4, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata/updates-stream-read-correctly"/imm32
68/push 4/imm32
b8/copy-to-eax _test-input-stream/imm32
ff 6/subop/push *(eax+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-input-stream, 14, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
# . check-ints-equal(slice->end - _test-input-stream, 16, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata: end"/imm32
68/push 0x10/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-whole-comment:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " # a")
# . . push args
68/push " # a"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(_test-input-stream->read, 5, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment/updates-stream-read-correctly"/imm32
68/push 5/imm32
b8/copy-to-eax _test-input-stream/imm32
ff 6/subop/push *(eax+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-input-stream, 14, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
# . check-ints-equal(slice->end - _test-input-stream, 17, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-comment: end"/imm32
68/push 0x11/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-empty-slice-on-eof:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write nothing to _test-input-stream
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->end - slice->start, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-empty-expression-on-eof"/imm32
68/push 0/imm32
# . . push slice->end - slice->start
8b/-> *(ecx+4) 0/r32/eax
2b/subtract-> *ecx 0/r32/eax # subtract *ecx from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-string-literal:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " \"a b\" ")
# . . push args
68/push " \"a b\" "/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 6, msg)
# . check-ints-equal(slice->end - _test-input-stream, 18, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-literal: end"/imm32
68/push 0x12/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-string-with-escapes:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " \"a\\\"b\"")
# . . push args
68/push " \"a\\\"b\""/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
# . check-ints-equal(slice->end - _test-input-stream, 19, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-string-with-escapes: end"/imm32
68/push 0x13/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-whole-expression:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " *(a b) ")
# . . push args
68/push " *(a b) "/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 7, msg)
# . check-ints-equal(slice->end - _test-input-stream, 19, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-whole-expression: end"/imm32
68/push 0x13/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " ) ")
# . . push args
68/push " ) "/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: start"/imm32
68/push 0/imm32
ff 6/subop/push *ecx
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-close-paren: end"/imm32
68/push 0/imm32
ff 6/subop/push *(ecx+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " ) # abc ")
# . . push args
68/push " ) # abc "/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: start"/imm32
68/push 0/imm32
ff 6/subop/push *ecx
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-handles-comment-after-trailing-close-paren: end"/imm32
68/push 0/imm32
ff 6/subop/push *(ecx+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " )\n")
# . . push args
68/push " )\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: start"/imm32
68/push 0/imm32
ff 6/subop/push *ecx
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end, 0, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-handles-newline-after-trailing-close-paren: end"/imm32
68/push 0/imm32
ff 6/subop/push *(ecx+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-next-word-string-or-expression-without-metadata-stops-at-close-paren:
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add %esp 4/imm32
# var slice/ecx : slice
68/push 0/imm32/end
68/push 0/imm32/start
89/<- %ecx 4/r32/esp
# write(_test-input-stream, " abc) # def")
# . . push args
68/push " abc) # def"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# next-word-string-or-expression-without-metadata(_test-input-stream, slice)
# . . push args
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call next-word-string-or-expression-without-metadata/disp32
# . . discard args
81 0/subop/add %esp 8/imm32
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/-> *ecx 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
# . check-ints-equal(slice->end - _test-input-stream, 16, msg)
# . . push args
68/push "F - test-next-word-string-or-expression-without-metadata-stops-at-close-paren: end"/imm32
68/push 0x10/imm32
# . . push slice->end - _test-input-stream
8b/-> *(ecx+4) 0/r32/eax
81 5/subop/subtract %eax _test-input-stream/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add %esp 0xc/imm32
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return