diff options
Diffstat (limited to 'subx')
-rw-r--r-- | subx/056trace.subx | 2 | ||||
-rw-r--r-- | subx/058stream-equal.subx | 1 | ||||
-rw-r--r-- | subx/064write-byte.subx | 2 | ||||
-rw-r--r-- | subx/069allocate.subx | 2 | ||||
-rw-r--r-- | subx/070new-stream.subx | 2 | ||||
-rw-r--r-- | subx/072slice.subx | 4 | ||||
-rw-r--r-- | subx/074print-int-decimal.subx | 2 | ||||
-rw-r--r-- | subx/Readme.md | 36 | ||||
-rwxr-xr-x | subx/apps/assort | bin | 28457 -> 28463 bytes | |||
-rwxr-xr-x | subx/apps/crenshaw2-1 | bin | 23301 -> 23307 bytes | |||
-rwxr-xr-x | subx/apps/crenshaw2-1b | bin | 23860 -> 23866 bytes | |||
-rwxr-xr-x | subx/apps/dquotes | bin | 25223 -> 34772 bytes | |||
-rw-r--r-- | subx/apps/dquotes.subx | 1095 | ||||
-rwxr-xr-x | subx/apps/factorial | bin | 22217 -> 22223 bytes | |||
-rw-r--r-- | subx/apps/factorial.subx | 2 | ||||
-rwxr-xr-x | subx/apps/handle | bin | 23023 -> 23082 bytes | |||
-rw-r--r-- | subx/apps/handle.subx | 52 | ||||
-rwxr-xr-x | subx/apps/hex | bin | 26310 -> 26316 bytes | |||
-rwxr-xr-x | subx/apps/pack | bin | 43371 -> 43377 bytes | |||
-rw-r--r-- | subx/stats.md | 13 | ||||
-rwxr-xr-x | subx/test_apps | 18 |
21 files changed, 966 insertions, 265 deletions
diff --git a/subx/056trace.subx b/subx/056trace.subx index 71d72796..417c24ce 100644 --- a/subx/056trace.subx +++ b/subx/056trace.subx @@ -962,7 +962,7 @@ $_append-4:end: $_append-4:abort: # . _write(2/stderr, error) # . . push args - 68/push "stream overflow"/imm32 + 68/push "stream overflow\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 diff --git a/subx/058stream-equal.subx b/subx/058stream-equal.subx index c65112cc..68296212 100644 --- a/subx/058stream-equal.subx +++ b/subx/058stream-equal.subx @@ -26,6 +26,7 @@ stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boole # EDI = s 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI # if (f->write != s->length) return false +$stream-data-equal?:compare-lengths: 39/compare 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # compare *EDI and EAX 75/jump-if-not-equal $stream-data-equal?:false/disp8 # currs/EDI = s->data diff --git a/subx/064write-byte.subx b/subx/064write-byte.subx index 606cc7d3..057b9164 100644 --- a/subx/064write-byte.subx +++ b/subx/064write-byte.subx @@ -243,7 +243,7 @@ $append-byte:end: $append-byte:abort: # . _write(2/stderr, error) # . . push args - 68/push "append-byte: out of space"/imm32 + 68/push "append-byte: out of space\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 diff --git a/subx/069allocate.subx b/subx/069allocate.subx index ac491ac3..e262b4d5 100644 --- a/subx/069allocate.subx +++ b/subx/069allocate.subx @@ -177,7 +177,7 @@ allocate-region: # ad : (address allocation-descriptor), n : int -> new-ad : (a $allocate-region:abort: # . _write(2/stderr, error) # . . push args - 68/push "allocate-region: failed to allocate"/imm32 + 68/push "allocate-region: failed to allocate\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 diff --git a/subx/070new-stream.subx b/subx/070new-stream.subx index 8a833581..b6934b1e 100644 --- a/subx/070new-stream.subx +++ b/subx/070new-stream.subx @@ -52,7 +52,7 @@ $new-stream:end: $new-stream:abort: # . _write(2/stderr, error) # . . push args - 68/push "new-stream: size too large"/imm32 + 68/push "new-stream: size too large\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 diff --git a/subx/072slice.subx b/subx/072slice.subx index 3aff095d..c9a7fe4a 100644 --- a/subx/072slice.subx +++ b/subx/072slice.subx @@ -1007,7 +1007,7 @@ $slice-to-string:end: $slice-to-string:abort: # . _write(2/stderr, error) # . . push args - 68/push "slice-to-string: out of space"/imm32 + 68/push "slice-to-string: out of space\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 @@ -1107,4 +1107,4 @@ _test-slice-data-3: 64/d _test-slice-data-4: -# . _. vim:nowrap:textwidth=0 +# . . vim:nowrap:textwidth=0 diff --git a/subx/074print-int-decimal.subx b/subx/074print-int-decimal.subx index 57daad01..f6ea490f 100644 --- a/subx/074print-int-decimal.subx +++ b/subx/074print-int-decimal.subx @@ -116,7 +116,7 @@ $print-int32-decimal:end: $print-int32-decimal:abort: # . _write(2/stderr, error) # . . push args - 68/push "print-int32-decimal: out of space"/imm32 + 68/push "print-int32-decimal: out of space\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 diff --git a/subx/Readme.md b/subx/Readme.md index cab1a503..cb00eae0 100644 --- a/subx/Readme.md +++ b/subx/Readme.md @@ -44,8 +44,10 @@ Emulated runs generate a trace that permits [time-travel debugging](https://gith $ ./subx --debug translate examples/factorial.subx -o examples/factorial saving address->label information to 'labels' saving address->source information to 'source_lines' + $ ./subx --debug --trace run examples/factorial saving trace to 'last_run' + $ ../browse_trace/browse_trace last_run # text-mode debugger UI ``` @@ -101,10 +103,11 @@ a few registers: * Six general-purpose 32-bit registers: EAX, EBX, ECX, EDX, ESI and EDI * Two additional 32-bit registers: ESP and EBP (I suggest you only use these to manage the call stack.) -* Three 1-bit _flag_ registers for conditional branching: +* Four 1-bit _flag_ registers for conditional branching: - zero/equal flag ZF - sign flag SF - overflow flag OF + - carry flag CF SubX programs consist of instructions like `89/copy`, `01/add`, `3d/compare` and `52/push-ECX` which modify these registers as well as a byte-addressable @@ -152,19 +155,19 @@ _addressing mode_. This is a 2-bit argument that can take 4 possible values, and it determines what other arguments are required, and how to interpret them. -* If `/mod` is `3`: the operand is the register described by the 3-bit `/rm32` - argument similarly to `/r32` above. +* If `/mod` is `3`: the operand is in the register described by the 3-bit + `/rm32` argument similarly to `/r32` above. -* If `/mod` is `0`: the operand is the address provided in the register +* If `/mod` is `0`: the operand is in the address provided in the register described by `/rm32`. That's `*rm32` in C syntax. -* If `/mod` is `1`: the operand is the address provided by adding the register - in `/rm32` with the (1-byte) displacement. That's `*(rm32 + disp8)` in C - syntax. +* If `/mod` is `1`: the operand is in the address provided by adding the + register in `/rm32` with the (1-byte) displacement. That's `*(rm32 + disp8)` + in C syntax. -* If `/mod` is `2`: the operand is the address provided by adding the register - in `/rm32` with the (4-byte) displacement. That's `*(/rm32 + disp32)` in C - syntax. +* If `/mod` is `2`: the operand is in the address provided by adding the + register in `/rm32` with the (4-byte) displacement. That's `*(/rm32 + + disp32)` in C syntax. In the last three cases, one exception occurs when the `/rm32` argument contains `4`. Rather than encoding register `ESP`, it means the address is @@ -215,9 +218,10 @@ This program sums the first 10 natural numbers. By convention I use horizontal tabstops to help read instructions, dots to help follow the long lines, comments before groups of instructions to describe their high-level purpose, and comments at the end of complex instructions to state the low-level -operation they perform. Numbers are always in hexadecimal (base 16); the '0x' -prefix is optional, and I tend to include it as a reminder when numbers look -like decimal numbers or words. +operation they perform. Numbers are always in hexadecimal (base 16) and must +start with a digit ('0'..'9'); use the '0x' prefix when a number starts with a +letter ('a'..'f'). I tend to also include it as a reminder when numbers look +like decimal numbers. Try running this example now: @@ -337,9 +341,9 @@ runnable on a Linux system running on Intel x86 processors, either 32- or 1. [Converting ascii hex bytes to binary.](http://akkartik.github.io/mu/html/subx/apps/hex.subx.html) (✓) 2. [Packing bitfields for x86 instructions into bytes.](http://akkartik.github.io/mu/html/subx/apps/pack.subx.html) (✓) - 3. [Combining segments with the same name.](apps/assort.subx) (✓) - 4. Support for string literals. (10% complete) - 5. Replacing addresses with labels. + 3. [Combining segments with the same name.](http://akkartik.github.io/mu/html/subx/apps/assort.subx.html) (✓) + 4. [Support for string literals.](http://akkartik.github.io/mu/html/subx/apps/dquotes.subx.html) (✓) + 5. [Replacing addresses with labels.](https://github.com/akkartik/mu/pull/34) (10% complete) * Testable, dependency-injected vocabulary of primitives - Streams: `read()`, `write()`. (✓) diff --git a/subx/apps/assort b/subx/apps/assort index 37585b8c..9d27fd9a 100755 --- a/subx/apps/assort +++ b/subx/apps/assort Binary files differdiff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 index 7caeb9af..159ed81a 100755 --- a/subx/apps/crenshaw2-1 +++ b/subx/apps/crenshaw2-1 Binary files differdiff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b index c065c1f5..f4e02da6 100755 --- a/subx/apps/crenshaw2-1b +++ b/subx/apps/crenshaw2-1b Binary files differdiff --git a/subx/apps/dquotes b/subx/apps/dquotes index 4f7a6bec..6774b8df 100755 --- a/subx/apps/dquotes +++ b/subx/apps/dquotes Binary files differdiff --git a/subx/apps/dquotes.subx b/subx/apps/dquotes.subx index b9c06c16..64742599 100644 --- a/subx/apps/dquotes.subx +++ b/subx/apps/dquotes.subx @@ -90,7 +90,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void # read-line-buffered(in, line) # if (line->write == 0) break # end of file # while true - # var word-slice = next-word(line) + # var word-slice = next-word-or-string(line) # if slice-empty?(word-slice) # end of line # break # if slice-starts-with?(word-slice, "#") # comment @@ -165,12 +165,12 @@ $convert:check0: 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX 0f 84/jump-if-equal $convert:break/disp32 $convert:word-loop: - # next-word(line, word-slice) + # next-word-or-string(line, word-slice) # . . push args 52/push-EDX 51/push-ECX # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP $convert:check1: @@ -731,39 +731,39 @@ test-convert-processes-string-literals: # called. We just want to make sure instructions using string literals # switch to a string variable with the right value. # (Modifying string literals completely off the radar for now.) - # dump output {{{ - # . write(2/stderr, "result: ^") - # . . push args - 68/push "result: ^"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # }}} +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +#? # }}} # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) # . . push args 68/push "F - test-convert-processes-string-literals/0"/imm32 @@ -791,10 +791,10 @@ test-convert-processes-string-literals: e8/call check-next-stream-line-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data ", msg) + # . check-next-stream-line-equal(_test-output-stream, "== data", msg) # . . push args 68/push "F - test-convert-processes-string-literals/3"/imm32 - 68/push "== data "/imm32 + 68/push "== data"/imm32 68/push _test-output-stream/imm32 # . . call e8/call check-next-stream-line-equal/disp32 @@ -844,15 +844,15 @@ test-convert-processes-string-literals: # generate the data segment contents byte by byte for a given slice emit-string-literal-data: # out : (address stream), word : (address slice) # pseudocode - # var len = word->end - word->start - 2 # ignore the double-quotes - # append-int32-hex(out, len) - # write(out, "/imm32") + # len = string-length-at-start-of-slice(word->start, word->end) + # print(out, "#{len}/imm32 ") # curr = word->start # ++curr # skip '"' # while true # if (curr >= word->end) break # c = *curr # if (c == '"') break + # if (c == '\') ++curr, c = *curr # append-byte-hex(out, c) # if c is alphanumeric: # write(out, "/") @@ -872,16 +872,21 @@ emit-string-literal-data: # out : (address stream), word : (address slice) 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI # curr/EDX = word->start 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX + # max/ESI = word->end + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI $emit-string-literal-data:emit-length: - # TODO: handle metadata here + # len/EAX = string-length-at-start-of-slice(word->start, word->end) + # . . push args + 56/push-ESI + 52/push-EDX + # . . call + e8/call string-length-at-start-of-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # print(out, "#{len}/imm32 ") - # . len/ECX = word->end - word->start - 2 - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # subtract EDX from ECX - 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 2/imm32 # subtract from ECX # . print-int32(out, len) # . . push args - 51/push-ECX + 50/push-EAX ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) # . . call e8/call print-int32/disp32 @@ -898,9 +903,7 @@ $emit-string-literal-data:emit-length: $emit-string-literal-data:loop-init: # ++curr # skip initial '"' 42/increment-EDX - # max/ESI = word->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - # ECX = 0 + # c/ECX = 0 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX $emit-string-literal-data:loop: # if (curr >= max) break @@ -911,6 +914,17 @@ $emit-string-literal-data:loop: # if (ECX == '"') break 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x22/imm32/dquote # compare ECX 74/jump-if-equal $emit-string-literal-data:end/disp8 + # if (ECX == '\') ++curr, ECX = *curr + 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x5c/imm32/backslash # compare ECX + 75/jump-if-not-equal $emit-string-literal-data:emit/disp8 + # . ++curr + 42/increment-EDX + # . if (curr >= max) break + 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI + 7d/jump-if-greater-or-equal $emit-string-literal-data:end/disp8 + # . CL = *curr + 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL +$emit-string-literal-data:emit: # append-byte-hex(out, CL) # . . push args 51/push-ECX @@ -1017,8 +1031,8 @@ test-emit-string-literal-data: e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '"abc"' - 68/push _test-slice-abc-end/imm32 + # var slice/ECX = '"abc"/d' + 68/push _test-slice-abc-metadata-end/imm32 68/push _test-slice-abc/imm32 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX # emit-string-literal-data(_test-output-stream, slice) @@ -1251,7 +1265,7 @@ test-emit-string-literal-data-handles-escape-sequences: # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg) # . . push args 68/push "F - test-emit-string-literal-data-handles-escape-sequences"/imm32 - 68/push "3/imm32 61/a 22 62/b "/imm32 + 68/push "0x00000003/imm32 61/a 22 62/b "/imm32 68/push _test-output-stream/imm32 # . . call e8/call check-stream-equal/disp32 @@ -1264,6 +1278,21 @@ test-emit-string-literal-data-handles-escape-sequences: # emit everything from a word except the initial datum emit-metadata: # out : (address buffered-file), word : (address slice) + # pseudocode + # var slice = {0, word->end} + # curr = word->start + # if *curr == '"' + # curr = skip-string-in-slice(curr, word->end) + # else + # while true + # if curr == word->end + # return + # if *curr == '/' + # break + # ++curr + # slice->curr = curr + # write-slice-buffered(out, slice) + # # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1271,59 +1300,66 @@ emit-metadata: # out : (address buffered-file), word : (address slice) 50/push-EAX 51/push-ECX 52/push-EDX - - # PSEUDOCODE - # ECX = (char *) word->start - # while true: - # if ECX == word->end: return - # if *(ECX++) == '/': break - # write-slice-buffered(out, {ECX, word->end}) - - # ECX = word - 8b/copy/word 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - # EDX = word->end - 8b/copy/word->end 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # ECX = word->start - 8b/copy/word->start 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX - - # clear out EAX - b8/copy-to-EAX 0/imm32 - # while *start != '/': -$skip-datum-loop: - # . start == end? - 39/compare-ECX-and 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # EDX == ECX - # . if so, return from function (it's only datum, or empty) - 74/jump-if-equal $emit-metadata:end/disp8 - - # . start++ - 41/increment-ECX # ECX++ - - # . EAX = *start - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - - # . EAX != '/'? - 3d/compare-EAX-and 0x2f/imm32 - # . if so, continue looping - 75/jump-if-not-equal $skip-datum-loop/disp8 - # end - - # write-slice-buffered(out, &{start, end}) - # . push end + 53/push-EBX + 56/push-ESI + # ESI = word + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + # curr/ECX = word->start + 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX + # end/EDX = word->end + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX + # var slice/EBX = {0, end} + 52/push-EDX + 68/push 0/imm32 + 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX + # EAX = 0 + b8/copy-to-EAX 0/imm32 +$emit-metadata:check-for-string-literal: + # - if (*curr == '"') curr = skip-string-in-slice(curr, end) + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + 3d/compare-EAX-and 0x22/imm32/dquote + 75/jump-if-not-equal $emit-metadata:skip-datum-loop/disp8 +$emit-metadata:skip-string-literal: + # . EAX = skip-string-in-slice(curr, end) + # . . push args 52/push-EDX - # . push start 51/push-ECX - # . push &{start, end} - 54/push-ESP - - # . push out + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # . curr = EAX + 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX + eb/jump $emit-metadata:emit/disp8 +$emit-metadata:skip-datum-loop: + # - otherwise scan for '/' + # if (curr == end) return + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX + 74/jump-if-equal $emit-metadata:end/disp8 + # if (*curr == '/') break + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + 3d/compare-EAX-and 0x2f/imm32/slash + 74/jump-if-equal $emit-metadata:emit/disp8 + # ++curr + 41/increment-ECX + eb/jump $emit-metadata:skip-datum-loop/disp8 +$emit-metadata:emit: + # slice->curr = ECX + 89/copy 0/mod/indirect 3/rm32/EBX . . . 1/r32/ECX . . # copy ECX to *EBX + # write-slice-buffered(out, slice) + # . . push args + 53/push-EBX ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - + # . . call e8/call write-slice-buffered/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 0x10/imm32 . # add 16 to ESP - + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP $emit-metadata:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP # . restore registers + 5e/pop-to-ESI + 5b/pop-to-EBX 5a/pop-to-EDX 59/pop-to-ECX 58/pop-to-EAX @@ -1494,9 +1530,150 @@ test-emit-metadata-multiple: 5d/pop-to-EBP c3/return +test-emit-metadata-when-no-datum: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup + # . 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-output-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var slice/ECX = "/abc" + b8/copy-to-EAX "/abc"/imm32 + # . push end/ECX + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 51/push-ECX + # . push curr/EAX + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . save stack pointer + 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + # emit-metadata(_test-output-buffered-file, slice) + # . . push args + 51/push-ECX + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-metadata/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-stream-equal(_test-output-stream, "/abc", msg) # nothing skipped + # . . push args + 68/push "F - test-emit-metadata-when-no-datum"/imm32 + 68/push "/abc"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-emit-metadata-in-string-literal: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup + # . 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-output-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var slice/ECX = "\"abc/def\"/ghi" + 68/push _test-slice-literal-string-with-metadata-end/imm32 + 68/push _test-slice-literal-string/imm32/start + 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + # emit-metadata(_test-output-buffered-file, slice) + # . . push args + 51/push-ECX + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-metadata/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . 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 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # }}} + # check-stream-equal(_test-output-stream, "/ghi", msg) # important that there's no leading space + # . . push args + 68/push "F - test-emit-metadata-in-string-literal"/imm32 + 68/push "/ghi"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + # (re)compute the bounds of the next word in the line # return empty string on reaching end of file -next-word: # line : (address stream byte), out : (address slice) +next-word-or-string: # line : (address stream byte), out : (address slice) # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1517,18 +1694,18 @@ next-word: # line : (address stream byte), out : (address slice) e8/call skip-chars-matching/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$next-word:check0: +$next-word-or-string:check0: # if (line->read >= line->write) clear out and return # . EAX = line->read 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX # . if (EAX < line->write) goto next check 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI - 7c/jump-if-lesser $next-word:check-for-comment/disp8 + 7c/jump-if-lesser $next-word-or-string:check-for-comment/disp8 # . return out = {0, 0} c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) - eb/jump $next-word:end/disp8 -$next-word:check-for-comment: + eb/jump $next-word-or-string:end/disp8 +$next-word-or-string:check-for-comment: # out->start = &line->data[line->read] 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX @@ -1539,8 +1716,8 @@ $next-word:check-for-comment: 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL # . compare 3d/compare-EAX-and 0x23/imm32/pound - 75/jump-if-not-equal $next-word:check-for-string-literal/disp8 -$next-word:comment: + 75/jump-if-not-equal $next-word-or-string:check-for-string-literal/disp8 +$next-word-or-string:comment: # out->end = &line->data[line->write] 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX @@ -1549,32 +1726,26 @@ $next-word:comment: 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) # return - eb/jump $next-word:end/disp8 -$next-word:check-for-string-literal: + eb/jump $next-word-or-string:end/disp8 +$next-word-or-string:check-for-string-literal: # if line->data[line->read] == '"' # . EAX = line->data[line->read] 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL # . compare 3d/compare-EAX-and 0x22/imm32/dquote - 75/jump-if-not-equal $next-word:regular-word/disp8 -$next-word:string-literal: - # ++line->read # skip '"' - # . persist line->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - # . ++line->read - ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # increment *(ESI+4) - # parse-string(line, out) + 75/jump-if-not-equal $next-word-or-string:regular-word/disp8 +$next-word-or-string:string-literal: + # skip-string(line) # . . push args - 57/push-EDI 56/push-ESI # . . call - e8/call parse-string/disp32 + e8/call skip-string/disp32 # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP # fall through -$next-word:regular-word: - # otherwise skip-chars-not-matching-whitespace(line) # including trailing newline +$next-word-or-string:regular-word: + # skip-chars-not-matching-whitespace(line) # including trailing newline # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) # . . call @@ -1585,7 +1756,7 @@ $next-word:regular-word: 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -$next-word:end: +$next-word-or-string:end: # . restore registers 5f/pop-to-EDI 5e/pop-to-ESI @@ -1596,81 +1767,7 @@ $next-word:end: 5d/pop-to-EBP c3/return -parse-string: # line : (address stream byte), out : (address slice) - # pseudocode: - # ESI = line - # curr/ECX = line->data[line->read] - # max/EDX = line->data[line->write] - # while curr >= max - # if (*curr == '"') ++curr, break - # if (*curr == '\\') curr+=2, continue - # ++curr - # line->read = curr - line->data - # out->end = curr - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/ECX = &table->data[table->read] - # . ECX = table->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # . ECX = table->data + ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy ESI+ECX+12 to ECX - # max/EDX = &table->data[table->write] - # . EDX = table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # . EDX = table->data + EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ESI+EDX+12 to EDX - # clear EAX - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -$parse-string:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $parse-string:break/disp8 - # c/EAX = *curr - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -$parse-string:check1: - # if (c == '"') break # rely on caller to skip trailing non-whitespace - 3d/compare-EAX-and 0x22/imm32/dquote - 74/jump-if-equal $parse-string:break/disp8 -$parse-string:check2: - # if (c == '\\') ++curr - 3d/compare-EAX-and 0x5c/imm32/backslash - 75/jump-if-not-equal $parse-string:continue/disp8 - # . ++curr - 41/increment-ECX -$parse-string:continue: - # ++curr - 41/increment-ECX - # loop - eb/jump $parse-string:loop/disp8 -$parse-string:break: - # out->end = curr - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) - # line->read = curr - line - 12 - 29/subtract 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # subtract ESI from ECX - 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 0xc/imm32 # subtract from ECX - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) -$parse-string:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word: +test-next-word-or-string: # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1694,17 +1791,17 @@ test-next-word: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-input-stream, slice) + # next-word-or-string(_test-input-stream, slice) # . . push args 51/push-ECX 68/push _test-input-stream/imm32 # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # check-ints-equal(_test-input-stream->read, 4, msg) # . . push args - 68/push "F - test-next-word/updates-stream-read-correctly"/imm32 + 68/push "F - test-next-word-or-string/updates-stream-read-correctly"/imm32 68/push 4/imm32 b8/copy-to-EAX _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) @@ -1715,7 +1812,7 @@ test-next-word: # 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: start"/imm32 + 68/push "F - test-next-word-or-string: start"/imm32 68/push 0xe/imm32 # . . push slice->start - _test-input-stream 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX @@ -1728,7 +1825,7 @@ test-next-word: # 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: end"/imm32 + 68/push "F - test-next-word-or-string: end"/imm32 68/push 0x10/imm32 # . . push slice->end - _test-input-stream 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -1743,7 +1840,7 @@ test-next-word: 5d/pop-to-EBP c3/return -test-next-word-returns-whole-comment: +test-next-word-or-string-returns-whole-comment: # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1767,17 +1864,17 @@ test-next-word-returns-whole-comment: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-input-stream, slice) + # next-word-or-string(_test-input-stream, slice) # . . push args 51/push-ECX 68/push _test-input-stream/imm32 # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # check-ints-equal(_test-input-stream->read, 5, msg) # . . push args - 68/push "F - test-next-word-returns-whole-comment/updates-stream-read-correctly"/imm32 + 68/push "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32 68/push 5/imm32 b8/copy-to-EAX _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) @@ -1788,7 +1885,7 @@ test-next-word-returns-whole-comment: # 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-returns-whole-comment: start"/imm32 + 68/push "F - test-next-word-or-string-returns-whole-comment: start"/imm32 68/push 0xe/imm32 # . . push slice->start - _test-input-stream 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX @@ -1801,7 +1898,7 @@ test-next-word-returns-whole-comment: # 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-returns-whole-comment: end"/imm32 + 68/push "F - test-next-word-or-string-returns-whole-comment: end"/imm32 68/push 0x11/imm32 # . . push slice->end - _test-input-stream 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -1816,7 +1913,7 @@ test-next-word-returns-whole-comment: 5d/pop-to-EBP c3/return -test-next-word-returns-empty-string-on-eof: +test-next-word-or-string-returns-empty-string-on-eof: # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1833,17 +1930,17 @@ test-next-word-returns-empty-string-on-eof: 68/push 0/imm32/start 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX # write nothing to _test-input-stream - # next-word(_test-input-stream, slice) + # next-word-or-string(_test-input-stream, slice) # . . push args 51/push-ECX 68/push _test-input-stream/imm32 # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # check-ints-equal(slice->end - slice->start, 0, msg) # . . push args - 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 + 68/push "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32 68/push 0/imm32 # . . push slice->end - slice->start 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -1858,7 +1955,7 @@ test-next-word-returns-empty-string-on-eof: 5d/pop-to-EBP c3/return -test-next-word-returns-whole-string: +test-next-word-or-string-returns-whole-string: # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1882,18 +1979,18 @@ test-next-word-returns-whole-string: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-input-stream, slice) + # next-word-or-string(_test-input-stream, slice) # . . push args 51/push-ECX 68/push _test-input-stream/imm32 # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # 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-returns-whole-string: start"/imm32 + 68/push "F - test-next-word-or-string-returns-whole-string: start"/imm32 68/push 0xd/imm32 # . . push slice->start - _test-input-stream 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX @@ -1906,7 +2003,7 @@ test-next-word-returns-whole-string: # check-ints-equal(slice->end - _test-input-stream->data, 12, msg) # . check-ints-equal(slice->end - _test-input-stream, 24, msg) # . . push args - 68/push "F - test-next-word-returns-whole-string: end"/imm32 + 68/push "F - test-next-word-or-string-returns-whole-string: end"/imm32 68/push 0x18/imm32 # . . push slice->end - _test-input-stream 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -1921,7 +2018,7 @@ test-next-word-returns-whole-string: 5d/pop-to-EBP c3/return -test-next-word-returns-string-with-escapes: +test-next-word-or-string-returns-string-with-escapes: # . prolog 55/push-EBP 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1945,18 +2042,18 @@ test-next-word-returns-string-with-escapes: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-input-stream, slice) + # next-word-or-string(_test-input-stream, slice) # . . push args 51/push-ECX 68/push _test-input-stream/imm32 # . . call - e8/call next-word/disp32 + e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # 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-returns-string-with-escapes: start"/imm32 + 68/push "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32 68/push 0xd/imm32 # . . push slice->start - _test-input-stream 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX @@ -1969,7 +2066,7 @@ test-next-word-returns-string-with-escapes: # check-ints-equal(slice->end - _test-input-stream->data, 9, msg) # . check-ints-equal(slice->end - _test-input-stream, 21, msg) # . . push args - 68/push "F - test-next-word-returns-string-with-escapes: end"/imm32 + 68/push "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32 68/push 0x15/imm32 # . . push slice->end - _test-input-stream 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -1984,6 +2081,546 @@ test-next-word-returns-string-with-escapes: 5d/pop-to-EBP c3/return +# update line->read to end of string literal surrounded by double quotes +# line->read must start out at a double-quote +skip-string: # line : (address stream) + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # . save registers + 50/push-EAX + 51/push-ECX + 52/push-EDX + # ECX = line + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) + # . . push &line->data[line->write] + 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX + 52/push-EDX + # . . push &line->data[line->read] + 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX + 52/push-EDX + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # line->read = EAX - line->data + 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX + 2d/subtract-from-EAX 0xc/imm32 + 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) +$skip-string:end: + # . restore registers + 5a/pop-to-EDX + 59/pop-to-ECX + 58/pop-to-EAX + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . write(_test-input-stream, "\"abc\" def") + # . indices: 0123 45 + # . . push args + 68/push "\"abc\" def"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-string"/imm32 + 68/push 5/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-ignores-spaces: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . write(_test-input-stream, "\"a b\"/yz") + # . indices: 0123 45 + # . . push args + 68/push "\"a b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-string-ignores-spaces"/imm32 + 68/push 5/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-ignores-escapes: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . write(_test-input-stream, "\"a\\\"b\"/yz") + # . indices: 01 2 34 56 + # . . push args + 68/push "\"a\\\"b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(line->read, 6, msg) + # . . push args + 68/push "F - test-skip-string-ignores-escapes"/imm32 + 68/push 6/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-works-from-mid-stream: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # 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 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . write(_test-input-stream, "0 \"a\\\"b\"/yz") + # . indices: 01 2 34 56 + # . . push args + 68/push "0 \"a\\\"b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # precondition: line->read == 2 + c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(line->read, 8, msg) + # . . push args + 68/push "F - test-skip-string-works-from-mid-stream"/imm32 + 68/push 8/imm32 + b8/copy-to-EAX _test-input-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # . save registers + 51/push-ECX + 52/push-EDX + 53/push-EBX + # ECX = curr + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + # EDX = end + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + # EAX = 0 + 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + # skip initial dquote + 41/increment-ECX +$skip-string-in-slice:loop: + # if (curr >= end) return curr + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 + # AL = *curr + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +$skip-string-in-slice:dquote: + # if (EAX == '"') break + 3d/compare-EAX-and 0x22/imm32/double-quote + 74/jump-if-equal $skip-string-in-slice:break/disp8 +$skip-string-in-slice:check-for-escape: + # if (EAX == '\') escape next char + 3d/compare-EAX-and 0x5c/imm32/backslash + 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 +$skip-string-in-slice:escape: + 41/increment-ECX +$skip-string-in-slice:continue: + # ++curr + 41/increment-ECX + eb/jump $skip-string-in-slice:loop/disp8 +$skip-string-in-slice:break: + # skip final dquote + 41/increment-ECX +$skip-string-in-slice:return-curr: + # return curr + 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX +$skip-string-in-slice:end: + # . restore registers + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-in-slice: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"abc\" def" + b8/copy-to-EAX "\"abc\" def"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = skip-string-in-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice"/imm32 + 68/push 4/imm32 + # . . push ECX-EAX + 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 51/push-ECX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-in-slice-ignores-spaces: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"a b\"/yz" + b8/copy-to-EAX "\"a b\"/yz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = skip-string-in-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 + 68/push 3/imm32 + # . . push ECX-EAX + 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 51/push-ECX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-in-slice-ignores-escapes: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"a\\\"b\"/yz" + b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = skip-string-in-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 + 68/push 3/imm32 + # . . push ECX-EAX + 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 51/push-ECX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-skip-string-in-slice-stops-at-end: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"abc" # unbalanced dquote + b8/copy-to-EAX "\"abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = skip-string-in-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice + # . . push args + 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 + 68/push 0/imm32 + # . . push ECX-EAX + 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 51/push-ECX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # . save registers + 51/push-ECX + 52/push-EDX + 53/push-EBX + # ECX = curr + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + # EDX = end + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + # length/EAX = 0 + 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + # EBX = 0 + 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX + # skip initial dquote + 41/increment-ECX +$string-length-at-start-of-slice:loop: + # if (curr >= end) return length + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 + # BL = *curr + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL +$string-length-at-start-of-slice:dquote: + # if (EBX == '"') break + 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX + 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 +$string-length-at-start-of-slice:check-for-escape: + # if (EBX == '\') escape next char + 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX + 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 +$string-length-at-start-of-slice:escape: + # increment curr but not result + 41/increment-ECX +$string-length-at-start-of-slice:continue: + # ++result + 40/increment-EAX + # ++curr + 41/increment-ECX + eb/jump $string-length-at-start-of-slice:loop/disp8 +$string-length-at-start-of-slice:end: + # . restore registers + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-string-length-at-start-of-slice: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"abc\" def" + b8/copy-to-EAX "\"abc\" def"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = string-length-at-start-of-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call string-length-at-start-of-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 3, msg) + # . . push args + 68/push "F - test-string-length-at-start-of-slice"/imm32 + 68/push 3/imm32 + 50/push-EAX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-string-length-at-start-of-slice-escaped: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup: (EAX..ECX) = "\"ab\\c\" def" + b8/copy-to-EAX "\"ab\\c\" def"/imm32 + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 05/add-to-EAX 4/imm32 + # EAX = string-length-at-start-of-slice(EAX, ECX) + # . . push args + 51/push-ECX + 50/push-EAX + # . . call + e8/call string-length-at-start-of-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 3, msg) + # . . push args + 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 + 68/push 3/imm32 + 50/push-EAX + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + == data Segment-size: @@ -2009,6 +2646,8 @@ Slash: _test-slice-abc: 22/dquote 61/a 62/b 63/c 22/dquote # "abc" _test-slice-abc-end: + 2f/slash 64/d +_test-slice-abc-metadata-end: _test-slice-empty-string-literal: 22/dquote 22/dquote # "" @@ -2031,4 +2670,14 @@ _test-slice-word-end: 2f/slash 67/g 68/h 69/i # /ghi _test-slice-word-end2: +# "abc/def"/ghi +_test-slice-literal-string: + 22/dquote + 61/a 62/b 63/c # abc + 2f/slash 64/d 65/e 66/f # /def + 22/dquote +_test-slice-literal-string-end: + 2f/slash 67/g 68/h 69/i # /ghi +_test-slice-literal-string-with-metadata-end: + # . . vim:nowrap:textwidth=0 diff --git a/subx/apps/factorial b/subx/apps/factorial index 16b54308..0f913091 100755 --- a/subx/apps/factorial +++ b/subx/apps/factorial Binary files differdiff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx index 98efc6fa..e4b7a057 100644 --- a/subx/apps/factorial.subx +++ b/subx/apps/factorial.subx @@ -50,8 +50,8 @@ Entry: # run tests if necessary, compute `factorial(5)` if not e8/call run-tests/disp32 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX eb/jump $main:end/disp8 # where EAX will get copied to EBX - # - otherwise return factorial(5) $run-main: + # - otherwise return factorial(5) # . . push args 68/push 5/imm32 # . . call diff --git a/subx/apps/handle b/subx/apps/handle index 154e3725..c7110e42 100755 --- a/subx/apps/handle +++ b/subx/apps/handle Binary files differdiff --git a/subx/apps/handle.subx b/subx/apps/handle.subx index 0ed12067..97a6c622 100644 --- a/subx/apps/handle.subx +++ b/subx/apps/handle.subx @@ -15,10 +15,11 @@ # To run (from the subx directory): # $ ./subx translate *.subx apps/handle.subx -o apps/handle # $ ./subx run apps/handle -# Expected result is a hard abort: -# ........lookup failed -# (This file is a prototype, so the tests in this file aren't real tests. Don't -# expect to run anything in the same process after they've completed.) +# Expected result is a successful lookup followed by a hard abort: +# lookup succeeded +# lookup failed +# (This file is a prototype. The 'tests' in it aren't real; failures are +# expected.) == code # instruction effective address register displacement immediate @@ -212,25 +213,44 @@ lookup: # h : (handle T) -> EAX : (address T) # EAX = handle 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX # - inline { + # push handle->alloc_id + ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX + # EAX = handle->address (payload) + 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # copy *(EAX+4) to EAX # push handle->address - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(EAX+4) - # EAX = handle->alloc_id + 50/push-EAX + # EAX = payload->alloc_id 8b/copy 0/mod/indirect 0/rm32/EAX . . . . . . # copy *EAX to EAX - # if (EAX != *ESP) abort - 39/compare 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # compare *ESP and EAX - 75/jump-if-not-equal $lookup:fail/disp8 - # return ESP+4 + # if (EAX != handle->alloc_id) abort + 39/compare 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # compare *(ESP+4) and EAX + 75/jump-if-not-equal $lookup:abort/disp8 + # EAX = pop handle->address 58/pop-to-EAX + # discard handle->alloc_id + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # add 4 05/add-to-EAX 4/imm32 # - } + # - alternative consuming a second register { +#? # ECX = handle->alloc_id +#? 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +#? # EAX = handle->address (payload) +#? 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to EAX +#? # if (ECX != *EAX) abort +#? 39/compare 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # compare *EAX and ECX +#? 75/jump-if-not-equal $lookup:abort/disp8 +#? # add 4 to EAX +#? 05/add-to-EAX 4/imm32 + # - } # . epilog 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 5d/pop-to-EBP c3/return -$lookup:fail: + +$lookup:abort: # . _write(2/stderr, msg) # . . push args - 68/push "lookup failed"/imm32 + 68/push "lookup failed\n"/imm32 68/push 2/imm32/stderr # . . call e8/call _write/disp32 @@ -294,6 +314,14 @@ test-lookup-success: # clean up # . *Next-alloc-id = 1 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id + # write(2/stderr, "lookup succeeded\n") + # . . push args + 68/push "lookup succeeded\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # . restore registers 5a/pop-to-EDX 59/pop-to-ECX diff --git a/subx/apps/hex b/subx/apps/hex index b23b8b86..5d663c6d 100755 --- a/subx/apps/hex +++ b/subx/apps/hex Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack index dfc71dc4..ad55fb75 100755 --- a/subx/apps/pack +++ b/subx/apps/pack Binary files differdiff --git a/subx/stats.md b/subx/stats.md index 847c4b9a..a53d4963 100644 --- a/subx/stats.md +++ b/subx/stats.md @@ -1,16 +1,21 @@ Initial -tests/whitespace/comments ## Lines in source +standard library 7712 1814 apps/crenshaw2-1b.subx 798 176 apps/crenshaw2-1.subx 601 180 apps/factorial.subx 107 28 apps/handle.subx 361 58 -apps/hex.subx 1535 133 -apps/pack.subx 1667 241 +apps/hex.subx 1511 144 +apps/pack.subx 7348 1054 +apps/assort.subx 1318 284 +apps/dquotes.subx 2694 497 ## Bytes in executable crenshaw2-1 17612 4112 crenshaw2-1b 18171 4140 factorial 16530 3488 handle 17323 3582 -hex 20591 3866 -pack 20762 4054 +hex 22684 4909 +pack 37316 7825 +assort 22506 5342 +dquotes 27186 5849 diff --git a/subx/test_apps b/subx/test_apps index 3001940f..a6e78ee3 100755 --- a/subx/test_apps +++ b/subx/test_apps @@ -131,9 +131,13 @@ test `uname` = 'Linux' && examples/ex12 echo handle ./subx translate 0*.subx apps/handle.subx -o apps/handle [ "$1" != record ] && git diff --exit-code apps/handle -./subx run apps/handle 2>&1 |grep -q 'lookup failed' +./subx run apps/handle > handle.out 2>&1 || true +grep -q 'lookup succeeded' handle.out || { echo "missing success test"; exit 1; } +grep -q 'lookup failed' handle.out || { echo "missing failure test"; exit 1; } test `uname` = 'Linux' && { - apps/handle test 2>&1 |grep -q 'lookup failed' + apps/handle > handle.out 2>&1 || true + grep -q 'lookup succeeded' handle.out || { echo "missing success test"; exit 1; } + grep -q 'lookup failed' handle.out || { echo "missing failure test"; exit 1; } } echo factorial @@ -200,4 +204,14 @@ test `uname` = 'Linux' && { echo } +echo dquotes +./subx translate 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes +[ "$1" != record ] && git diff --exit-code apps/dquotes +./subx run apps/dquotes test +echo +test `uname` = 'Linux' && { + apps/dquotes test + echo +} + exit 0 |