A stack based programming DSL, inspired by Factor
Stacktor allows programmers to use stack based programming next to more familiar styles of programming such as functional, imperative, object-oriented, etc. In stack programming, arguments are taken from the stack, and return values are pushed back on to the stack. Literal values are automatically pushed on to the stack. Stack operations (such as dropping the top element of the stack) allow programmers to shuffle order of arguments, duplicate arguments, and otherwise modify the stack as needed.
Stacktor was inspired by Factor, "a practical stack language." Factor programs are composed of "words" and literals. Words are actions that occur on the stack. For example, the "dup" word takes the top of the stack and pushes an additional copy back on the stack. Words can also be composed of a series of other words, allowing the creation of richer levels of abstraction.
In Stacktor you can define words using the (define-word ...) form; however, Stacktor will convert any Scheme procedure into a word automatically. Therefore, you can use existing Scheme procedures right inside of stack actions. When a procedure is found in a word or begin-stack form, Stacktor will pop the correct number of arguments (not including "rest" arguments) and apply the procedure to those arguments. The return value will be pushed onto the stack. Future versions of Stactor may push multiple return values onto the stack, though I haven't decided if that is a good idea yet.
To begin using stack based programming in your code, use the (begin-stack ...) form. Each item in the form will be inspected at runtime to see if it is a literal value, a procedure, or a Stacktor "word." This form will return a stack object, which you can convert to a list or process any other way you choose.
Returns a stack after operating with the supplied words. Words may be literal values (which are pushed onto the stack), word record types (typically created elsewhere), or procedures (which are converted to words at runtime).
Within the begin-stack form, you can force pushing items onto the stack. This is most useful when you want to push a procedure onto the stack, which would usually be converted to a word.
Example: (begin-stack '(1 2 3 4 5) (push (lambda (x) (* x x))) map) => (stack (1 4 9 16 25))
This keyword may be come a keyword in future releases. Do not count on it remaining a procedure.
Identical to begin-stack but also pasing in a stack, instead of creating a new one. Mostly useful when defining an usual words, but even then consider other options.
The word record is used to mark special procedures. Abstractly, word is a procedure with the signature of STACK -> STACK. For convenience, these procedures are wrapped in a special record type. You can certianly create words directly, but it is recommended you use the included word macros.
Takes one or more words and creates a word record/procedure.
Expands to: (make-word (lambda (stk) (begin-with-stack stk WORD ...)))
Creates a new word record with name. Basically a convenice macro that in turn wraps (word ...)
Expands to: (define NAME (word WORD ...))
By including the stacktor-words unit, you can pick up some useful predfined words. The following are taken from the Factor documentation. They use the Factor documentation style of defining the necessary input stack and the resulting stack. All stack operations have been prefixed with stk- to prevent name clashes.
|stk-drop||( x -- )|
|stk-2drop||( x y -- )|
|stk-3drop||( x y z -- )|
|stk-nip||( x y -- y )|
|stk-2nip||( x y z -- z )|
|stk-dup||( x -- x x )|
|stk-2dup||( x y -- x y x y )|
|stk-3dup||( x y z -- x y z x y z )|
|stk-dupd||( x y -- x x y )|
|stk-over||( x y -- x y x )|
|stk-pick||( x y z -- x y z x )|
|stk-tuck||( x y -- y x y )|
|stk-swap||( x y -- y x )|
|stk-swapd||( x y z -- y x z )|
|stk-rot||( x y z -- y z x )|
|stk--rot||( x y z -- z x y )|
|stk-roll||( x y z t -- y z t x )|
|stk--roll||( x y z t -- t x y z )|
(begin-stack 9 '(1 2 3 4 5) (push (lambda (x) (* x x))) map stk-swap (lambda (x) (lambda (y) (> y x))) ;; returns a function that will be pushed on the stack filter ) => (stack 16 25)
;; words are easy to create by cut and paste! (define-word map-square-greater-filter (push (lambda (x) (* x x))) map stk-swap (lambda (x) (lambda (y) (> y x))) filter) (begin-stack 9 '(1 2 3 4 5) map-square-greater-filter) => (stack 16 25)
Copyright (c) 2008, Mark Fredrickson. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.