Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ianj-als/pcl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Johnson <ian.johnson@appliedlanguage.com>2013-11-11 21:10:40 +0400
committerIan Johnson <ian.johnson@appliedlanguage.com>2013-11-11 21:10:40 +0400
commit2b76e0b2426fc833cc814c0f36d7c2fdb006a48e (patch)
treee196c5b6a5fb86ae006c770e6b6b4eaf904fb170
parent203e44a3d3254a8805d92381ae26975f49da1c21 (diff)
Re-factored the symbol table and its scoping.
-rw-r--r--src/pclc/parser/command.py25
-rw-r--r--src/pclc/visitors/do_executor_visitor.py33
-rw-r--r--src/pclc/visitors/executor_visitor.py15
-rw-r--r--src/pclc/visitors/first_pass_resolver_visitor.py62
-rw-r--r--src/pclc/visitors/second_pass_resolver_visitor.py4
-rw-r--r--src/pclc/visitors/symbol_table.py102
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]