April 20th, 2010

Dynamic features, part 3

The really big new dynamic feature in the next release is dynamic compilation.  This is the ability to take a run-time string - something the user types in, something you read from a file, or something you construct within the program using string operators - and run it through the compiler as though it had been part of the source code all along, producing a function you can execute.

This capability will be familiar to anyone who's used Lisp, Javascript, or another mostly-interpreted language.  C++ and other mostly-compiled languages don't tend to offer anything similar.  Up until now, TADS has been about halfway between those poles, but this new feature pushes TADS fully into the dynamic camp.

Dynamic compilation is quite easy to use.  You just run a string through the new Compiler object:

  local x = 'function(x) { return x*x; }';
  local f = Compiler.compile(x);
  local sq = f(10);

The string you give to the Compiler.compile() method uses almost the same syntax you'd use to write a function in the program's main source code.  The only difference is that the function has no name; instead, we just use the "function" keyword to introduce the argument list.  Instead of invoking the dynamic code by name, you invoke it via the object that Compiler.compile() returns.  This is a new type of intrinsic object called a DynamicFunc, which behaves in most respects like an anonymous function object.  To invoke it, we simply call the object as though it were a function, as shown in the last line above.

A DynamicFunc can be used anywhere an ordinary function pointer or anonymous function can be used.  A really interesting implication is that you can use a dynamic function with setMethod().  So not only can you create new methods of existing objects, but you can also create new methods out of thin air, without anticipating what they might look like when you write the program code.

  hallway.setMethod(&desc, Compiler.compile('method() { "The hall is very nice!"; }'));

Note that we've used the "method" keyword in place of "function".  The difference is that using "method" tells the compiler that you expect a "self" and related values to be available when the code is executed, because you intend to use the code as an object method.

Dynamic compilation has some other interesting capabilities that we'll talk more about next time.