class CodeGenerator: def __init__(self): self.library = CodeLibrary() self.pipelines = [] def format_pipeline(self, pipeline): return " | ".join(self._format_unit(*u) for u in pipeline) def init(self, script=False, **namespace): self.add_vars( script=script, time=time.asctime(), argv=sys.argv[1:], version=__version__, **namespace) def init(self, arguments, filenames): self.arguments = arguments self.namespace = attrdict( pipelines=self.pipelines, filenames=filenames, script=self.arguments.dump is not None, output=self.arguments.output, quiet=self.arguments.quiet, stats=self.arguments.stats, decode=self.arguments.decode, encode=self.arguments.encode, time=time.asctime(), argv=sys.argv[1:], version=__version__) def add_pipeline(self, pipeline): self.unit_serial = 0 self.finalized = False self.pipelines.append(attrdict(units=[])) for unit_number, (name, arguments, handler, handler_arguments) in enumerate(pipeline): self.add_unit(name, arguments, handler, handler_arguments, unit_number) if not self.finalized: self.add_unit("write") self.pipelines[-1]["serial"] = len(self.pipelines) - 1 self.pipelines[-1]["formatted"] = " | ".join(u["formatted"] for u in self.pipelines[-1]["units"]) def add_unit(self, name, arguments=(), handler=None, handler_arguments=(), unit_number=None): unit = self.library.get_unit(name) if self.unit_serial == 0: if not unit.type == "input": self.add_unit("read") elif unit.type == "input": # FIXME use different exception raise ParserError("duplicate use of input unit (%d:%s)" % (unit_number, name)) if unit.type == "output": self.add_unit("output_stats") if self.finalized: # FIXME use different exception raise ParserError("duplicate use of output unit (%d:%s)" % (unit_number, name)) self.finalized = True elif self.finalized: raise ParserError("cannot use unit (%d:%s) after output unit" % (unit_number, name)) # Prepare namespace. # FIXME harmonize the variable names. self.pipelines[-1]["units"].append(attrdict( handler=handler, handler_code=handler_arguments[0] if handler_arguments else None, ignore_exc=self._format_args(handler_arguments) if handler_arguments else None, expr_code=arguments[0] if arguments else None, name=unit.name, serial=self.unit_serial, real_serial=unit_number, formatted=self._format_unit(unit.name, arguments, handler, handler_arguments), argno=len(arguments), args=arguments, args_formatted=self._format_args(arguments))) self.unit_serial += 1 if self.unit_serial == 1: self.add_unit("input_stats") def _format_unit(self, name, arguments, handler, handler_arguments): args = [" ".join(a.split()) for a in arguments] hargs = [" ".join(a.split()) for a in handler_arguments] if handler is not None: return "%s(%s) %s (%s)" % (name, ", ".join(args), handler, ", ".join(hargs)) else: return "%s(%s)" % (name, ", ".join(args)) def _format_args(self, arguments): if len(arguments) == 0: return "()" elif len(arguments) == 1: return "(" + arguments[0] + ",)" else: return "(" + ", ".join(arguments) + ")" def format_epilog(self): lines = [] for name, unit in self.library.get_units(): lines.append(name) lines += _indent(unit.description.splitlines(), 4) lines.append("") return "\n".join(lines) def assemble(self): if not self.pipelines: self.add_pipeline("") # FIXME #self.lines.insert(0, "{!set used_units %s}" % self._format_args([repr(u) for u in sorted(self.used_units)])) self.namespace["pls_formatted"] = ", ".join("pipeline_%02d()" % i for i in range(len(self.pipelines))) yield from Template(self.library, self.namespace, "skeleton").parse()