Yet another new scoping implementation

Brat is now on its third, and hopefully final, scope/environment implementation. Since scope in Brat is different than in Neko, it is necessary to work around it.

Neko has both global and local variables, and Neko functions work as closures which copy local functions from the environment. This copying is the main problem for Brat, because inner scopes always have access to variables in enclosing scopes. Let’s say you are implementing a while loop, like this:

while = { condition, block |
	true? condition,
		{ block; while ->condition, ->block }

Now you might want to use it like this:

x = 0
while { x < 10 } { p x; x = x + 1 }

If this used Neko scoping rules, the x variable in the outer scope would not be incremented, and the two closures would have separate copies of x, so the block inside the loop would never modify the variable in the conditional.

First attempt

The first method for implementing scope the Brat way was to make all variables global. That solved the problem above, but there were other problems. Besides being inefficient and inelegant, it was not entirely correct, either. Local variables inside recursive functions, for example, did not behave properly.

Second attempt

One way to allow functions to modify outer variables in Neko without using globals is to put them inside an array. The array is copied into the function, but modifications to the contents of the array are reflected in the outer variable.

For example:

//This is Neko code

var x = 1
var a = $array(x)

f = function() {
        a[0] = 2


$print(a[0])  //prints "2"

Knowing this, we can simply put all our variables into arrays and they will be properly modified by inner functions and all will be right with the world inside our programs.

But there is still a problem. Why construct an array to hold variables that may never be used in an inner scope? Not only is it a waste of memory, we are performing pointless array index look-ups rather than just straight variables.

Third attempt

The latest version of Brat now does a scan of the program and determines which variables will be used by inner scopes. Variables which are will be stored inside an array, while those that do not will be left as local variables. This saves memory and time when running the program, for a small increase in both when compiling.

Implementing this was something of a chore (it requires a second pass when ‘compiling,’ when before everything was accomplished in a single pass), which is why there have been few updates recently. However, now that it is done, Brat is more efficient and, in my opinion, proper.

comments by Disqus
Fork me on GitHub