summary refs log tree commit diff
path: root/transform.e
diff options
context:
space:
mode:
Diffstat (limited to 'transform.e')
-rw-r--r--transform.e117
1 files changed, 112 insertions, 5 deletions
diff --git a/transform.e b/transform.e
index 9a2cb21..5bec117 100644
--- a/transform.e
+++ b/transform.e
@@ -2786,6 +2786,8 @@ allocate-transformation-state s" transformation-state" variable
 ~   ~ : provide-hex64
 ~   ~ : provide-keyword
 ~   ~ : drop-subitem
+~   ~ : swap-subitems
+~   ~ : 3 roll-subitems
 ~
 ~   By the way, the reason it's possible to write these examples is that
 ~ they're all embedded within an outer comment, the text you're reading, so
@@ -3139,7 +3141,10 @@ allocate-transformation-state s" transformation-state" variable
 ~   With all this context, it likely doesn't need much explanation, but the
 ~ drop-subitem command will create an -entry-type-drop-substring entry. When
 ~ this entry is executed, it will discard an item from the substring entry
-~ stack.
+~ stack. Similarly, swap-subitems creates an -entry-type-swap-substrings,
+~ which swaps the top two items, and roll-subitems creates an
+~ -entry-type-roll-substrings, which rolls them, or unrolls them with a
+~ negative parameter.
 ~
 ~   One more thing: The order in which provide-* commands execute will most
 ~ likely match the order in which the corresponding bytes are output. This may
@@ -3205,6 +3210,8 @@ allocate-transformation-state s" transformation-state" variable
 : hex-output-metadata-entry-type-push-substring-hex64 12 ;
 : hex-output-metadata-entry-type-push-substring-string 13 ;
 : hex-output-metadata-entry-type-drop-substring 14 ;
+: hex-output-metadata-entry-type-swap-substrings 15 ;
+: hex-output-metadata-entry-type-roll-substrings 16 ;
 
 ~   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
@@ -3252,7 +3259,8 @@ allocate-transformation-state s" transformation-state" variable
   || ;
 
 : is-push-substring-entry
-  ~ Notably, -entry-type-drop-substring is not a push-substring entry.
+  ~   Notably, the substring swap, drop, and roll entry types are not
+  ~ push-substring entries.
   hex-output-metadata-entry-type @
   dup hex-output-metadata-entry-type-push-substring-decimal = swap
   dup hex-output-metadata-entry-type-push-substring-hex8 = 3roll || swap
@@ -3994,6 +4002,94 @@ allocate-transformation-state s" transformation-state" variable
         hex-output-metadata-entry-type-drop-substring = {
           pop-substring-entry-stack drop
         } if
+
+        dup hex-output-metadata-entry-type @
+        hex-output-metadata-entry-type-swap-substrings = {
+          ~   Just like in hex-emit-template-string, we want to make sure to
+          ~ discard the zeroes we get if we underflow the stack.
+          pop-substring-entry-stack
+          pop-substring-entry-stack
+          swap
+          dup { push-substring-entry-stack } { drop } if-else
+          dup { push-substring-entry-stack } { drop } if-else
+        } if
+
+        dup hex-output-metadata-entry-type @
+        hex-output-metadata-entry-type-roll-substrings = {
+          ~ The parameter giving the amount to roll by is in the string field.
+          dup hex-output-metadata-entry-string @
+          ~ (... amount to roll by)
+
+          ~   We resist the temptation to do the memcopy math. We don't need
+          ~ to, since the regular Forth roll and unroll do it for us.
+          dup 0 < {
+            ~   In this scenario, we're rolling. The amount to roll by is also
+            ~ the number of items.
+
+            0 { 2dup < } { pop-substring-entry-stack 3unroll 1+ } while
+            drop
+            ~ (... items, amount to roll by / number of items)
+
+            ~   Please notice that the items we have popped from the substring
+            ~ entry stack are now, on the value stack, in the reverse of the
+            ~ order they were on the entry stack. They will reverse again when
+            ~ we push them back.
+            ~
+            ~   The amount to roll by is positive, so the desired operation is
+            ~ a regular roll. Since the entries are reversed right now, we
+            ~ unroll them, which will be what we want when we put them back.
+            ~
+            ~   Try it out interactively with a toy example and convince
+            ~ yourself. It's hard to really model in your head, but it's true.
+            ~ The intuition is that, if you're looking at the value stack,
+            ~ the bottommost item there will be the topmost one after
+            ~ re-reversing, so we think of our operations as proceeding
+            ~ up-stack from there, instead of the usual operations that start
+            ~ at the topmost item and proceed downwards.
+            ~
+            ~   Anyway, this little trick does a regular unroll, which acts as
+            ~ a roll, and keeps the value around.
+            dup 3unroll 1+ unroll
+          } if
+          ~ (... items if positive, amount to roll by)
+
+          ~   We did the positive case first, which means the amount to roll
+          ~ by is still its original value, regardless of whether that case
+          ~ ran or not. So, we can check if it's negative to see if we need
+          ~ to do the negative case.
+          dup 0 > {
+            ~   In this scenario, we're unrolling. We negate the amount to
+            ~ roll by to get the amount to unroll by, which is also the number
+            ~ of items.
+            negate
+
+            0 { 2dup < } { pop-substring-entry-stack 3unroll 1+ } while
+            drop
+            ~ (... items, number of items)
+
+            ~   The parameter was negative, so we were asked to do an unroll.
+            ~ This trick does a roll, and keeps the value. Notice that "roll"
+            ~ always needs a positive amount.
+            dup 1+ roll swap
+          } if
+          ~ (... items, number of items)
+
+          ~   Now, regardless of which case ran, we have the items and we have
+          ~ a non-negative count of them. Notice that if we were asked to roll
+          ~ by zero, neither case ran, but that still describes what we have
+          ~ accurately.
+
+          ~ Now we push them back.
+          0 { 2dup < } {
+            3roll
+
+            ~ Again, if we underflowed we got zeros, and we discard them.
+            dup { push-substring-entry-stack } { drop } if-else
+
+            1+
+          } while
+          drop drop
+        } if
       } if
       hex-output-metadata-next-entry
     } while
@@ -4877,9 +4973,20 @@ allocate-transformation-state s" transformation-state" variable
   } if
   dup s" drop-subitem" stringcmp 0 = {
     ~ Create a new "drop substring" entry.
-    drop drop
-    2 pick execution-token-to-entry entry-to-name
-    hex-output-metadata-entry-type-drop-substring swap
+    drop drop hex-output-metadata-entry-type-drop-substring 0
+    add-hex-output-metadata-entry
+    exit
+  } if
+  dup s" swap-subitems" stringcmp 0 = {
+    ~ Create a new "swap substrings" entry.
+    drop drop hex-output-metadata-entry-type-swap-substrings 0
+    add-hex-output-metadata-entry
+    exit
+  } if
+  dup s" roll-subitems" stringcmp 0 = {
+    ~   Create a new "roll substrings" entry. The parameter goes in the string
+    ~ field, and the length is always zero.
+    drop drop 0 swap hex-output-metadata-entry-type-roll-substrings swap
     add-hex-output-metadata-entry
     exit
   } if