diff options
author | Ian Johnson <ian.johnson@appliedlanguage.com> | 2013-11-11 21:10:40 +0400 |
---|---|---|
committer | Ian Johnson <ian.johnson@appliedlanguage.com> | 2013-11-11 21:10:40 +0400 |
commit | 2b76e0b2426fc833cc814c0f36d7c2fdb006a48e (patch) | |
tree | e196c5b6a5fb86ae006c770e6b6b4eaf904fb170 | |
parent | 203e44a3d3254a8805d92381ae26975f49da1c21 (diff) |
Re-factored the symbol table and its scoping.
-rw-r--r-- | src/pclc/parser/command.py | 25 | ||||
-rw-r--r-- | src/pclc/visitors/do_executor_visitor.py | 33 | ||||
-rw-r--r-- | src/pclc/visitors/executor_visitor.py | 15 | ||||
-rw-r--r-- | src/pclc/visitors/first_pass_resolver_visitor.py | 62 | ||||
-rw-r--r-- | src/pclc/visitors/second_pass_resolver_visitor.py | 4 | ||||
-rw-r--r-- | src/pclc/visitors/symbol_table.py | 102 |
6 files changed, 127 insertions, 114 deletions
diff --git a/src/pclc/parser/command.py b/src/pclc/parser/command.py index dc8a036..3fe1bcc 100644 --- a/src/pclc/parser/command.py +++ b/src/pclc/parser/command.py @@ -19,13 +19,24 @@ from entity import Entity -class Function(Entity): +class ResolutionSymbols(object): + def __init__(self): + self.symbols = dict() + + def __setitem__(self, symbol, value): + self.symbols[symbol] = value + + def __getitem__(self, symbol): + return self.symbols[symbol] + +class Function(Entity, ResolutionSymbols): def __init__(self, filename, lineno, name, arguments): Entity.__init__(self, filename, lineno) + ResolutionSymbols.__init__(self) self.name = name self.arguments = arguments @@ -43,13 +54,14 @@ class Function(Entity): ", ".join(map(lambda arg: arg.__repr__(), self.arguments)), super(Function, self).__repr__()) -class Command(Entity): +class Command(Entity, ResolutionSymbols): def __init__(self, filename, lineno, identifier, function): Entity.__init__(self, filename, lineno) + ResolutionSymbols.__init__(self) self.identifier = identifier self.function = function @@ -71,13 +83,14 @@ class Command(Entity): repr(self.function), super(Command, self).__repr__()) -class Return(Entity): +class Return(Entity, ResolutionSymbols): def __init__(self, filename, lineno, value = None, mappings = list()): Entity.__init__(self, filename, lineno) + ResolutionSymbols.__init__(self) self.value = value self.mappings = mappings @@ -101,7 +114,7 @@ class Return(Entity): else ", ".join(map(lambda m: repr(m), self.mappings)), \ super(Return, self).__repr__()) -class IfCommand(Entity): +class IfCommand(Entity, ResolutionSymbols): class Block(Entity): def __init__(self, filename, lineno, commands, if_command): Entity.__init__(self, filename, lineno) @@ -138,6 +151,7 @@ class IfCommand(Entity): then_commands, else_commands): Entity.__init__(self, filename, lineno) + ResolutionSymbols.__init__(self) self.identifier = identifier self.condition = condition self.then_commands = IfCommand.ThenBlock(filename, then_commands[0].lineno, then_commands, self) @@ -173,7 +187,7 @@ class IfCommand(Entity): " ".join(map(f, self.else_commands)), super(IfCommand, self).__repr__()) -class LetCommand(Entity): +class LetCommand(Entity, ResolutionSymbols): class LetBindings(Entity): def __init__(self, filename, @@ -207,6 +221,7 @@ class LetCommand(Entity): bindings, expression): Entity.__init__(self, filename, lineno) + ResolutionSymbols.__init__(self) self.identifier = identifier self.bindings = LetCommand.LetBindings(filename, bindings[0].lineno, bindings) self.expression = expression diff --git a/src/pclc/visitors/do_executor_visitor.py b/src/pclc/visitors/do_executor_visitor.py index 0b620ce..e163209 100644 --- a/src/pclc/visitors/do_executor_visitor.py +++ b/src/pclc/visitors/do_executor_visitor.py @@ -144,10 +144,10 @@ class IntermediateRepresentation(object): self.__current_node = self.__current_let.parent self.__current_let = None - def generate_code(self, executor_visitor, assignment_symbol_table, is_instrumented): + def generate_code(self, executor_visitor, is_instrumented): # Generate function call lambdas - generate_func_args = lambda args: ", ".join([executor_visitor._generate_terminal(a) for a in args]) - generate_func_call = lambda f: "____%s(%s)" % (f.name, generate_func_args(f.arguments)) + generate_func_args = lambda args, scope: ", ".join([executor_visitor._generate_terminal(a, scope) for a in args]) + generate_func_call = lambda f, scope: "____%s(%s)" % (f.name, generate_func_args(f.arguments, scope)) top_function_name = self.__get_function_name(executor_visitor._module.definition.identifier) code = [("def %s(a, s):" % top_function_name, "+")] @@ -167,12 +167,14 @@ class IntermediateRepresentation(object): if isinstance(node, IntermediateRepresentation.IRCommandNode): # Command action code generation command = node.object + scope = command['scope'] + code.append(("def %s(a, s):" % self.__get_function_name(command), "+")) if command.identifier: code.append(("%s = %s" % (executor_visitor._get_temp_var(command.identifier), \ - generate_function_call(command.function)), "")) + generate_function_call(command.function, scope)), "")) else: - code.append((generate_function_call(command.function), "")) + code.append((generate_function_call(command.function, scope), "")) for child in node.children: more_code = self.__generate_code(child, @@ -193,8 +195,10 @@ class IntermediateRepresentation(object): elif isinstance(node, IntermediateRepresentation.IRIfNode): # If command action code generation if_command = node.object + scope = if_command['scope'] + code.append(("def %s(a, s):" % self.__get_function_name(if_command), "+")) - code.append(("if %s:" % executor_visitor._generate_condition(if_command.condition), "+")) + code.append(("if %s:" % executor_visitor._generate_condition(if_command.condition, scope), "+")) for then_node in node.then_block: more_code = self.__generate_code(then_node, @@ -220,13 +224,15 @@ class IntermediateRepresentation(object): code.append(("%s(a, s)" % self.__lookup_function_name(if_command), "")) elif isinstance(node, IntermediateRepresentation.IRReturnNode): return_command = node.object + scope = return_command['scope'] + if return_command.value: - code.append(("return %s" % executor_visitor._generate_terminal(return_command.value))) + code.append(("return %s" % executor_visitor._generate_terminal(return_command.value, scope))) elif len(return_command.mappings) == 0: code.append(("return None", "")) else: code.append(("return {%s}" % ", ".join(["'%s' : %s" % \ - (m.to, executor_visitor._generate_terminal(m.from_)) \ + (m.to, executor_visitor._generate_terminal(m.from_, scope)) \ for m in return_command.mappings]), "")) elif isinstance(node, IntermediateRepresentation.IRLetNode): # Let command @@ -247,7 +253,8 @@ class IntermediateRepresentation(object): # executor_visitor, # is_instrumented) code.append(("%s = %s" % (executor_visitor._get_temp_var(let_command.expression), \ - generate_function_call(let_command.expression)), "")) + generate_function_call(let_command.expression, + let_command.expression['scope'])), "")) code.append(("return %s" % executor_visitor._lookup_var(let_command.expression), "")) code.extend([(None, "-"), (None, "-")]) @@ -290,9 +297,7 @@ class DoExecutorVisitor(ExecutorVisitor): @multimethod(Import) def visit(self, an_import): - self._write_line("import %s as ____%s" % \ - (an_import.module_name, \ - an_import.alias)) + self._write_line("import %s as ____%s" % (an_import.module_name, an_import.alias)) @multimethod(Component) def visit(self, component): @@ -329,9 +334,7 @@ class DoExecutorVisitor(ExecutorVisitor): @multimethod(object) def visit(self, nowt): - func_defs = self.__ir.generate_code(self, - self._module.resolution_symbols['assignment_table'], - self._is_instrumented) + func_defs = self.__ir.generate_code(self, self._is_instrumented) # Write initialise function self._write_function("initialise", func_defs, ["config"]) diff --git a/src/pclc/visitors/executor_visitor.py b/src/pclc/visitors/executor_visitor.py index 7fa8cba..c217b3d 100644 --- a/src/pclc/visitors/executor_visitor.py +++ b/src/pclc/visitors/executor_visitor.py @@ -111,11 +111,10 @@ class ExecutorVisitor(object): def _lookup_var(self, expr): return self._var_table[expr] - def _generate_terminal(self, terminal): + def _generate_terminal(self, terminal, scope): if isinstance(terminal, StateIdentifier): return "s['%s']" % terminal.identifier - elif isinstance(terminal, Identifier) and \ - terminal in self._module.resolution_symbols['assignment_table']: + elif scope is not None and isinstance(terminal, Identifier) and terminal in scope: return self._lookup_var(terminal) elif isinstance(terminal, Identifier): return "a['%s']" % terminal @@ -125,20 +124,20 @@ class ExecutorVisitor(object): raise ValueError("Unexpected terminal in conditional: filename = %s, line no = %d" % \ (terminal.filename, terminal.lineno)) - def _generate_condition(self, cond_expr): + def _generate_condition(self, cond_expr, scope = None): # Terminal! if isinstance(cond_expr, TerminalConditionalExpression): - return self._generate_terminal(cond_expr.terminal) + return self._generate_terminal(cond_expr.terminal, scope) elif isinstance(cond_expr, ConditionalExpression): - left_code = self._generate_condition(cond_expr.left) - right_code = self._generate_condition(cond_expr.right) + left_code = self._generate_condition(cond_expr.left, scope) + right_code = self._generate_condition(cond_expr.right, scope) op = self.__conditional_operators[cond_expr.__class__] if isinstance(cond_expr, XorConditionalExpression): left_code = "bool(%s)" % left_code right_code = "bool(%s)" % right_code return "(%s %s %s)" % (left_code, op, right_code) elif isinstance(cond_expr, UnaryConditionalExpression): - return "(%s)" % self._generate_condition(cond_expr.expression) + return "(%s)" % self._generate_condition(cond_expr.expression, scope) else: raise ValueError("Unexpected expression in conditional: filename = %s, line no = %d" % \ (cond_expr.filename, cond_expr.lineno)) diff --git a/src/pclc/visitors/first_pass_resolver_visitor.py b/src/pclc/visitors/first_pass_resolver_visitor.py index 508c7d6..7a36888 100644 --- a/src/pclc/visitors/first_pass_resolver_visitor.py +++ b/src/pclc/visitors/first_pass_resolver_visitor.py @@ -200,6 +200,7 @@ class FirstPassResolverVisitor(ResolverVisitor): def visit(self, module): self._module = module if not self._module.__dict__.has_key("resolution_symbols"): + global_symbol_table = SymbolTable() self._module.resolution_symbols = {'imports' : dict(), 'components' : dict(), 'used_components' : dict(), @@ -207,7 +208,8 @@ class FirstPassResolverVisitor(ResolverVisitor): 'configuration' : dict(), 'unused_configuration' : list(), 'command_table' : list(), - 'assignment_table' : SymbolTable()} + 'symbol_table' : global_symbol_table} + self._current_scope = global_symbol_table if not self.__dict__.has_key("__resolve_import"): self.__resolve_import = self.__resolve_runtime_import if self._module.definition.is_leaf \ else self.__resolve_pcl_import @@ -801,6 +803,9 @@ class FirstPassResolverVisitor(ResolverVisitor): @multimethod(Function) def visit(self, function): + # Record the current scope in the entity's resolution symbols + function['scope'] = self._current_scope + # Get the package alias and function name package_alias, name = function.name.split(".") @@ -870,10 +875,10 @@ class FirstPassResolverVisitor(ResolverVisitor): if isinstance(argument, StateIdentifier): if argument not in self._module.resolution_symbols['configuration']: self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s", - [function], - lambda f: {'filename' : f.filename, - 'lineno' : f.lineno, - 'arg_name' : argument}) + [argument], + lambda a: {'filename' : a.filename, + 'lineno' : a.lineno, + 'arg_name' : a}) else: # Mark the configuration as used try: @@ -883,12 +888,12 @@ class FirstPassResolverVisitor(ResolverVisitor): pass elif isinstance(argument, Identifier): if argument not in self._module.definition.inputs and \ - argument not in self._module.resolution_symbols['assignment_table']: + argument not in self._current_scope: self._add_errors("ERROR: %(filename)s at line %(lineno)d, unknown function argument %(arg_name)s", - [function], - lambda f: {'filename' : f.filename, - 'lineno' : f.lineno, - 'arg_name' : argument}) + [argument], + lambda a: {'filename' : a.filename, + 'lineno' : a.lineno, + 'arg_name' : a}) def __resolve_assignment(self, identifier, assign_object): # Check the assigned variable is *not* an input. Inputs are immutable. @@ -909,7 +914,7 @@ class FirstPassResolverVisitor(ResolverVisitor): # Check if assignment variable has been used already. Code generation may not # generate all code to provide the previous use! - if identifier in self._module.resolution_symbols['assignment_table']: + if identifier in self._current_scope: self._add_warnings("WARNING: %(filename)s at line %(lineno)d, duplicate assignment variable %(var_name)s", [identifier], lambda i: {'filename' : i.filename, @@ -917,16 +922,22 @@ class FirstPassResolverVisitor(ResolverVisitor): 'var_name' : i}) else: # Record the assignment and the command - self._module.resolution_symbols['assignment_table'][identifier] = assign_object + self._current_scope[identifier] = assign_object @multimethod(Command) def visit(self, command): + # Record the current scope in the entity's resolution symbols + command['scope'] = self._current_scope + # If no assignment then we don't need to do anything. if command.identifier: self.__resolve_assignment(command.identifier, command) @multimethod(Return) def visit(self, ret): + # Record the current scope in the entity's resolution symbols + ret['scope'] = self._current_scope + if not ret.mappings: return @@ -943,7 +954,8 @@ class FirstPassResolverVisitor(ResolverVisitor): duplicate_to[mapping.to] = 1 # Does the 'from' exist? - if mapping.from_ not in self._module.resolution_symbols['assignment_table']: + if mapping.from_ not in self._current_scope and \ + mapping.from_ not in self._module.definition.inputs: missing_froms.append(mapping.from_) # Record duplicate 'to' maps @@ -976,28 +988,40 @@ class FirstPassResolverVisitor(ResolverVisitor): @multimethod(IfCommand) def visit(self, if_command): - self._module.resolution_symbols['assignment_table'].pop_inner_scope() + # Record the current scope in the entity's resolution symbols + if_command['scope'] = self._current_scope + + self._current_scope = self._current_scope.get_parent() if if_command.identifier: self.__resolve_assignment(if_command.identifier, if_command) @multimethod(IfCommand.ThenBlock) def visit(self, then_block): - self._module.resolution_symbols['assignment_table'].push_inner_scope() + then_scope = SymbolTable() + self._current_scope.add_nested_scope(then_scope) + self._current_scope = then_scope @multimethod(IfCommand.ElseBlock) def visit(self, else_block): - self._module.resolution_symbols['assignment_table'].pop_inner_scope() - self._module.resolution_symbols['assignment_table'].push_inner_scope() + previous_scope = self._current_scope.get_parent() + else_scope = SymbolTable() + previous_scope.add_nested_scope(else_scope) + self._current_scope = else_scope @multimethod(LetCommand) def visit(self, let_command): + # Record the current scope in the entity's resolution symbols + let_command['scope'] = self._current_scope + if let_command.identifier: self.__resolve_assignment(let_command.identifier, let_command) @multimethod(LetCommand.LetBindings) def visit(self, let_bindings): - self._module.resolution_symbols['assignment_table'].push_inner_scope() + let_scope = SymbolTable() + self._current_scope.add_nested_scope(let_scope) + self._current_scope = let_scope @multimethod(LetCommand.LetEnd) def visit(self, let_end): - self._module.resolution_symbols['assignment_table'].pop_inner_scope() + self._current_scope = self._current_scope.get_parent() diff --git a/src/pclc/visitors/second_pass_resolver_visitor.py b/src/pclc/visitors/second_pass_resolver_visitor.py index 05868f6..f384f94 100644 --- a/src/pclc/visitors/second_pass_resolver_visitor.py +++ b/src/pclc/visitors/second_pass_resolver_visitor.py @@ -158,3 +158,7 @@ class SecondPassResolverVisitor(FirstPassResolverVisitor): @multimethod(LetCommand.LetBindings) def visit(self, let_bindings): pass + + @multimethod(LetCommand.LetEnd) + def visit(self, let_end): + pass diff --git a/src/pclc/visitors/symbol_table.py b/src/pclc/visitors/symbol_table.py index 1e68f80..98492c7 100644 --- a/src/pclc/visitors/symbol_table.py +++ b/src/pclc/visitors/symbol_table.py @@ -17,88 +17,56 @@ # along with Pipeline Creation Language (PCL). If not, see <http://www.gnu.org/licenses/>. # class SymbolTable(object): - class Scope(object): - def __init__(self): - self.__table = dict() - self.__nested_scopes = list() - self.__root = None - - def addNestedScope(self, scope): - scope.__root = self - return self.__nested_scopes.append(scope) - - def getRoot(self): - return self.__root - - def __getitem__(self, key): - return self.__table[key] - - def __setitem__(self, key, value): - self.__table[key] = value - - def __delitem__(self, key): - del self.__table[key] - - def __contains__(self, item): - return item in self.__table - - def __iteritems__(self): - return self.__table.__iteritems__() - - def iterkeys(self): - return self.__table.iterkeys() - - def keys(self): - return self.__table.keys() - - def has_key(self, key): - return self.__table.has_key(key) + def __init__(self): + self.__table = dict() + self.__nested_scopes = list() + self.__parent = None - def _get_nested_scopes_iter(self): - return self.__nested_scopes.__iter__() + def add_nested_scope(self, scope): + scope.__parent = self + return self.__nested_scopes.append(scope) - def __init__(self): - self.__tree = SymbolTable.Scope() - self.__current_scope = self.__tree + def get_parent(self): + return self.__parent def __getitem__(self, key): - scope = self.__current_scope - while scope is not None: - if scope.has_key(key): - return scope[key] - scope.getRoot() - - raise KeyError + return self.__table[key] def __setitem__(self, key, value): - self.__current_scope[key] = value + self.__table[key] = value + + def __delitem__(self, key): + del self.__table[key] def __contains__(self, item): - scope = self.__current_scope + scope = self while scope is not None: - if item in scope: + if item in scope.__table: return True - scope = scope.getRoot() + scope = scope.__parent return False - def push_inner_scope(self): - scope = SymbolTable.Scope() - self.__current_scope.addNestedScope(scope) - self.__current_scope = scope + def __iteritems__(self): + return self.__table.__iteritems__() - def pop_inner_scope(self): - outer_scope = self.__current_scope.getRoot() - if outer_scope is not None: - self.__current_scope = outer_scope + def iterkeys(self): + return self.__table.iterkeys() + + def keys(self): + return self.__table.keys() + + def has_key(self, key): + return self.__table.has_key(key) def __str__(self): disp = lambda s, d: (" " * d) + ("%d : " % d) + ", ".join([str(sym) for sym in s.keys()]) + "\n" - def trav(s, d): - rep = disp(s, d) - for ns in s._get_nested_scopes_iter(): - rep += trav(ns, d + 1) - return rep - - return trav(self.__tree, 0) + def trav(st): + if st is None: + return (0, "") + + depth, result = trav(st.__parent) + return (depth + 1, result + disp(st, depth)) + + return trav(self)[1] |