DAVE'S LIFE ON HOLD

Interpreter Objects for JavaScript

For the past few weeks, I have been playing around with a JavaScript object that interprets its arguments when invoked as a function in its own context. 

  Test("log:","hello Dave, 1 + 2 =", 1,2,"+", "log")

Where in:

Function.prototype"+" = function()  <br>        this['.stack'].push(this['.stack'].pop() + this['.stack'].pop());<br>        return 0

Function.prototype'log:' = function(x) <br>        this.globals.console.log(x);<br>        return 1

Function.prototype.log = function() <br>        this.globals.console.log(this['.stack'].pop());<br>        return 0

How it works is each word is either a method of the object or a global reference (hoisted via a globals proxy) or a literal value. Any literal value is simply any non-method named argument pushed onto the object's stack and treated as data.  The definition of the Function object has much to do with using multiple instances of the same interpreter function as our base object.  In effect, we attach the state machine onto the new Function() we construct for each "Object" in the system and let it maintain its locality.   "Objects" in this system interact by passing messages via a simple JavaScript function call. Any object with a reference to another object can invoke its methods by simply calling that object-function passing the messages as arguments. 

Part of the programming model for this environment is that strings can contain source code which can be compiled and evaled as well. The way it works is that String.prototype is extended with a .compile and .eval method so that one can say:

 Foo.alert =  'alert("hello world")'.compile()

which would be the equivalent of:

  Foo.alert = function()           return; " alt=" 
          alert("hello world"); 
          return; " />

For parameter definitions, you can use the | separator to separate a list of params from the body of the function:

  Foo.alert = 'name | alert("hello " + name)'.compile()

This is the equivalent of:

  Foo.alert = function(name)           return 0; " alt="
          alert("hello " + name); 
          return 0; " />

In this fashion one can add as many params as one wants and it will generate the associated function.  Another fun feature is using the innerText property of a DOM element to inline your code:

  document.querySelectorAll(".code").map('x |  x.innerText.eval()'.compile())

This will take each element on a page with class code and evaluate it as if it were scripted using these tools. We're using  compile() to return the function, and using eval to run the embedded code.  This trick is lots of fun for syntax highlighted code listings that are also executed on the page.   We can use all of this together with CSS to produce executable documents with many embedded stack machines.   We can also embed and send objects via JSON to other machines with the ability to embed context:

alert("hello " + name)'

Where the recipient of the JSON constructs a 'Foo' object in its current context, applies the 'does:' method of Foo, which compiles the method named 'alert:', which itself a method of Foo defined as:

  function(name)         return 1 " alt="
        alert("hello " + name);
        return 1 " />

What is interesting here is 'alert:' itself derives the value of name from the message stream and not the object's internal state. 

  Foo('alert:','Dave');

Will in fact pop up "hello Dave" as the return 1 indicates that 'alert:' is a message parsing method with arity 1. In the case of:

   Foo(1,2,'+')

The messages 1, 2, and '+' are all methods of arity 0. The compile method on string has the nice property of returning the arity as the return value. Each method by the conventions of the compiler requires the method return the number of parameters a message expects so it can consume that many from the message stream. 

Take for example a method like 'does:', which takes 2 arguments and generates a method definition.  The underlying method looks something like:

   function(name,method) <br>          this[name] = method.compile();<br>          return 2

That is pretty much it, you can rewrite 'does:' as a 2 stage process:

Foo('define:', 'alert','as:', '| alert("goodbye")')

Which would look something like:

Foo('does:','define:', 'term | this".term" = term')
        ('does:','as:',
                    'method| thisthis[".term"] = method.compile()')

And now we have two unary messages in the place of one through an addition to the statemachine.  We can push this further:

  Foo('define:','define','as:',
          '| this".term" = this.stack.pop()')
          ('as','define','as:',
                  '| thisthis[".term"] = this.stack.pop().compile()')

We now have two null arity methods which take their arguments on the internal stack. This exploits any string which is not a method is a literal:

  Foo('warn','define', '| console.warn(this.stack.pop())','as')

This being better than that is a mere point of debate, but this pretty effectively illustrates how flexible this approach can be.  In fact, we can take a hybrid approach and define does as:

  method | thisthis.stack.pop() = method.compile()

so it would look like:

  Foo('warn','does:','| console.warn(this.stack.pop())')

Which opens up the possibility of using a method to generate a method name, and leave it on the stack. 

This code will be up on my github shortly...