Search code examples
compiler-constructionscopebytecodestack-machine

How to handle scope when generating bytecode with a handwritten compiler


I have written a small compiler for a simple stack machine. It can assemble and handle scope/functions through a number of virtual machine hacks only. That is I have it define scope and in scope variable definitions in the bytecode itself.

Can I get some pointers on how I should be handling scope.

The problems I face are mainly, how do I let it know when to and when not to overwrite a variable outside with a variable inside, and similar. The bytecode is mutable and I would prefer to change it.

Other problems include how to retain variables outside after returning. So that a variable still has its value. I can push it to stack, but I could have a lot of variables.

I think there is some compiler work done to check these things, but I can't think of what would need to be done to do it.


Solution

  • One way would be to rename variables at compile time to ensure there is no masking. So:

    {
       declare foo;
       foo = assignment;
       {
         declare foo;
    
         foo = another_assignment;
         another_use = foo;
       }
       use = foo;
    }
    

    Is equivalent to:

    {
       declare foo_0;
       foo_0 = assignment;
       {
         declare foo_1;
    
         foo_1 = another_assignment;
         another_use = foo_1;
       }
       use = foo_0;
    }
    

    While compiling, you maintain one 'rename-stack' per variable. And:

    1. Whenever you see a declaration, you generate a new name and push it to the corresponding variable's rename-stack.

    2. When you see an assignment/usage, you replace the name with whatever is on the top of the stack.

    3. When you leave a scope, you pop it off the stack.