A Lispy Forth for Smalltalk
Avi Bryant
avi at beta4.com
Sun Feb 15 01:52:44 UTC 2004
I've just put up a package called "Sorrow" on SqueakMap, which is an
extremely simple compiler for a language inspired by the "functional
Forth" Joy
(http://www.latrobe.edu.au/philosophy/phimvt/joy/j01tut.html).
The compiler takes Squeak arrays as input, and in fact the easiest way
to invoke it is to send #value: to an array literal, which will
immediately compile and evaluate a new method. It implements a postfix
stack language, so there are no variable references or scopes. Any
non-symbol objects in the array get pushed onto the stack; a symbol
either pushes a global variable onto the stack (if it starts with an
uppercase letter) or causes a message send. The message send will pop
the appropiate number of arguments off of the stack for the arity of
the selector, and dispatch on the last of these (the lowest on the
stack).
Each method invocation has its own stack, which starts with its
receiver and arguments. The top of the stack once the method has
finished executing is returned at the end, ie, placed on top of the
sender's stack (I'm open to suggestion on the details of this, though).
It may be clear that this maps pretty directly to Squeak bytecode,
which is why the compiler is so trivial (around 30 lines, but I was
being verbose).
Block like behavior can be gotten by simply constructing and passing
around arrays. Here's a simple example which evaluates to 6 when sent
#value -
#((1 2 3) 0 (+) inject:into:)
You can also use #addSelector:withArray: to install the compiled
version of an array in a class, eg,
Integer addSelector: #plusOne withArray: #(1 +).
41 onePlus. "42"
The methods #swap: and #dup are useful for stack manipulation, eg,
Integer addSelector: #double withArray: #(dup +).
21 double. "42"
Integer addSelector: #oneDividedBy withArray: #(1 swap: /).
2 oneDividedBy. "1/2"
I just did this for fun, but I can almost imagine using these arrays in
the same way that people sometimes define #value: on Symbol:
someCollection select: #(isOdd) thenCollect: #(2 *)
instead of
someCollect select: [:ea | ea isOdd] thenCollect: [:ea | ea * 2].
Avi
More information about the Squeak-dev
mailing list
|