diff options
author | Ian Johnson <ian.johnson@appliedlanguage.com> | 2013-10-21 20:23:51 +0400 |
---|---|---|
committer | Ian Johnson <ian.johnson@appliedlanguage.com> | 2013-10-21 20:23:51 +0400 |
commit | 7675a18cc6bd760b5bea34c216185c5cec6c8212 (patch) | |
tree | 199bbc4a3f5d6727b5ec8cb3e7c9f8137a39c585 | |
parent | b43d1c1ff5f0ad64552339feaee0d176156e0ed4 (diff) |
Started do notation code generation.
-rw-r--r-- | src/pclc/parser/executor.py | 30 | ||||
-rwxr-xr-x | src/pclc/pclc.py | 6 | ||||
-rw-r--r-- | src/pclc/visitors/do_executor_visitor.py | 100 | ||||
-rw-r--r-- | src/pclc/visitors/executor_visitor.py | 381 | ||||
-rw-r--r-- | src/pclc/visitors/first_pass_resolver_visitor.py | 8 | ||||
-rw-r--r-- | src/pclc/visitors/pcl_executor_visitor.py | 366 |
6 files changed, 534 insertions, 357 deletions
diff --git a/src/pclc/parser/executor.py b/src/pclc/parser/executor.py new file mode 100644 index 0000000..85387ab --- /dev/null +++ b/src/pclc/parser/executor.py @@ -0,0 +1,30 @@ +# +# Copyright Capita Translation and Interpreting 2013 +# +# This file is part of Pipeline Creation Language (PCL). +# +# Pipeline Creation Language (PCL) is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pipeline Creation Language (PCL) is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Pipeline Creation Language (PCL). If not, see <http://www.gnu.org/licenses/>. +# +from visitors.pcl_executor_visitor import PCLExecutorVisitor +from visitors.do_executor_visitor import DoExecutorVisitor + +class Executor(object): + def __init__(self, filename_root, is_instrumented = False): + self.__filename_root = filename_root + self.__is_instrumented = is_instrumented + + def execute(self, component): + executor = DoExecutorVisitor(self.__filename_root, self.__is_instrumented) if component.definition.is_leaf \ + else PCLExecutorVisitor(self.__filename_root, self.__is_instrumented) + component.accept(executor) diff --git a/src/pclc/pclc.py b/src/pclc/pclc.py index fffcd4d..615238c 100755 --- a/src/pclc/pclc.py +++ b/src/pclc/pclc.py @@ -24,7 +24,7 @@ import traceback from optparse import OptionParser from parser.helpers import parse_component from parser.resolver import Resolver -from visitors.executor_visitor import ExecutorVisitor +from parser.executor import Executor __VERSION = "1.1.8" @@ -91,9 +91,9 @@ if __name__ == '__main__': sys.exit(1) # Execute. - executor = ExecutorVisitor(basename_bits[0], options.is_instrumented) + executor = Executor(basename_bits[0], options.is_instrumented) try: - ast.accept(executor) + executor.execute(ast) except Exception as ex: print >> sys.stderr, traceback.format_exc() print >> sys.stderr, "ERROR: Code generation failed: %s" % ex diff --git a/src/pclc/visitors/do_executor_visitor.py b/src/pclc/visitors/do_executor_visitor.py new file mode 100644 index 0000000..cb7d391 --- /dev/null +++ b/src/pclc/visitors/do_executor_visitor.py @@ -0,0 +1,100 @@ +# +# Copyright Capita Translation and Interpreting 2013 +# +# This file is part of Pipeline Creation Language (PCL). +# +# Pipeline Creation Language (PCL) is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pipeline Creation Language (PCL) is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Pipeline Creation Language (PCL). If not, see <http://www.gnu.org/licenses/>. +# +import datetime +import os + +from multimethod import multimethod, multimethodclass +from executor_visitor import ExecutorVisitor +from parser.import_spec import Import +from parser.module import Module +from parser.component import Component +from parser.command import Function, Command, Return, IfCommand + + +@multimethodclass +class DoExecutorVisitor(ExecutorVisitor): + def __init__(self, filename_root, is_instrumented): + ExecutorVisitor.__init__(self, filename_root, "", is_instrumented) + + @multimethod(Module) + def visit(self, module): + self._module = module + + @multimethod(Import) + def visit(self, an_import): + self._write_line("import %s as ____%s" % \ + (an_import.module_name, \ + an_import.alias)) + + @multimethod(Component) + def visit(self, component): + type_formatting_fn = lambda c: str([str(i) for i in c]) + + # The get name function. This is not strictly needed but is included for completeness + self._write_line() + self._write_line() + self._write_function("get_name", + [("return '%s'" % \ + component.identifier, + "")]) + + # The get inputs function + self._write_function("get_inputs", + "return %s" % \ + type_formatting_fn(component.inputs)) + + # The get outputs function + self._write_function("get_outputs", + "return %s" % \ + type_formatting_fn(component.outputs)) + + # The get configuration function + self._write_function("get_configuration", + "return %s" % \ + [str(i) for i in self._module.resolution_symbols['configuration'].keys()]) + + # The configure function + self._write_function("configure", + "return {%s}" % ", ".join(["'%s' : args['%s']" % (i, i) \ + for i in self._module.resolution_symbols['configuration']]), + ["args"]) + + @multimethod(Function) + def visit(self, function): + pass + + @multimethod(Command) + def visit(self, command): + pass + + @multimethod(Return) + def visit(self, ret): + pass + + @multimethod(IfCommand) + def visit(self, if_command): + pass + + @multimethod(IfCommand.ThenBlock) + def visit(self, then_block): + pass + + @multimethod(IfCommand.ElseBlock) + def visit(self, else_block): + pass diff --git a/src/pclc/visitors/executor_visitor.py b/src/pclc/visitors/executor_visitor.py index 1732366..b8e01b8 100644 --- a/src/pclc/visitors/executor_visitor.py +++ b/src/pclc/visitors/executor_visitor.py @@ -19,9 +19,6 @@ import datetime import os -from multimethod import multimethod, multimethodclass -from parser.import_spec import Import -from parser.component import Component from parser.conditional_expressions import ConditionalExpression, \ AndConditionalExpression, \ OrConditionalExpression, \ @@ -34,31 +31,7 @@ from parser.conditional_expressions import ConditionalExpression, \ LessThanEqualToConditionalExpression, \ UnaryConditionalExpression, \ TerminalConditionalExpression -from parser.declaration import Declaration -from parser.expressions import Literal, \ - Identifier, \ - StateIdentifier, \ - Expression, \ - UnaryExpression, \ - CompositionExpression, \ - ParallelWithTupleExpression, \ - ParallelWithScalarExpression, \ - FirstExpression, \ - SecondExpression, \ - SplitExpression, \ - MergeExpression, \ - WireExpression, \ - WireTupleExpression, \ - IfExpression, \ - IdentifierExpression -from parser.mappings import Mapping, \ - TopMapping, \ - BottomMapping, \ - LiteralMapping -from parser.module import Module - -@multimethodclass class ExecutorVisitor(object): __INDENTATION = " " __HEADER = "#\n" \ @@ -66,22 +39,11 @@ class ExecutorVisitor(object): "#\n" \ "# This file was automatically generated by PCLc on\n" \ "# %(datetime)s.\n" \ - "#\n" \ - "from pypeline.helpers.parallel_helpers import cons_function_component, cons_wire, cons_split_wire, cons_unsplit_wire, cons_if_component\n" \ - "from pypeline.core.arrows.kleisli_arrow import KleisliArrow\n" \ - "from pypeline.core.arrows.kleisli_arrow_choice import KleisliArrowChoice\n" \ - "from pypeline.core.types.either import Left, Right\n" \ - "from pypeline.core.types.state import return_\n" - __INSTRUMENTATION_FUNCTIONS = "import sys, threading, datetime\n" \ - "def ____instr_component(component_decl_id, component_id, event, a, s):\n" \ - " print >> sys.stderr, '%s: %s: Component %s is %s %s (id = %s) with input %s and state %s' % (datetime.datetime.now().strftime('%x %X.%f'), threading.current_thread().name, get_name(), event, component_decl_id, component_id, a, {skey : s[skey] for skey in filter(lambda k: k != '____prev_', s.keys())})\n" \ - " return a\n"\ - "def ____instr_component_construction(component_decl_id, component_id, component_config, invoked_component, decl_line_no):\n" \ - " print >> sys.stderr, '%s: %s: Component %s is constructing %s (id = %s) with configuration %s (%s instance declared at line %d)' % (datetime.datetime.now().strftime('%x %X.%f'), threading.current_thread().name, get_name(), component_decl_id, component_id, component_config, invoked_component, decl_line_no)\n" + "#\n" __TEMP_VAR = "____tmp_%d" - def __init__(self, filename_root, is_instrumented = False): - # Check that the __init__.py file exists in the + def __init__(self, filename_root, imports = "", is_instrumented = False): + # Check that the __init__.py file exists in the # working directory if os.path.isfile('__init__.py') is False: open('__init__.py', 'w').close() @@ -91,11 +53,11 @@ class ExecutorVisitor(object): self._var_table = dict() header_args = {'datetime' : \ datetime.datetime.now().strftime("%A %d %B %Y at %H:%M:%S")} - self.__write_line(ExecutorVisitor.__HEADER % header_args) - self.__is_instrumented = is_instrumented - if self.__is_instrumented: + self._write_line((ExecutorVisitor.__HEADER + imports) % header_args) + self._is_instrumented = is_instrumented + if self._is_instrumented: self.__write_line(ExecutorVisitor.__INSTRUMENTATION_FUNCTIONS) - self.__write_line() + self._write_line() self.__conditional_operators = {AndConditionalExpression : 'and', OrConditionalExpression : 'or', XorConditionalExpression : '^', @@ -106,55 +68,51 @@ class ExecutorVisitor(object): GreaterThanEqualToConditionalExpression : '>=', LessThanEqualToConditionalExpression : '<='} - @multimethod(Module) - def visit(self, module): - self._module = module - - def __write_line(self, stuff = ""): + def _write_line(self, stuff = ""): with_indents = "%s%s\n" % (ExecutorVisitor.__INDENTATION * self._indent_level, \ stuff) self._object_file.write(with_indents) - def __write_lines(self, lines): + def _write_lines(self, lines): if isinstance(lines, list) or \ isinstance(lines, tuple): for line, indent_step in lines: - self.__write_line(line) + self._write_line(line) if indent_step == "+": - self.__incr_indent_level() + self._incr_indent_level() elif indent_step == "-": - self.__decr_indent_level() + self._decr_indent_level() else: - self.__write_line(lines) + self._write_line(lines) - def __incr_indent_level(self): + def _incr_indent_level(self): self._indent_level += 1 - def __decr_indent_level(self): + def _decr_indent_level(self): self._indent_level -= 1 if self._indent_level < 1: - self.__reset_indent_level(self) + self._reset_indent_level(self) - def __reset_indent_level(self): + def _reset_indent_level(self): self._indent_level = 0 - def __write_function(self, fn_name, body_lines, arguments = []): - self.__reset_indent_level() - self.__write_line("def %s(%s):" % (fn_name, ", ".join(arguments))) - self.__incr_indent_level() - self.__write_lines(body_lines) - self.__write_line() + def _write_function(self, fn_name, body_lines, arguments = []): + self._reset_indent_level() + self._write_line("def %s(%s):" % (fn_name, ", ".join(arguments))) + self._incr_indent_level() + self._write_lines(body_lines) + self._write_line() - def __get_temp_var(self, expr): + def _get_temp_var(self, expr): temp_var = ExecutorVisitor.__TEMP_VAR % self._tmp_var_no self._var_table[expr] = temp_var self._tmp_var_no += 1 return temp_var - def __lookup_var(self, expr): + def _lookup_var(self, expr): return self._var_table[expr] - def __generate_condition(self, cond_expr): + def _generate_condition(self, cond_expr): # Terminal! if isinstance(cond_expr, TerminalConditionalExpression): terminal = cond_expr.terminal @@ -176,292 +134,7 @@ class ExecutorVisitor(object): right_code = "bool(%s)" % right_code return "(%s %s %s)" % (left_code, op, right_code) elif isinstance(cond_expr, UnaryConditionExpress): - return "(%s)" % self.__generate_condition(cond_expr.expression) + return "(%s)" % self._generate_condition(cond_expr.expression) else: raise ValueError("Unexpected expression in conditional: filename = %s, line no = %d" % \ (cond_expr.filename, cond_expr.lineno)) - - @multimethod(Import) - def visit(self, an_import): - self.__write_line("import %s as ____%s" % \ - (an_import.module_name, \ - an_import.alias)) - - @multimethod(Component) - def visit(self, component): - type_formatting_fn = lambda m: m >= (lambda c: str(([str(i) for i in c[0]], \ - [str(i) for i in c[1]])) \ - if isinstance(c, tuple) else str([str(i) for i in c])) - - # The get name function. This is not strictly needed but is included for completeness - self.__write_line() - self.__write_line() - self.__write_function("get_name", - [("return '%s'" % \ - component.identifier, - "")]) - - # The get inputs function - self.__write_function("get_inputs", - "return %s" % \ - type_formatting_fn(self._module.resolution_symbols['inputs'])) - - # The get outputs function - self.__write_function("get_outputs", - "return %s" % \ - type_formatting_fn(self._module.resolution_symbols['outputs'])) - - # The get configuration function - self.__write_function("get_configuration", - "return %s" % \ - [str(i) for i in self._module.resolution_symbols['configuration'].keys()]) - - # The configure function - self.__write_function("configure", - "return {%s}" % ", ".join(["'%s' : args['%s']" % (i, i) \ - for i in self._module.resolution_symbols['configuration']]), - ["args"]) - - # Component configuration - component_configuration_exprs = ["%s_configuration = %s" % \ - (decl.identifier, - "{%s}" % (", ".join(["'%s' : %s" % \ - (cm.to, \ - "config['%s']" % cm.from_ \ - if isinstance(cm.from_, Identifier) \ - else cm.from_.value.__repr__() \ - if isinstance(cm.from_.value, str) \ - else m.literal) \ - for cm in decl.configuration_mappings]))) \ - for decl in self._module.resolution_symbols['components']] - # The initialise function - component_initialisations = ["%(id)s = ____%(comp_alias)s.initialise(____%(comp_alias)s.configure(%(id)s_configuration))" % \ - {'id' : decl.identifier, - 'comp_alias' : decl.component_alias} \ - for decl in self._module.resolution_symbols['components']] - # Guard against a module returning a non-Kleisli arrow type from initialise - component_decl_guards = ["%(id)s = %(id)s " \ - "if isinstance(%(id)s, KleisliArrow) " \ - "else cons_function_component(%(id)s)" % \ - {'id' : decl.identifier} \ - for decl in self._module.resolution_symbols['components']] - # Wrap this component with any state conversion components - state_wrappers = ["%(id)s = ((cons_function_component(lambda a, s: a, state_mutator = lambda s: {%(state)s, '____prev_' : s})) >> %(id)s) >> cons_function_component(lambda a, s: a, state_mutator = lambda s: s['____prev_'])" % \ - {'id' : decl.identifier, - 'state' : "%s" % (", ".join(["'%s' : %s" % \ - (cm.to, \ - "s['%s']" % cm.from_ \ - if isinstance(cm.from_, Identifier) \ - else cm.from_.value.__repr__() \ - if isinstance(cm.from_.value, str) \ - else m.literal) \ - for cm in decl.configuration_mappings]))} if decl.configuration_mappings else "" - for decl in self._module.resolution_symbols['components']] - # Do we generate instrumented code? - if self.__is_instrumented: - # Constructed component identifier - component_id_exprs = ["%(id)s_id = id(%(id)s)" % \ - {'id' : decl.identifier} \ - for decl in self._module.resolution_symbols['components']] - # Instrument component construction - component_init_instrumentation_exprs = ["____instr_component_construction('%(id)s', %(id)s_id, %(id)s_configuration, ____%(comp_alias)s.get_name(), %(decl_line_no)d)" % \ - {'id' : decl.identifier, - 'comp_alias' : decl.component_alias, - 'decl_line_no' : decl.lineno} \ - for decl in self._module.resolution_symbols['components']] - # Wrap with instrumentation - component_instrumentation_exprs = ["%(id)s = ((cons_function_component(lambda a, s: ____instr_component('%(id)s', %(id)s_id, 'starting', a, s)) >> " \ - "%(id)s) >> " \ - "cons_function_component(lambda a, s: ____instr_component('%(id)s', %(id)s_id, 'finishing', a, s)))" % \ - {'id' : decl.identifier, - 'comp_alias' : decl.component_alias, - 'decl_line_no' : decl.lineno} \ - for decl in self._module.resolution_symbols['components']] - # Generated code zipper - decl_zipper = zip(component_configuration_exprs, - component_initialisations, - component_decl_guards, - component_id_exprs, - component_init_instrumentation_exprs, - component_instrumentation_exprs, - state_wrappers) - else: - decl_zipper = zip(component_configuration_exprs, - component_initialisations, - component_decl_guards, - state_wrappers) - initialise_fn = [t for triple in decl_zipper for t in triple] - # Store variables in variable table - for decl in self._module.resolution_symbols['components']: - self._var_table[IdentifierExpression(None, - 0, - decl.identifier, - Expression(None, 0))] = str(decl.identifier) - self.__write_function("initialise", - [(smt, "") for smt in initialise_fn], - ["config"]) - - @multimethod(Declaration) - def visit(self, decl): - pass - - @multimethod(object) - def visit(self, nowt): - for expr in self._var_table.keys(): - if expr.parent == None: - self.__write_line() - self.__write_line("return %s" % self.__lookup_var(expr)) - break - self._object_file.close() - - @multimethod(UnaryExpression) - def visit(self, unary_expr): - var_name = self._var_table.pop(unary_expr.expression) - self._var_table[unary_expr] = var_name - - @multimethod(CompositionExpression) - def visit(self, comp_expr): - self.__write_line("%s = %s >> %s" % \ - (self.__get_temp_var(comp_expr), - self.__lookup_var(comp_expr.left), - self.__lookup_var(comp_expr.right))) - - @multimethod(ParallelWithTupleExpression) - def visit(self, para_tuple_expr): - self.__write_line("%s = %s ** %s" % \ - (self.__get_temp_var(para_tuple_expr), - self.__lookup_var(para_tuple_expr.left), - self.__lookup_var(para_tuple_expr.right))) - - @multimethod(ParallelWithScalarExpression) - def visit(self, para_scalar_expr): - self.__write_line("%s = %s & %s" % \ - (self.__get_temp_var(para_scalar_expr), - self.__lookup_var(para_scalar_expr.left), - self.__lookup_var(para_scalar_expr.right))) - - @multimethod(FirstExpression) - def visit(self, first_expr): - self.__write_line("%s = %s.first()" % \ - (self.__get_temp_var(first_expr), - self.__lookup_var(first_expr.expression))) - - @multimethod(SecondExpression) - def visit(self, second_expr): - self.__write_line("%s = %s.second()" % \ - (self.__get_temp_var(second_expr), - self.__lookup_var(second_expr.expression))) - - @multimethod(SplitExpression) - def visit(self, split_expr): - self.__write_line("%s = cons_split_wire()" % \ - self.__get_temp_var(split_expr)) - - @multimethod(MergeExpression) - def visit(self, merge_expr): - top_mappings = ["'%s' : t['%s']" % (m.to, m.from_) \ - for m in merge_expr.top_mapping \ - if str(m.to) != '_'] - bottom_mappings = ["'%s' : b['%s']" % (m.to, m.from_) \ - for m in merge_expr.bottom_mapping \ - if str(m.to) != '_'] - literal_mappings = ["'%s' : %s" % \ - (m.to, \ - m.literal.value.__repr__() if isinstance(m.literal.value, str) else m.literal) \ - for m in merge_expr.literal_mapping] - mapping = ", ".join(top_mappings + bottom_mappings + literal_mappings) - self.__write_line("%s = cons_unsplit_wire(lambda t, b: {%s})" % \ - (self.__get_temp_var(merge_expr), - mapping)) - - @staticmethod - def __build_wire_expr(mapping): - dict_repr = ["'%s' : %s" % (m.to, - "a['%s']" % (m.from_) if isinstance(m, Mapping) \ - else (m.literal.value.__repr__() if isinstance(m.literal.value, str) else m.literal)) \ - for m in mapping \ - if str(m.to) != '_'] - return "cons_wire(lambda a, s: {%s})" % (", ".join(dict_repr)) - - @multimethod(WireExpression) - def visit(self, wire_expr): - self.__write_line("%s = %s" % (self.__get_temp_var(wire_expr), ExecutorVisitor.__build_wire_expr(wire_expr.mapping))) - - @multimethod(WireTupleExpression) - def visit(self, wire_tuple_expr): - self.__write_line("%s = %s ** %s" % \ - (self.__get_temp_var(wire_tuple_expr), - ExecutorVisitor.__build_wire_expr(wire_tuple_expr.top_mapping), - ExecutorVisitor.__build_wire_expr(wire_tuple_expr.bottom_mapping))) - - @multimethod(IfExpression) - def visit(self, if_expr): - self.__write_line("%s = cons_if_component(lambda a, s: %s, %s, %s)" % \ - (self.__get_temp_var(if_expr), - self.__generate_condition(if_expr.condition), - self.__lookup_var(if_expr.then), - self.__lookup_var(if_expr.else_))) - - @multimethod(AndConditionalExpression) - def visit(self, and_cond_expr): - pass - - @multimethod(OrConditionalExpression) - def visit(self, or_cond_expr): - pass - - @multimethod(XorConditionalExpression) - def visit(self, xor_cond_expr): - pass - - @multimethod(EqualsConditionalExpression) - def visit(self, eq_cond_expr): - pass - - @multimethod(NotEqualsConditionalExpression) - def visit(self, ne_cond_expr): - pass - - @multimethod(GreaterThanConditionalExpression) - def visit(self, gt_cond_expr): - pass - - @multimethod(LessThanConditionalExpression) - def visit(self, lt_cond_expr): - pass - - @multimethod(GreaterThanEqualToConditionalExpression) - def visit(self, gte_cond_expr): - pass - - @multimethod(LessThanEqualToConditionalExpression) - def visit(self, lte_cond_expr): - pass - - @multimethod(UnaryConditionalExpression) - def visit(self, unary_cond_expr): - pass - - @multimethod(TerminalConditionalExpression) - def visit(self, term_cond_expr): - pass - - @multimethod(Mapping) - def visit(self, mapping): - pass - - @multimethod(TopMapping) - def visit(self, mapping): - pass - - @multimethod(BottomMapping) - def visit(self, mapping): - pass - - @multimethod(LiteralMapping) - def visit(self, mapping): - pass - - @multimethod(IdentifierExpression) - def visit(self, iden_expr): - pass diff --git a/src/pclc/visitors/first_pass_resolver_visitor.py b/src/pclc/visitors/first_pass_resolver_visitor.py index dc8370b..d8c6573 100644 --- a/src/pclc/visitors/first_pass_resolver_visitor.py +++ b/src/pclc/visitors/first_pass_resolver_visitor.py @@ -383,6 +383,14 @@ class FirstPassResolverVisitor(ResolverVisitor): component.declarations, component.module.resolution_symbols['imports']) + self._add_errors("ERROR: %(filename)s at line %(lineno)d, only one input is supported", + [component.inputs[0]] if isinstance(component.inputs, tuple) and component.is_leaf else [], + lambda i: {'filename' : i.filename, + 'lineno' : i.lineno}) + self._add_errors("ERROR: %(filename)s at line %(lineno)d, only one output is supported", + [component.output[0]] if isinstance(component.outputs, tuple) and component.is_leaf else [], + lambda o: {'filename' : o.filename, + 'lineno' : o.lineno}) self._add_errors("ERROR: %(filename)s at line %(lineno)d, input " \ "declaration contains duplicate identifier %(entity)s", duplicate_inputs, diff --git a/src/pclc/visitors/pcl_executor_visitor.py b/src/pclc/visitors/pcl_executor_visitor.py new file mode 100644 index 0000000..8eec33e --- /dev/null +++ b/src/pclc/visitors/pcl_executor_visitor.py @@ -0,0 +1,366 @@ +# +# Copyright Capita Translation and Interpreting 2013 +# +# This file is part of Pipeline Creation Language (PCL). +# +# Pipeline Creation Language (PCL) is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pipeline Creation Language (PCL) is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Pipeline Creation Language (PCL). If not, see <http://www.gnu.org/licenses/>. +# +import datetime +import os + +from multimethod import multimethod, multimethodclass +from parser.import_spec import Import +from parser.component import Component +from parser.conditional_expressions import ConditionalExpression, \ + AndConditionalExpression, \ + OrConditionalExpression, \ + XorConditionalExpression, \ + EqualsConditionalExpression, \ + NotEqualsConditionalExpression, \ + GreaterThanConditionalExpression, \ + LessThanConditionalExpression, \ + GreaterThanEqualToConditionalExpression, \ + LessThanEqualToConditionalExpression, \ + UnaryConditionalExpression, \ + TerminalConditionalExpression +from parser.declaration import Declaration +from parser.expressions import Literal, \ + Identifier, \ + StateIdentifier, \ + Expression, \ + UnaryExpression, \ + CompositionExpression, \ + ParallelWithTupleExpression, \ + ParallelWithScalarExpression, \ + FirstExpression, \ + SecondExpression, \ + SplitExpression, \ + MergeExpression, \ + WireExpression, \ + WireTupleExpression, \ + IfExpression, \ + IdentifierExpression +from parser.mappings import Mapping, \ + TopMapping, \ + BottomMapping, \ + LiteralMapping +from parser.module import Module +from executor_visitor import ExecutorVisitor + + +@multimethodclass +class PCLExecutorVisitor(ExecutorVisitor): + __IMPORTS = "from pypeline.helpers.parallel_helpers import cons_function_component, cons_wire, cons_split_wire, cons_unsplit_wire, cons_if_component\n" \ + "from pypeline.core.arrows.kleisli_arrow import KleisliArrow\n" \ + "from pypeline.core.arrows.kleisli_arrow_choice import KleisliArrowChoice\n" \ + "from pypeline.core.types.either import Left, Right\n" \ + "from pypeline.core.types.state import return_\n" + __INSTRUMENTATION_FUNCTIONS = "import sys, threading, datetime\n" \ + "def ____instr_component(component_decl_id, component_id, event, a, s):\n" \ + " print >> sys.stderr, '%s: %s: Component %s is %s %s (id = %s) with input %s and state %s' % (datetime.datetime.now().strftime('%x %X.%f'), threading.current_thread().name, get_name(), event, component_decl_id, component_id, a, {skey : s[skey] for skey in filter(lambda k: k != '____prev_', s.keys())})\n" \ + " return a\n"\ + "def ____instr_component_construction(component_decl_id, component_id, component_config, invoked_component, decl_line_no):\n" \ + " print >> sys.stderr, '%s: %s: Component %s is constructing %s (id = %s) with configuration %s (%s instance declared at line %d)' % (datetime.datetime.now().strftime('%x %X.%f'), threading.current_thread().name, get_name(), component_decl_id, component_id, component_config, invoked_component, decl_line_no)\n" + + def __init__(self, filename_root, is_instrumented = False): + ExecutorVisitor.__init__(self, filename_root, PCLExecutorVisitor.__IMPORTS, is_instrumented) + + @multimethod(Module) + def visit(self, module): + self._module = module + + @multimethod(Import) + def visit(self, an_import): + self._write_line("import %s as ____%s" % \ + (an_import.module_name, \ + an_import.alias)) + + @multimethod(Component) + def visit(self, component): + type_formatting_fn = lambda m: m >= (lambda c: str(([str(i) for i in c[0]], \ + [str(i) for i in c[1]])) \ + if isinstance(c, tuple) else str([str(i) for i in c])) + + # The get name function. This is not strictly needed but is included for completeness + self._write_line() + self._write_line() + self._write_function("get_name", + [("return '%s'" % \ + component.identifier, + "")]) + + # The get inputs function + self._write_function("get_inputs", + "return %s" % \ + type_formatting_fn(self._module.resolution_symbols['inputs'])) + + # The get outputs function + self._write_function("get_outputs", + "return %s" % \ + type_formatting_fn(self._module.resolution_symbols['outputs'])) + + # The get configuration function + self._write_function("get_configuration", + "return %s" % \ + [str(i) for i in self._module.resolution_symbols['configuration'].keys()]) + + # The configure function + self._write_function("configure", + "return {%s}" % ", ".join(["'%s' : args['%s']" % (i, i) \ + for i in self._module.resolution_symbols['configuration']]), + ["args"]) + + # Component configuration + component_configuration_exprs = ["%s_configuration = %s" % \ + (decl.identifier, + "{%s}" % (", ".join(["'%s' : %s" % \ + (cm.to, \ + "config['%s']" % cm.from_ \ + if isinstance(cm.from_, Identifier) \ + else cm.from_.value.__repr__() \ + if isinstance(cm.from_.value, str) \ + else m.literal) \ + for cm in decl.configuration_mappings]))) \ + for decl in self._module.resolution_symbols['components']] + # The initialise function + component_initialisations = ["%(id)s = ____%(comp_alias)s.initialise(____%(comp_alias)s.configure(%(id)s_configuration))" % \ + {'id' : decl.identifier, + 'comp_alias' : decl.component_alias} \ + for decl in self._module.resolution_symbols['components']] + # Guard against a module returning a non-Kleisli arrow type from initialise + component_decl_guards = ["%(id)s = %(id)s " \ + "if isinstance(%(id)s, KleisliArrow) " \ + "else cons_function_component(%(id)s)" % \ + {'id' : decl.identifier} \ + for decl in self._module.resolution_symbols['components']] + # Wrap this component with any state conversion components + state_wrappers = ["%(id)s = ((cons_function_component(lambda a, s: a, state_mutator = lambda s: {%(state)s, '____prev_' : s})) >> %(id)s) >> cons_function_component(lambda a, s: a, state_mutator = lambda s: s['____prev_'])" % \ + {'id' : decl.identifier, + 'state' : "%s" % (", ".join(["'%s' : %s" % \ + (cm.to, \ + "s['%s']" % cm.from_ \ + if isinstance(cm.from_, Identifier) \ + else cm.from_.value.__repr__() \ + if isinstance(cm.from_.value, str) \ + else m.literal) \ + for cm in decl.configuration_mappings]))} if decl.configuration_mappings else "" + for decl in self._module.resolution_symbols['components']] + # Do we generate instrumented code? + if self._is_instrumented: + # Constructed component identifier + component_id_exprs = ["%(id)s_id = id(%(id)s)" % \ + {'id' : decl.identifier} \ + for decl in self._module.resolution_symbols['components']] + # Instrument component construction + component_init_instrumentation_exprs = ["____instr_component_construction('%(id)s', %(id)s_id, %(id)s_configuration, ____%(comp_alias)s.get_name(), %(decl_line_no)d)" % \ + {'id' : decl.identifier, + 'comp_alias' : decl.component_alias, + 'decl_line_no' : decl.lineno} \ + for decl in self._module.resolution_symbols['components']] + # Wrap with instrumentation + component_instrumentation_exprs = ["%(id)s = ((cons_function_component(lambda a, s: ____instr_component('%(id)s', %(id)s_id, 'starting', a, s)) >> " \ + "%(id)s) >> " \ + "cons_function_component(lambda a, s: ____instr_component('%(id)s', %(id)s_id, 'finishing', a, s)))" % \ + {'id' : decl.identifier, + 'comp_alias' : decl.component_alias, + 'decl_line_no' : decl.lineno} \ + for decl in self._module.resolution_symbols['components']] + # Generated code zipper + decl_zipper = zip(component_configuration_exprs, + component_initialisations, + component_decl_guards, + component_id_exprs, + component_init_instrumentation_exprs, + component_instrumentation_exprs, + state_wrappers) + else: + decl_zipper = zip(component_configuration_exprs, + component_initialisations, + component_decl_guards, + state_wrappers) + initialise_fn = [t for triple in decl_zipper for t in triple] + # Store variables in variable table + for decl in self._module.resolution_symbols['components']: + self._var_table[IdentifierExpression(None, + 0, + decl.identifier, + Expression(None, 0))] = str(decl.identifier) + self._write_function("initialise", + [(smt, "") for smt in initialise_fn], + ["config"]) + + @multimethod(Declaration) + def visit(self, decl): + pass + + @multimethod(object) + def visit(self, nowt): + for expr in self._var_table.keys(): + if expr.parent == None: + self._write_line() + self._write_line("return %s" % self._lookup_var(expr)) + break + self._object_file.close() + + @multimethod(UnaryExpression) + def visit(self, unary_expr): + var_name = self._var_table.pop(unary_expr.expression) + self._var_table[unary_expr] = var_name + + @multimethod(CompositionExpression) + def visit(self, comp_expr): + self._write_line("%s = %s >> %s" % \ + (self._get_temp_var(comp_expr), + self._lookup_var(comp_expr.left), + self._lookup_var(comp_expr.right))) + + @multimethod(ParallelWithTupleExpression) + def visit(self, para_tuple_expr): + self._write_line("%s = %s ** %s" % \ + (self._get_temp_var(para_tuple_expr), + self._lookup_var(para_tuple_expr.left), + self._lookup_var(para_tuple_expr.right))) + + @multimethod(ParallelWithScalarExpression) + def visit(self, para_scalar_expr): + self._write_line("%s = %s & %s" % \ + (self._get_temp_var(para_scalar_expr), + self._lookup_var(para_scalar_expr.left), + self._lookup_var(para_scalar_expr.right))) + + @multimethod(FirstExpression) + def visit(self, first_expr): + self._write_line("%s = %s.first()" % \ + (self._get_temp_var(first_expr), + self._lookup_var(first_expr.expression))) + + @multimethod(SecondExpression) + def visit(self, second_expr): + self._write_line("%s = %s.second()" % \ + (self._get_temp_var(second_expr), + self._lookup_var(second_expr.expression))) + + @multimethod(SplitExpression) + def visit(self, split_expr): + self._write_line("%s = cons_split_wire()" % \ + self._get_temp_var(split_expr)) + + @multimethod(MergeExpression) + def visit(self, merge_expr): + top_mappings = ["'%s' : t['%s']" % (m.to, m.from_) \ + for m in merge_expr.top_mapping \ + if str(m.to) != '_'] + bottom_mappings = ["'%s' : b['%s']" % (m.to, m.from_) \ + for m in merge_expr.bottom_mapping \ + if str(m.to) != '_'] + literal_mappings = ["'%s' : %s" % \ + (m.to, \ + m.literal.value.__repr__() if isinstance(m.literal.value, str) else m.literal) \ + for m in merge_expr.literal_mapping] + mapping = ", ".join(top_mappings + bottom_mappings + literal_mappings) + self.__write_line("%s = cons_unsplit_wire(lambda t, b: {%s})" % \ + (self._get_temp_var(merge_expr), + mapping)) + + @staticmethod + def __build_wire_expr(mapping): + dict_repr = ["'%s' : %s" % (m.to, + "a['%s']" % (m.from_) if isinstance(m, Mapping) \ + else (m.literal.value.__repr__() if isinstance(m.literal.value, str) else m.literal)) \ + for m in mapping \ + if str(m.to) != '_'] + return "cons_wire(lambda a, s: {%s})" % (", ".join(dict_repr)) + + @multimethod(WireExpression) + def visit(self, wire_expr): + self._write_line("%s = %s" % (self._get_temp_var(wire_expr), PCLExecutorVisitor.__build_wire_expr(wire_expr.mapping))) + + @multimethod(WireTupleExpression) + def visit(self, wire_tuple_expr): + self.__write_line("%s = %s ** %s" % \ + (self._get_temp_var(wire_tuple_expr), + ExecutorVisitor.__build_wire_expr(wire_tuple_expr.top_mapping), + ExecutorVisitor.__build_wire_expr(wire_tuple_expr.bottom_mapping))) + + @multimethod(IfExpression) + def visit(self, if_expr): + self.__write_line("%s = cons_if_component(lambda a, s: %s, %s, %s)" % \ + (self._get_temp_var(if_expr), + self._generate_condition(if_expr.condition), + self._lookup_var(if_expr.then), + self._lookup_var(if_expr.else_))) + + @multimethod(AndConditionalExpression) + def visit(self, and_cond_expr): + pass + + @multimethod(OrConditionalExpression) + def visit(self, or_cond_expr): + pass + + @multimethod(XorConditionalExpression) + def visit(self, xor_cond_expr): + pass + + @multimethod(EqualsConditionalExpression) + def visit(self, eq_cond_expr): + pass + + @multimethod(NotEqualsConditionalExpression) + def visit(self, ne_cond_expr): + pass + + @multimethod(GreaterThanConditionalExpression) + def visit(self, gt_cond_expr): + pass + + @multimethod(LessThanConditionalExpression) + def visit(self, lt_cond_expr): + pass + + @multimethod(GreaterThanEqualToConditionalExpression) + def visit(self, gte_cond_expr): + pass + + @multimethod(LessThanEqualToConditionalExpression) + def visit(self, lte_cond_expr): + pass + + @multimethod(UnaryConditionalExpression) + def visit(self, unary_cond_expr): + pass + + @multimethod(TerminalConditionalExpression) + def visit(self, term_cond_expr): + pass + + @multimethod(Mapping) + def visit(self, mapping): + pass + + @multimethod(TopMapping) + def visit(self, mapping): + pass + + @multimethod(BottomMapping) + def visit(self, mapping): + pass + + @multimethod(LiteralMapping) + def visit(self, mapping): + pass + + @multimethod(IdentifierExpression) + def visit(self, iden_expr): + pass |