diff options
| -rw-r--r-- | transform.e | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/transform.e b/transform.e new file mode 100644 index 0000000..d018ad9 --- /dev/null +++ b/transform.e @@ -0,0 +1,118 @@ +: L: + ' L' entry-to-execution-token execute + { ' set-label entry-to-execution-token , } + { set-label } if-else + ; make-immediate + ~ TODO probably needs to do more + +8 allocate s" transform-state" variable +0 transform-state ! + +~ We allow immediate words to run, which means for example that flow-control +~ hexdump-between words such as if-else expect to be able to mutate the heap, +~ and they expect to find the stuff they've been compiling present on it. We +~ make this possible by swapping out the value of "here" during the execution +~ of transformed code. By focusing on "here", we get to keep the existing +~ ",". +~ +~ (-- done) +: transform-inner + word + ." wrapped here " here @ .hex64 newline + + ~ If no word was returned, exit. + dup 0 = { drop 0 exit } if + + ~ The string is on the top of the stack, so to get a pointer to it we get + ~ the stack address. + ~ (string) + value@ + + ~ If it's the magic word, end the transformation. + dup s" pyrzqxgl" stringcmp 0 = { drop dropstring 1 exit } if + ." flags " interpreter-flags @ . newline + ." transformed: " dup emitstring newline + + ~ Otherwise, look it up to see what it means. + find + + ~ Check whether the word was found in the dictionary. + dup 0 != { + ~ If the word is in the dictionary, check what mode we're in, then... + dropstring-with-result + ~ (entry pointer) + interpreter-flags @ 0x01 & { + ~ ... if we're in compile mode, there's still a chance it's an immediate + ~ word, in which case we fall through to interpret mode... + dup entry-flags@ 1 & 0 = + + ~ ... but it's a regular word, so append it to the heap. + { entry-to-execution-token . 0 exit } if + } if + + ~ ... if we're in interpret mode, or the word is immediate, run it. + ." stack before immediate word " stackhex + entry-to-execution-token execute 0 exit + } if + + ~ If it's not in the dictionary, check whether it's a decimal number. + drop + ~ As before, we get the stack address and use it as a string pointer. + ~ (string) + value@ read-integer 0 = { + ~ It's a number. + interpreter-flags @ 0x01 & { + ~ We're in compile mode; append first "lit", then the number, to the + ~ heap. The version of "lit" we use is the one that's current when we + ~ ourselves are compiled, hardcoded; doing a dynamic lookup would + ~ require dealing with what happens if it's not found. + dropstring-with-result + [ ' lit entry-to-execution-token literal ] + , , + 0 exit + } if + + ~ We're in interpret mode; push the number to the stack. Or at least, that's + ~ what the code we're interpreting will see. Really it's already on the + ~ stack, just clean everything else up and leave it there. + dropstring-with-result 0 exit + } if + + ~ If it's neither in the dictionary nor a number, just print an error. + s" No such word: " emitstring value@ emitstring dropstring 0 ; + +~ (output point -- output point, done) +: transform-one + ~ Save the old value of "here", and set it to our output point. + here @ transform-state ! here ! + ~ Now the stack has nothing of ours on it, so client code can do its thing. + + ~ Invoke client code. + transform-inner + ~ (done) + + ~ Swap them back. + here @ transform-state @ here ! + + ~ While we don't actually use transform-state outside of this invocation, + ~ for tidiness we zero it out. + 0 transform-state ! + + swap ; + + +~ (output point -- output point) +: transform { transform-one { exit } if } forever ; + +1024 allocate dup +transform +: za ." ZA" 12 13 - . ; +: ' word value@ find dropstring-with-result + interpreter-flags @ 1 & { literal } if ; make-immediate +~ ' za . newline +pyrzqxgl +." stack after " stackhex +2dup swap hexdump-between +~ : piz ." PIZ" ' za . newline ; piz +bye + |