DAVE'S LIFE ON HOLD

Back button and Forth

This weekend I had a few minutes free after the wife and kids went to sleep and so I put together a little virtual machine:



It is built on top of the Self.js code base, and came out of a thought that has been ringing around my head since about 1994.

"What if each word in Forth were a message send to the top of the stack?"

This is dangerous thought indeed, as it provides for a pure postfix language, wherein each element on the stack defines its own dictionary. In effect, you recompose the problem of Forth as "a fuck ton of little forths". Each object becomes its own DSL, interpreting the remainder of The Input Buffer (TIB in forth parlance) as a message send to itself. On object may decide to replace itself with another object on top of the stack, or may squirrel itself away, or just pop itself off in a fit of pique.

How this interpreter works is it treats any Array as the input buffer (read program or script). So when you call the .eval() method on the array it will pop off the first element and send it to the top of the stack. There is special code for handling the undefined stack object, which basically does a push the message as an object or as itself if you can't figure it out. If the object on the top of the stack responds to the message it evaluates it returning the new state of the stack as the result of the function. There is also code for invoking globals there as well from the window space. And finally all other messages simply get pushed onto the stack as themselves. This allows you to send a number to a number and have the both numbers pushed onto the stack.


Arithmetic is handled by extending Number.prototype to handle a bunch of messages in a more Forth like manner:



This allows us to abuse the string message send semantics on numbers to do our dynamic dispatch via:

2'+'.apply(2,1)

Where we are accessing the prototype's function property and dispatching it to the remainder of the stack (stack.after(0)).

Similarly, I've patched Strings, Arrays, and Objects to each have their own fetch method similar to the classical @ word in Forth.



This can be used by using an index entity @ expression to fetch the element out of the thing. So:

'foo'  foo: 'bar' @

Will produce 'bar'. Where as:

2 'hello world' @

will produce 'l', as strings and arrays are 0 based. Playing around with this I also felt the need to implement concatenation for which I chose the forth , word which usually would save a value to the heap at the here location. Since this is implemented in Javascript, and I don't expect to entertain raw memory access anytime soon, although I am planning on having an array of int32 values that can be used to map to a memory image that can be downloaded as a binary for say building native binaries suitable for running in NaCL. Using concatenation is is possible to write programs that write programs, as you can invoke eval on an array by sending it the 'eval' message:

1, 2, '+' 'eval'

Which also allows one to pass around blocks of code in an evaluable format. I have a few other ideas on how to add functions into the mix, translating forth code to Javascript for building "native" functions, and building in partial evaluators largely for building efficient macros processing.

This week I hope to hook up my web toolkit, and build an editor that allows one to write Forth.js programs in a graphical environment similar to my Phos environment, which would also work nicely with Self.js.

On a side note, since I modified the Array.eval in Self.js to be able to process binary messages, the Forth.js Array.eval overrides with different behavior. This level of flexibility is of great interest to me, as it demonstrates that in ES.next it might be possible to marry many different languages which all target JavaScript as a platform. With being able to represent programs as data via JSON, and having a persistent store like Riak and CouchDB, means that it becomes possible to define a small cache friendly core of functionality in a JavaScript toolkit and then share applications that are built on top of it.

Since I'm well on my way to having working Lisp, Self, and Forth ( read prefix, infix, postfix) languages running on a shared code base, I'm pretty certain I could pull off a multi-lingual toolkit in under 1k LOC.