; ctPushPop.inc ; ; This header file contains the implementation of the ; compile-time _push and _pop macros. ; ; There are actually two implementations of these macros ; in this file: ; ; 1. A version based on a single (compile-time) ; stack variable for each stack. ; ; 2. A version based on an array of (compile- ; time) stack variables with each variable ; representing a single entry in the stack. ; ; Generally (1) is the better choice as it's simpler ; and easy to understand. However, choice (1) has a ; stack nesting limitation of about 240 characters ; on the stack. If you need deeply nested stacks or ; need to push a lot of text onto the stack, then ; use implementation (2). ; ; The use1var constant determines whether this code ; implements version (1) (use1var is defined) or ; version (2) (use1var is not defined). ifndef ctPushPop_inc ctPushPop_inc = 1 ;use1var = 1 option casemap:none ifdef use1var ; Version (1) implementation of _push and _pop: ; _push macro- ; ; First argument is the name of a compile-time stack ; object (textequ constant, typically <> when the ; stack is empty). Second argument is a value (text) ; to "push" onto the stack. Inserts "value:" at the ; beginning of the specified stack object. ; Returns the new stack value including the pushed ; value (which should be assigned to the original ; stack variable). ; ; Note: technically, this could be a normal macro ; not a "functional" macro. The "exitm <>" statement ; appears only to force this macro to have parentheses ; around the arguments so that the syntax matches that ; of the "_pop" macro. ; ; Calling sequence: ; ; _push (stackVar, ) ; ; The angle brackets ("<" and ">" aren't necessary if ; the value being pushed is a single word. _push macro theStack, theValue local result result catstr <&theValue>, <:>, <&theStack> theStack textequ result exitm <> endm ; _pop macro- ; ; Passed a single argument that is the name of a global ; compile-time constant containing stack information. ; This macro pops the first item off the stack (all ; text up to the first ":" character) and returns that ; as the "function" result. This macro also removes ; the popped item from the stack specified as this ; macro's argument. ; ; Usage syntax: ; ; pData _pop(stackVar) ; ; Call will put popped text into "pData" and ; replace the value of "stackVar" with its string ; minus the popped data. _pop macro theStack local result, rPosn, slen ; First, check for an empty stack: ifb theStack echo Attempt to pop an empty stack! .err exitm <> endif ; Find the first ":" character in the stack variable: rPosn instr 1, theStack, <:> if rPosn le 0 echo Malformed stack data in pop. .err exitm <> endif ; Okay, this macro has found the ":" character, ; extract all the data up to, but not including, ; the ":" character: result substr theStack, 1, rPosn-1 ; Remove the popped item (including the ":" char) ; from the beginning of the stack variable: slen sizestr theStack if slen gt rPosn theStack substr theStack, rPosn+1 else theStack textequ <> endif ; Return the popped text as part of a "textequ" ; directive. exitm > endm ; _peek macro- ; ; Passed a single argument that is the name of a global ; compile-time constant containing stack information. ; This macro returns the first item on the stack without ; popping it. ; ; Usage syntax: ; ; pData _peek(stackVar) ; ; Call will put peeked text into "pData"\. _peek macro theStack local result, rPosn, slen ; First, check for an empty stack: ifb theStack echo Attempt to pop an empty stack! .err exitm <> endif ; Find the first ":" character in the stack variable: rPosn instr 1, theStack, <:> if rPosn le 0 echo Malformed stack data in pop. .err exitm <> endif ; Okay, this macro has found the ":" character, ; extract all the data up to, but not including, ; the ":" character: result substr theStack, 1, rPosn-1 ; Return the peeked text as part of a "textequ" ; directive. exitm > endm ; Version (2) implementation of _push and _pop: else ; pp_expand is a little "helper" macro that expands ; the text in the identifier passed to it and ; returns that text as the macro result. pp_expand macro arg local expand_txt expand_txt textequ <&arg> exitm expand_txt endm ; As for version (1) of the _push macro, this variant ; requires two arguments: a stack and a value to push. ; However, there is a big difference between this ; implementation and the version (1) implementation ; with respect to the stack argument. In version (1) ; the stack was a text constant that held all the ; stack data. In version (2), the stack is simply ; a numeric constant the specifies the number of ; stack elements currently present. Initially, this ; stack object should be assigned the value 0, ; as follow: ; ; someStk = 0 ; ; This macro will automatically create text constants ; names taking the form: "stkName_n" where "stkName" ; is the name of the stack variable (e.g., "someStk") ; and "n" is the current stack element (e.g., "0" ; through the current depth of the stack). For ; example, if you push two items onto the stack, ; the _push macro will set the stack variable to ; the value "2" and create the following two ; text constants: ; ; stkName_0 textequ <1st value pushed> ; stkName_1 textequ <2nd value pushed> ; ; The important thing to keep in mind is that you ; must not use names of the form "stkName_n" (where ; stkName is some stack identifer and "n" is a decimal ; value) in your programs as this may create ; duplicate symbol errors in your code. _push macro theStack, theValue local suffix ;; Convert the stack variable's value to ;; text string we can append to the stack ;; variable's name to create the name of ;; current stack element: suffix textequ %theStack identifier catstr <&theStack>, <_>, suffix ;; Take the identifier just created and ;; assign the pushed value to it; this, effectively, ;; pushes the item onto the stack into the ;; stack position specified by theStack variable. pp_expand(identifier) textequ <&theValue> ;; Bump up the stack pointer so the next ;; push operation will store its value in ;; the next greater stack element: theStack = theStack+1 exitm <> endm ; _pop macro- ; ; "theStack" is a global numeric variable ; whose value is the current size of the ; stack. Fetch the item at element(theStack-1) ; and return this as the function result. Also, ; decrement the stack pointer. _pop macro theStack local suffix, identifier ;; First, check for an empty stack: if theStack le 0 echo Attempt to pop an empty stack! .err exitm <> endif ;; Get the result from the "top of stack." This is ;; the value at element "theStack-1" in the array ;; of stack elements. theStack = theStack - 1 ;; Generate an identifier that corresponds to ;; element "suffix" in the stack array: suffix textequ %(theStack) identifier catstr <&theStack>, <_>, suffix ; Return the popped text as part of a "textequ" ; directive. exitm > endm ; _peek macro- ; ; "theStack" is a global numeric variable ; whose value is the current size of the ; stack. Fetch the item at element(theStack-1) ; and return this as the function result. _peek macro theStack local suffix, identifier ;; First, check for an empty stack: if theStack le 0 echo Attempt to pop an empty stack! .err exitm <> endif ;; Generate an identifier that corresponds to ;; element "suffix" in the stack array: suffix textequ %(theStack-1) identifier catstr <&theStack>, <_>, suffix ; Return the popped text as part of a "textequ" ; directive. exitm > endm endif ;use1var endif ;ifndef ctPushPop_inc