Curunir Curunir - 4 months ago 14
C# Question

C# Compiler-Building Scoping Issue

I am building a custom small interpreted script language and everything is working just fine except the scoping.
For the actual execution I am using a visitor pattern: enter image description here

I modified the pattern to pass through the Variable Table:

public void visit(ProgrammTree proTree){
VariableTable vt = new VariableTable();
foreach (var t in proTree.getChildren()) {
t.accept(this, vt);
}
}


And here is where the problem starts:

public void visit(WhileTree whiletree, VariableTable vt) {
var cond = (ConditionTree)whiletree.getChild(0);

while (cond.accept(this, vt).toBoolean()) {
var clonedSubTable = new VariableTable(vt)
foreach (Tree t in whiletree.getChildren()) {
t.accept(this, clonedSubTable );
}

}
}


Problem is that changes within the loop are not performed in the outer scope.
Do you have a smart way to implement this?

Answer Source

You left a couple of things a bit vague, so I'm going to make the following assumptions (please point out any that are wrong):

  • Your VariableTable maps variable names directly to their associated value
  • Whenever you assign a value, you directly set that value as the entry in the table (without going through any layer of indirection)
  • Your cloned variable tables do not keep a reference to the original and don't propagate any changes to the original

So under these assumption the problem is that any assignments done using the cloned table won't be visible in the original table even in cases where the assigned-to variable was already present in the original table.

To fix this, there are multiple approaches:

  1. You can make your table map variables to memory locations rather than values. You'd then have another table (or just a plain array) that maps memory locations to values. This way the table-entry for a variable would never change: Once the variable is defined, it gets a memory address and that address isn't going to change until the variable dies. Only the value at the address may change.

  2. A quick-and-dirty alternative to that approach that keeps you from having to manage your own memory would be to add a mutable wrapper around your values, i.e. a ValueWrapper class with a setValue method. Assuming that your cloned table is a shallow copy of the original, that means that you can call setValue on an entry of the cloned table and, if that entry was already present in the original table, the change will also be reflected in the original table.

  3. The solution that keeps closest to your current code would be to turn your table into a linked structure. Then new VariableTable(vt) would not actually copy anything, but simply create a new empty table with a parent link pointing to the original table. Any new entries would be inserted into the new table, but accesses to old entries would simply be propagated to the parent table.

Even if you choose to go with options 1 or 2 to solve your current problem, using a parent link instead of copying the table constantly would be a good idea anyway for performance reasons.

The downside of only going with solution 3 would be that you'll run into similar problems again when you implement closures. So it really only fixes your exact current problem.

The upside of solution 1 is that it allows you full control over your memory, so you have free hand in implementing features related to memory in any way you want. The down side is the same.