Well, the plan was to have Affe be a simple language. I guess it still is, but I've been adding support for many C-style language features and tweaking other features over the past day. Specifically, the following:
- Added if/else support, with the same nesting rules as C.
Added the following operators:
- ! (boolean not)
- == (equality)
- != (inequality)
- \< (less than)
- \<= (less than or equal to)
- > (greater than)
- >= (greater than or equal to)
The tree is now scanned for variables before compilation, since with conditionals it's no longer sufficient to initialize an unassigned variable the first time the compiler sees it. All variables local to the script are initialized to zero before the tree is compiled. This may be optimized later if the performance impact is really all that bad.
- A call can now be used as a statement instead of just in an expression.
- Added support for while loops, including the continue and break keywords.
- The library is now licensed under the LGPL.
The next two planned features are:
- A state preserving mechanism, like I discussed in my last post. I've been putting this off because I'm having trouble coming up with a clean way to pass the state object around.
- Subroutine support. The subroutine would be compiled into its own dynamic method (meaning that it would not share scope with the enclosing script) and would be called from the main script.
Subroutine support brings up two interesting points. One is that variable state persistence would probably not be able to extend to them, as storing its state on the same object would not make sense considering that the scope would be different. Presumably one could chain the states together using the subroutine names, but I'm not sure that persistence would really be needed in the case of nested subroutines anyway.
The other point is about the lifetime of a DynamicMethod. These objects are eligible for garbage collection, which is kind of their point. But if I call one DynamicMethod from another, and then discard the reference, could the GC destroy the method while the method that calls it is still alive? (And do Mono and MS.NET agree what should happen?) This is an important question to answer right now, because it would be pointless to implement this if the GC will ruin it.
(Actually if the GC would destroy the method, it could be worked around by maintaining a mapping between weak references to caller methods and strong references to the callee methods. The callee method would be discarded when its caller weak reference is collected.)
Okay, enough rambling.