BruteForce/Scope
/** Keep track of declarations in diffirent scopes */ class Scope extends Object exportstructs; var private int ScopeLevel; var int verbose; enum DeclarationType { DT_None, DT_String, DT_Int, DT_Float, DT_Bool, DT_Function, };
The diffirent types of declarations we have. The DT_None is a safe guard to be used for optional function arguments and should not be used for actualy declarations.
struct Declaration { var string name; var string value; var int scopelevel; var DeclarationType type; }; var array<Declaration> declarations;
The value can be anything: a string, an int or even a tree node.
function Declaration getDeclaration(string name, optional DeclarationType type) { local int i; for (i = declarations.length-1; i >= 0; i--) { if ((declarations[i].name ~= name) && (declarations[i].scopelevel <= ScopeLevel)) { if (verbose> 0 ) log(ScopeLevel$"] getDeclaration("$name$", "$type$")", 'Scope'); if ((type != DT_None) && (declarations[i].type != type)) { Warn("Declaration type doesn't match:"@name@"has"@declarations[i].type@"requested"@type); Assert(false); } return declarations[i]; } } Warn("Undeclared identifier:"@name); Assert(false); }
Look up a declaration in the declaration table. We start from the bottom because the the variables with the highest scope level are at the bottom and mostlikely they are used more that the low level scope declarations.
We also check the declaration type to match the requested type. This is only used to divide functions and normal variables at the moment.
function DeclarationType getType(string name) { local int i; for (i = declarations.length-1; i >= 0; i--) { if ((declarations[i].name ~= name) && (declarations[i].scopelevel <= ScopeLevel)) { if (verbose> 0 ) log(ScopeLevel$"] getType("$name$") ="@declarations[i].type, 'Scope'); return declarations[i].type; } } Warn("Undeclared identifier:"@name); Assert(false); } function string setDeclaration(string name, string value, optional DeclarationType type) { local int i; for (i = declarations.length-1; i >= 0; i--) { if ((declarations[i].name ~= name) && (declarations[i].scopelevel <= ScopeLevel)) { if (verbose> 0 ) log(ScopeLevel$"] setDeclaration("$name$", "$value$", "$type$")", 'Scope'); declarations[i].value = value; return declarations[i].value; } } Warn("Undeclared identifier:"@name); Assert(false); } function newDeclaration(string name, optional DeclarationType type, optional string value) { local int i; for (i = 0; i < declarations.length; i++) { if ((declarations[i].name ~= name) && (declarations[i].scopelevel == ScopeLevel)) { Warn("Identifier redeclared:"@name); Assert(false); } } if (verbose> 0 ) log(ScopeLevel$"] newDeclaration("$name$", "$type$")", 'Scope'); declarations.length = i+1; declarations[i].name = name; declarations[i].scopelevel = ScopeLevel; declarations[i].value = value; declarations[i].type = type; } function openScope() { if (verbose> 0 ) log(ScopeLevel$"] openScope()", 'Scope'); ScopeLevel++; }
openScope is easy, it just increases the current scope level, new declarations will automatically be put in this scope level.
function closeScope() { local int i; if (verbose> 0 ) log(ScopeLevel$"] closeScope()", 'Scope'); for (i = declarations.length-1; i >= 0; i--) { if (declarations[i].scopelevel >= ScopeLevel) { declarations.remove(i, 1); } } ScopeLevel--; }
On closeScope we need to remove all previously declared variables in this, or higher, scope. After that we decrease the scope level.
static function DeclarationType stringToType(string type) { if (type ~= "string") return DT_String; else if (type ~= "int") return DT_Int; else if (type ~= "float") return DT_Float; else if (type ~= "bool") return DT_Bool; Warn("Unknown type"@type); assert(false); }
Convert a string to a declaration type.
defaultproperties { ScopeLevel=0 verbose=0 }