From 05e59d02e76a1eed55f04f63257a7bb65326bcb0 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Mon, 7 Sep 2026 23:53:55 -0700 Subject: add indentation and blank lines as magic-comment features the output is really starting to look like something! Force-Push: yes Change-Id: Ic41c8b1702d884ff89693a86d457af7cc752aa86 --- elf.e | 3 +++ hex.e | 39 ++++++++++++++++++++++++++++++++++----- transform.e | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/elf.e b/elf.e index a832c61..a149a0f 100644 --- a/elf.e +++ b/elf.e @@ -23,6 +23,7 @@ ~ The only interesting thing here is the entry pointer. : elf-file-header + ~ : blank-line ~ : ELF file header ~ * denotes mandatory fields according to breadbox current-offset 3unroll @@ -87,6 +88,7 @@ ~ ~~ : elf-program-header + ~ : blank-line ~ : ELF program header ~ * denotes mandatory fields according to breadbox current-offset L!' elf-program-header @@ -114,6 +116,7 @@ ~ may be useful for other programs, such as hex.e. Its only difference from ~ the base version is that it adds the write permission. : elf-program-header-writable + ~ : blank-line ~ : ELF program header ~ * denotes mandatory fields according to breadbox current-offset L!' elf-program-header diff --git a/hex.e b/hex.e index ead2dc4..c20d61a 100644 --- a/hex.e +++ b/hex.e @@ -17,7 +17,9 @@ ~ (buffer start, output point -- buffer start, output point) : output-start-routine + ~ : blank-line ~ : This is the start routine, the first thing that runs when the ELF loads. + ~ : indent current-offset L!' cold-start ~ The basic registers preserved across syscalls are rbx, rsp, rbp. ~ To avoid redundant moves, we store the buffer pointer in rbx just once, @@ -101,7 +103,9 @@ L@' input-loop-start :cc-equal jmp-cc-rel-imm8-from-here ~ We're still in the comment, keep handling it. - L@' skip-comment jmp-rel-imm8-from-here ; + L@' skip-comment jmp-rel-imm8-from-here + ~ : deindent + ; ~ This routine has no expectations; it reads a byte into L' buffer, keeps @@ -111,7 +115,9 @@ ~ (output memory start, current output point ~ -- output memory start, current output point) : output-read-byte + ~ : blank-line ~ : This is the routine named "read-byte". + ~ : indent current-offset L!' read-byte ~ We use self-xor as a concise way to set registers to zero. :rax :rax xor-reg64-reg64 ~ syscall number for sys-read @@ -120,7 +126,9 @@ ~ We read one byte at a time, because it makes the loop structure simple. 1 :rdx mov-reg64-imm32 ~ buffer length syscall - ret ; + ret + ~ : deindent + ; ~ This routine expects :rax to hold an ASCII byte, which must be a valid @@ -131,7 +139,9 @@ ~ (output memory start, current output point ~ -- output memory start, current output point) : output-decode-nibble + ~ : blank-line ~ : This is the routine named "decode-nibble". + ~ : indent current-offset L!' decode-nibble 0x30 :rax sub-reg64-imm8 ~ ASCII zero @@ -161,17 +171,23 @@ L@' invalid-byte jmp-rel-imm8-from-here current-offset L!' got-nibble - ret ; + ret + ~ : deindent + ; ~ (output memory start, current output point ~ -- output memory start, current output point) : output-exit + ~ : blank-line ~ : This is the routine named "exit". + ~ : indent current-offset L!' exit 60 :rax mov-reg64-imm32 ~ syscall number for sys-exit 0 :rdi mov-reg64-imm32 ~ exit code - syscall ; + syscall + ~ : deindent + ; ~ Printing an error message makes sure we don't produce a valid-looking @@ -181,7 +197,9 @@ ~ (output memory start, current output point ~ -- output memory start, current output point) : output-error-handlers + ~ : blank-line ~ : These are the error handler routines. + ~ : indent current-offset L!' invalid-byte L@' origin L@' invalid-byte-message + :rsi mov-reg64-imm64 L@' invalid-byte-message-size :rdx mov-reg64-imm64 @@ -205,13 +223,16 @@ 60 :rax mov-reg64-imm32 ~ syscall number for sys-exit 1 :rdi mov-reg64-imm32 ~ exit code syscall + ~ : deindent ; ~ (output memory start, current output point ~ -- output memory start, current output point) : output-messages + ~ : blank-line ~ : These are the message strings. + ~ : indent current-offset dup L!' invalid-byte-message 3unroll s" Invalid byte." packstring current-offset 4 roll - L!' invalid-byte-message-size @@ -222,7 +243,10 @@ current-offset dup L!' unexpected-eof-message 3unroll s" Unexpected EOF." packstring - current-offset 4 roll - L!' unexpected-eof-message-size ; + current-offset 4 roll - L!' unexpected-eof-message-size + + ~ : deindent + ; ~ (output memory start, current output point ~ -- output memory start, current output point) @@ -239,9 +263,14 @@ output-decode-nibble output-error-handlers output-messages + + ~ : blank-line ~ : This is a buffer that's zero in the ELF, but is written to at runtime ~ : and used as a variable. + ~ : indent current-offset L!' buffer 0 pack64 + ~ : deindent + current-offset L!' total-size ; ' all-contents entry-to-execution-token label-loop diff --git a/transform.e b/transform.e index ce51255..e24e4b7 100644 --- a/transform.e +++ b/transform.e @@ -2721,7 +2721,8 @@ allocate-transformation-state s" transformation-state" variable : hex-output-metadata-label-loop-buffer-length 8 + ; : hex-output-metadata-latest-output-point 2 8 * + ; : hex-output-metadata-is-fresh-line 3 8 * + ; -: hex-output-metadata-first-entry 4 8 * + ; +: hex-output-metadata-indentation-depth 4 8 * + ; +: hex-output-metadata-first-entry 5 8 * + ; : hex-output-metadata-entry-data-start ; : hex-output-metadata-entry-data-length 8 + ; : hex-output-metadata-entry-type 2 8 * + ; @@ -2729,8 +2730,9 @@ allocate-transformation-state s" transformation-state" variable : hex-output-metadata-next-entry 4 8 * + ; : hex-output-metadata-entry-type-line-comment 0 ; : hex-output-metadata-entry-type-line-break 1 ; -: hex-output-metadata-entry-type-string-literal 2 ; -: hex-output-metadata-entry-type-alignment 3 ; +: hex-output-metadata-entry-type-indent 2 ; +: hex-output-metadata-entry-type-string-literal 3 ; +: hex-output-metadata-entry-type-alignment 4 ; ~ Initialize the contents of the output metadata to all zeroes. This is ~ called from hex-transform at its top level, at the very start, to make sure @@ -2744,6 +2746,7 @@ allocate-transformation-state s" transformation-state" variable dup hex-output-metadata-label-loop-buffer-length 0 swap ! dup hex-output-metadata-latest-output-point 0 swap ! dup hex-output-metadata-is-fresh-line 1 swap ! + dup hex-output-metadata-indentation-depth 0 swap ! dup hex-output-metadata-first-entry 0 swap ! ; ~ (pointer -- boolean) @@ -2809,7 +2812,7 @@ allocate-transformation-state s" transformation-state" variable ~ This likely won't affect anything, because every call to pack* ~ overwrites it, but we initialize the latest output point to the start of ~ the label loop buffer here. That way, in the event the very first thing in - ~ the output is something that's supposed to have a special comment on it, + ~ the output is something that's supposed to have a magic comment on it, ~ hex-tilde-replacement will know where to attach the metadata entry. ~ ~ If that was word soup, go read about hex-tilde-replacement and @@ -2949,6 +2952,14 @@ allocate-transformation-state s" transformation-state" variable : fresh-line is-fresh-line@ not { newline } if ; +: indentation-depth@ + transformation-state transformation-state-output-metadata @ + hex-output-metadata-indentation-depth @ ; + +: indentation-depth! + transformation-state transformation-state-output-metadata @ + hex-output-metadata-indentation-depth ! ; + ~ This "replacement" is a little different from an alternate: When the code ~ under transformation attempts to compile its own version of sys-write, it ~ gets a stub that calls this word instead. It's swapped out by @@ -2976,23 +2987,36 @@ allocate-transformation-state s" transformation-state" variable dup hex-output-metadata-entry-type @ hex-output-metadata-entry-type-line-comment = { fresh-line + indentation-depth@ indent + dup hex-output-metadata-entry-string @ ." ~ " emitstring newline + 1 is-fresh-line! } if dup hex-output-metadata-entry-type @ hex-output-metadata-entry-type-line-break = { + fresh-line newline 1 is-fresh-line! } if + dup hex-output-metadata-entry-type @ + hex-output-metadata-entry-type-indent = { + dup hex-output-metadata-entry-string @ + indentation-depth@ + indentation-depth! + } if + dup hex-output-metadata-entry-type @ hex-output-metadata-entry-type-string-literal = { fresh-line + indentation-depth@ indent + ." ~ Null-terminated string literal: " 0x22 value@ emitstring drop dup hex-output-metadata-entry-string @ emitstring 0x22 value@ emitstring drop + newline 1 is-fresh-line! } if @@ -3000,8 +3024,11 @@ allocate-transformation-state s" transformation-state" variable dup hex-output-metadata-entry-type @ hex-output-metadata-entry-type-alignment = { fresh-line + indentation-depth@ indent + ." ~ Alignment padding to width " dup hex-output-metadata-entry-string @ . + newline 1 is-fresh-line! } if @@ -3011,6 +3038,7 @@ allocate-transformation-state s" transformation-state" variable } while drop + is-fresh-line@ { indentation-depth@ indent } if dup 8@ .hex8 space 0 is-fresh-line! @@ -3703,11 +3731,32 @@ allocate-transformation-state s" transformation-state" variable transformation-state transformation-state-output-metadata @ hex-output-metadata-latest-output-point @ 0 - hex-output-metadata-entry-type-line-comment - 4 roll + 3roll + ~ (data start, data length, string pointer) + + dup s" blank-line" stringcmp 0 = { + drop hex-output-metadata-entry-type-line-break 0 + add-hex-output-metadata-entry + exit + } if + + dup s" indent" stringcmp 0 = { + drop hex-output-metadata-entry-type-indent 2 + add-hex-output-metadata-entry + exit + } if + + dup s" deindent" stringcmp 0 = { + drop hex-output-metadata-entry-type-indent -2 + add-hex-output-metadata-entry + exit + } if + + hex-output-metadata-entry-type-line-comment swap add-hex-output-metadata-entry ; + ~ The tilde alternate is a very important word for the hex transform; it has ~ the task of implementing a special comment syntax which can be used by words ~ that generate binary output, to define comments which will appear as part of -- cgit 1.4.1