diff --git a/artiq/transforms/lower_time.py b/artiq/transforms/lower_time.py deleted file mode 100644 index 904d73834..000000000 --- a/artiq/transforms/lower_time.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -This transform implements time management functions (delay_mu/now_mu/at_mu) -using an accumulator 'now' and simple replacement rules: - - delay_mu(t) -> now += t - now_mu() -> now - at_mu(t) -> now = t - -The function delay(), that uses seconds, must be lowered to delay_mu() before -invoking this transform. -The accumulator is initialized to an int64 value at the beginning of the -output function. -""" - -import ast - - -class _TimeLowerer(ast.NodeTransformer): - def visit_Call(self, node): - if node.func.id == "now_mu": - return ast.copy_location(ast.Name("now", ast.Load()), node) - else: - self.generic_visit(node) - return node - - def visit_Expr(self, node): - r = node - if isinstance(node.value, ast.Call): - funcname = node.value.func.id - if funcname == "delay_mu": - r = ast.copy_location( - ast.AugAssign(target=ast.Name("now", ast.Store()), - op=ast.Add(), - value=node.value.args[0]), - node) - elif funcname == "at_mu": - r = ast.copy_location( - ast.Assign(targets=[ast.Name("now", ast.Store())], - value=node.value.args[0]), - node) - self.generic_visit(r) - return r - - -def lower_time(func_def): - _TimeLowerer().visit(func_def) - call_init = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("now_init")], keywords=[]) - stmt_init = ast.Assign(targets=[ast.Name("now", ast.Store())], - value=call_init) - call_save = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("now_save"), ast.Name("now", ast.Load())], keywords=[]) - stmt_save = ast.Expr(call_save) - func_def.body = [ - stmt_init, - ast.Try(body=func_def.body, - handlers=[], - orelse=[], - finalbody=[stmt_save]) - ] diff --git a/artiq/transforms/quantize_time.py b/artiq/transforms/quantize_time.py deleted file mode 100644 index e4125d624..000000000 --- a/artiq/transforms/quantize_time.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -This transform turns calls to delay() that use non-integer time -expressed in seconds into calls to delay_mu() that use int64 time -expressed in multiples of ref_period. - -It does so by inserting multiplication/division/rounding operations around -those calls. - -The seconds_to_mu and mu_to_seconds core language functions are also -implemented here, as well as watchdog to syscall conversion. -""" - -import ast - -from artiq.transforms.tools import value_to_ast - - -def _seconds_to_mu(ref_period, node): - divided = ast.copy_location( - ast.BinOp(left=node, - op=ast.Div(), - right=value_to_ast(ref_period)), - node) - return ast.copy_location( - ast.Call(func=ast.Name("round64", ast.Load()), - args=[divided], keywords=[]), - divided) - - -def _mu_to_seconds(ref_period, node): - return ast.copy_location( - ast.BinOp(left=node, - op=ast.Mult(), - right=value_to_ast(ref_period)), - node) - - -class _TimeQuantizer(ast.NodeTransformer): - def __init__(self, ref_period): - self.ref_period = ref_period - self.watchdog_id_counter = 0 - - def visit_Call(self, node): - funcname = node.func.id - if funcname == "delay": - node.func.id = "delay_mu" - if (isinstance(node.args[0], ast.Call) - and node.args[0].func.id == "mu_to_seconds"): - # optimize: - # delay(mu_to_seconds(x)) -> delay_mu(x) - node.args[0] = self.visit(node.args[0].args[0]) - else: - node.args[0] = _seconds_to_mu(self.ref_period, - self.visit(node.args[0])) - return node - elif funcname == "seconds_to_mu": - return _seconds_to_mu(self.ref_period, - self.visit(node.args[0])) - elif funcname == "mu_to_seconds": - return _mu_to_seconds(self.ref_period, - self.visit(node.args[0])) - else: - self.generic_visit(node) - return node - - def visit_With(self, node): - self.generic_visit(node) - if (isinstance(node.items[0].context_expr, ast.Call) - and node.items[0].context_expr.func.id == "watchdog"): - - idname = "__watchdog_id_" + str(self.watchdog_id_counter) - self.watchdog_id_counter += 1 - - time = ast.BinOp(left=node.items[0].context_expr.args[0], - op=ast.Mult(), - right=ast.Num(1000)) - time_int = ast.Call( - func=ast.Name("round", ast.Load()), - args=[time], keywords=[]) - syscall_set = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("watchdog_set"), time_int], keywords=[]) - stmt_set = ast.copy_location( - ast.Assign(targets=[ast.Name(idname, ast.Store())], - value=syscall_set), - node) - - syscall_clear = ast.Call( - func=ast.Name("syscall", ast.Load()), - args=[ast.Str("watchdog_clear"), - ast.Name(idname, ast.Load())], keywords=[]) - stmt_clear = ast.copy_location(ast.Expr(syscall_clear), node) - - node.items[0] = ast.withitem( - context_expr=ast.Name(id="sequential", - ctx=ast.Load()), - optional_vars=None) - node.body = [ - stmt_set, - ast.Try(body=node.body, - handlers=[], - orelse=[], - finalbody=[stmt_clear]) - ] - return node - - -def quantize_time(func_def, ref_period): - _TimeQuantizer(ref_period).visit(func_def) diff --git a/artiq/transforms/tools.py b/artiq/transforms/tools.py deleted file mode 100644 index 557a60221..000000000 --- a/artiq/transforms/tools.py +++ /dev/null @@ -1,139 +0,0 @@ -import ast -from fractions import Fraction - -from artiq.language import core as core_language -from artiq.language import units - - -embeddable_funcs = ( - core_language.delay_mu, core_language.at_mu, core_language.now_mu, - core_language.delay, - core_language.seconds_to_mu, core_language.mu_to_seconds, - core_language.syscall, core_language.watchdog, - range, bool, int, float, round, len, - core_language.int64, core_language.round64, - Fraction, core_language.EncodedException -) -embeddable_func_names = {func.__name__ for func in embeddable_funcs} - - -def is_embeddable(func): - for ef in embeddable_funcs: - if func is ef: - return True - return False - - -def eval_ast(expr, symdict=dict()): - if not isinstance(expr, ast.Expression): - expr = ast.copy_location(ast.Expression(expr), expr) - ast.fix_missing_locations(expr) - code = compile(expr, "", "eval") - return eval(code, symdict) - - -class NotASTRepresentable(Exception): - pass - - -def value_to_ast(value): - if isinstance(value, core_language.int64): # must be before int - return ast.Call( - func=ast.Name("int64", ast.Load()), - args=[ast.Num(int(value))], keywords=[]) - elif isinstance(value, bool) or value is None: - # must also be before int - # isinstance(True/False, int) == True - return ast.NameConstant(value) - elif isinstance(value, (int, float)): - return ast.Num(value) - elif isinstance(value, Fraction): - return ast.Call( - func=ast.Name("Fraction", ast.Load()), - args=[ast.Num(value.numerator), ast.Num(value.denominator)], keywords=[]) - elif isinstance(value, str): - return ast.Str(value) - elif isinstance(value, list): - elts = [value_to_ast(elt) for elt in value] - return ast.List(elts, ast.Load()) - else: - for kg in core_language.kernel_globals: - if value is getattr(core_language, kg): - return ast.Name(kg, ast.Load()) - raise NotASTRepresentable(str(value)) - - -class NotConstant(Exception): - pass - - -def eval_constant(node): - if isinstance(node, ast.Num): - return node.n - elif isinstance(node, ast.Str): - return node.s - elif isinstance(node, ast.NameConstant): - return node.value - elif isinstance(node, ast.Call): - funcname = node.func.id - if funcname == "int64": - return core_language.int64(eval_constant(node.args[0])) - elif funcname == "Fraction": - numerator = eval_constant(node.args[0]) - denominator = eval_constant(node.args[1]) - return Fraction(numerator, denominator) - else: - raise NotConstant - else: - raise NotConstant - - -_replaceable_funcs = { - "bool", "int", "float", "round", - "int64", "round64", "Fraction", - "seconds_to_mu", "mu_to_seconds" -} - - -def _is_ref_transparent(dependencies, expr): - if isinstance(expr, (ast.NameConstant, ast.Num, ast.Str)): - return True - elif isinstance(expr, ast.Name): - dependencies.add(expr.id) - return True - elif isinstance(expr, ast.UnaryOp): - return _is_ref_transparent(dependencies, expr.operand) - elif isinstance(expr, ast.BinOp): - return (_is_ref_transparent(dependencies, expr.left) - and _is_ref_transparent(dependencies, expr.right)) - elif isinstance(expr, ast.BoolOp): - return all(_is_ref_transparent(dependencies, v) for v in expr.values) - elif isinstance(expr, ast.Call): - return (expr.func.id in _replaceable_funcs and - all(_is_ref_transparent(dependencies, arg) - for arg in expr.args)) - else: - return False - - -def is_ref_transparent(expr): - dependencies = set() - if _is_ref_transparent(dependencies, expr): - return True, dependencies - else: - return False, None - - -class _NodeCounter(ast.NodeVisitor): - def __init__(self): - self.count = 0 - - def generic_visit(self, node): - self.count += 1 - ast.NodeVisitor.generic_visit(self, node) - - -def count_all_nodes(node): - nc = _NodeCounter() - nc.visit(node) - return nc.count diff --git a/artiq/transforms/unparse.py b/artiq/transforms/unparse.py deleted file mode 100644 index c796b58e8..000000000 --- a/artiq/transforms/unparse.py +++ /dev/null @@ -1,575 +0,0 @@ -import sys -import ast - - -# Large float and imaginary literals get turned into infinities in the AST. -# We unparse those infinities to INFSTR. -INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1) - - -def _interleave(inter, f, seq): - """Call f on each item in seq, calling inter() in between. - """ - seq = iter(seq) - try: - f(next(seq)) - except StopIteration: - pass - else: - for x in seq: - inter() - f(x) - - -class _Unparser: - """Methods in this class recursively traverse an AST and - output source code for the abstract syntax; original formatting - is disregarded. """ - - def __init__(self, tree): - """Print the source for tree to the "result" string.""" - self.result = "" - self._indent = 0 - self.dispatch(tree) - self.result += "\n" - - def fill(self, text=""): - "Indent a piece of text, according to the current indentation level" - self.result += "\n"+" "*self._indent + text - - def write(self, text): - "Append a piece of text to the current line." - self.result += text - - def enter(self): - "Print ':', and increase the indentation." - self.write(":") - self._indent += 1 - - def leave(self): - "Decrease the indentation level." - self._indent -= 1 - - def dispatch(self, tree): - "Dispatcher function, dispatching tree type T to method _T." - if isinstance(tree, list): - for t in tree: - self.dispatch(t) - return - meth = getattr(self, "_"+tree.__class__.__name__) - meth(tree) - - # Unparsing methods - # - # There should be one method per concrete grammar type - # Constructors should be grouped by sum type. Ideally, - # this would follow the order in the grammar, but - # currently doesn't. - - def _Module(self, tree): - for stmt in tree.body: - self.dispatch(stmt) - - # stmt - def _Expr(self, tree): - self.fill() - self.dispatch(tree.value) - - def _Import(self, t): - self.fill("import ") - _interleave(lambda: self.write(", "), self.dispatch, t.names) - - def _ImportFrom(self, t): - self.fill("from ") - self.write("." * t.level) - if t.module: - self.write(t.module) - self.write(" import ") - _interleave(lambda: self.write(", "), self.dispatch, t.names) - - def _Assign(self, t): - self.fill() - for target in t.targets: - self.dispatch(target) - self.write(" = ") - self.dispatch(t.value) - - def _AugAssign(self, t): - self.fill() - self.dispatch(t.target) - self.write(" "+self.binop[t.op.__class__.__name__]+"= ") - self.dispatch(t.value) - - def _Return(self, t): - self.fill("return") - if t.value: - self.write(" ") - self.dispatch(t.value) - - def _Pass(self, t): - self.fill("pass") - - def _Break(self, t): - self.fill("break") - - def _Continue(self, t): - self.fill("continue") - - def _Delete(self, t): - self.fill("del ") - _interleave(lambda: self.write(", "), self.dispatch, t.targets) - - def _Assert(self, t): - self.fill("assert ") - self.dispatch(t.test) - if t.msg: - self.write(", ") - self.dispatch(t.msg) - - def _Global(self, t): - self.fill("global ") - _interleave(lambda: self.write(", "), self.write, t.names) - - def _Nonlocal(self, t): - self.fill("nonlocal ") - _interleave(lambda: self.write(", "), self.write, t.names) - - def _Yield(self, t): - self.write("(") - self.write("yield") - if t.value: - self.write(" ") - self.dispatch(t.value) - self.write(")") - - def _YieldFrom(self, t): - self.write("(") - self.write("yield from") - if t.value: - self.write(" ") - self.dispatch(t.value) - self.write(")") - - def _Raise(self, t): - self.fill("raise") - if not t.exc: - assert not t.cause - return - self.write(" ") - self.dispatch(t.exc) - if t.cause: - self.write(" from ") - self.dispatch(t.cause) - - def _Try(self, t): - self.fill("try") - self.enter() - self.dispatch(t.body) - self.leave() - for ex in t.handlers: - self.dispatch(ex) - if t.orelse: - self.fill("else") - self.enter() - self.dispatch(t.orelse) - self.leave() - if t.finalbody: - self.fill("finally") - self.enter() - self.dispatch(t.finalbody) - self.leave() - - def _ExceptHandler(self, t): - self.fill("except") - if t.type: - self.write(" ") - self.dispatch(t.type) - if t.name: - self.write(" as ") - self.write(t.name) - self.enter() - self.dispatch(t.body) - self.leave() - - def _ClassDef(self, t): - self.write("\n") - for deco in t.decorator_list: - self.fill("@") - self.dispatch(deco) - self.fill("class "+t.name) - self.write("(") - comma = False - for e in t.bases: - if comma: - self.write(", ") - else: - comma = True - self.dispatch(e) - for e in t.keywords: - if comma: - self.write(", ") - else: - comma = True - self.dispatch(e) - self.write(")") - - self.enter() - self.dispatch(t.body) - self.leave() - - def _FunctionDef(self, t): - self.write("\n") - for deco in t.decorator_list: - self.fill("@") - self.dispatch(deco) - self.fill("def "+t.name + "(") - self.dispatch(t.args) - self.write(")") - if t.returns: - self.write(" -> ") - self.dispatch(t.returns) - self.enter() - self.dispatch(t.body) - self.leave() - - def _For(self, t): - self.fill("for ") - self.dispatch(t.target) - self.write(" in ") - self.dispatch(t.iter) - self.enter() - self.dispatch(t.body) - self.leave() - if t.orelse: - self.fill("else") - self.enter() - self.dispatch(t.orelse) - self.leave() - - def _If(self, t): - self.fill("if ") - self.dispatch(t.test) - self.enter() - self.dispatch(t.body) - self.leave() - # collapse nested ifs into equivalent elifs. - while (t.orelse and len(t.orelse) == 1 and - isinstance(t.orelse[0], ast.If)): - t = t.orelse[0] - self.fill("elif ") - self.dispatch(t.test) - self.enter() - self.dispatch(t.body) - self.leave() - # final else - if t.orelse: - self.fill("else") - self.enter() - self.dispatch(t.orelse) - self.leave() - - def _While(self, t): - self.fill("while ") - self.dispatch(t.test) - self.enter() - self.dispatch(t.body) - self.leave() - if t.orelse: - self.fill("else") - self.enter() - self.dispatch(t.orelse) - self.leave() - - def _With(self, t): - self.fill("with ") - _interleave(lambda: self.write(", "), self.dispatch, t.items) - self.enter() - self.dispatch(t.body) - self.leave() - - # expr - def _Bytes(self, t): - self.write(repr(t.s)) - - def _Str(self, tree): - self.write(repr(tree.s)) - - def _Name(self, t): - self.write(t.id) - - def _NameConstant(self, t): - self.write(repr(t.value)) - - def _Num(self, t): - # Substitute overflowing decimal literal for AST infinities. - self.write(repr(t.n).replace("inf", INFSTR)) - - def _List(self, t): - self.write("[") - _interleave(lambda: self.write(", "), self.dispatch, t.elts) - self.write("]") - - def _ListComp(self, t): - self.write("[") - self.dispatch(t.elt) - for gen in t.generators: - self.dispatch(gen) - self.write("]") - - def _GeneratorExp(self, t): - self.write("(") - self.dispatch(t.elt) - for gen in t.generators: - self.dispatch(gen) - self.write(")") - - def _SetComp(self, t): - self.write("{") - self.dispatch(t.elt) - for gen in t.generators: - self.dispatch(gen) - self.write("}") - - def _DictComp(self, t): - self.write("{") - self.dispatch(t.key) - self.write(": ") - self.dispatch(t.value) - for gen in t.generators: - self.dispatch(gen) - self.write("}") - - def _comprehension(self, t): - self.write(" for ") - self.dispatch(t.target) - self.write(" in ") - self.dispatch(t.iter) - for if_clause in t.ifs: - self.write(" if ") - self.dispatch(if_clause) - - def _IfExp(self, t): - self.write("(") - self.dispatch(t.body) - self.write(" if ") - self.dispatch(t.test) - self.write(" else ") - self.dispatch(t.orelse) - self.write(")") - - def _Set(self, t): - assert(t.elts) # should be at least one element - self.write("{") - _interleave(lambda: self.write(", "), self.dispatch, t.elts) - self.write("}") - - def _Dict(self, t): - self.write("{") - - def write_pair(pair): - (k, v) = pair - self.dispatch(k) - self.write(": ") - self.dispatch(v) - _interleave(lambda: self.write(", "), write_pair, - zip(t.keys, t.values)) - self.write("}") - - def _Tuple(self, t): - self.write("(") - if len(t.elts) == 1: - (elt,) = t.elts - self.dispatch(elt) - self.write(",") - else: - _interleave(lambda: self.write(", "), self.dispatch, t.elts) - self.write(")") - - unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"} - - def _UnaryOp(self, t): - self.write("(") - self.write(self.unop[t.op.__class__.__name__]) - self.write(" ") - self.dispatch(t.operand) - self.write(")") - - binop = {"Add": "+", "Sub": "-", "Mult": "*", "Div": "/", "Mod": "%", - "LShift": "<<", "RShift": ">>", - "BitOr": "|", "BitXor": "^", "BitAnd": "&", - "FloorDiv": "//", "Pow": "**"} - - def _BinOp(self, t): - self.write("(") - self.dispatch(t.left) - self.write(" " + self.binop[t.op.__class__.__name__] + " ") - self.dispatch(t.right) - self.write(")") - - cmpops = {"Eq": "==", "NotEq": "!=", - "Lt": "<", "LtE": "<=", "Gt": ">", "GtE": ">=", - "Is": "is", "IsNot": "is not", "In": "in", "NotIn": "not in"} - - def _Compare(self, t): - self.write("(") - self.dispatch(t.left) - for o, e in zip(t.ops, t.comparators): - self.write(" " + self.cmpops[o.__class__.__name__] + " ") - self.dispatch(e) - self.write(")") - - boolops = {ast.And: "and", ast.Or: "or"} - - def _BoolOp(self, t): - self.write("(") - s = " %s " % self.boolops[t.op.__class__] - _interleave(lambda: self.write(s), self.dispatch, t.values) - self.write(")") - - def _Attribute(self, t): - self.dispatch(t.value) - # Special case: 3.__abs__() is a syntax error, so if t.value - # is an integer literal then we need to either parenthesize - # it or add an extra space to get 3 .__abs__(). - if isinstance(t.value, ast.Num) and isinstance(t.value.n, int): - self.write(" ") - self.write(".") - self.write(t.attr) - - def _Call(self, t): - self.dispatch(t.func) - self.write("(") - comma = False - for e in t.args: - if comma: - self.write(", ") - else: - comma = True - self.dispatch(e) - for e in t.keywords: - if comma: - self.write(", ") - else: - comma = True - self.dispatch(e) - self.write(")") - - def _Subscript(self, t): - self.dispatch(t.value) - self.write("[") - self.dispatch(t.slice) - self.write("]") - - def _Starred(self, t): - self.write("*") - self.dispatch(t.value) - - # slice - def _Ellipsis(self, t): - self.write("...") - - def _Index(self, t): - self.dispatch(t.value) - - def _Slice(self, t): - if t.lower: - self.dispatch(t.lower) - self.write(":") - if t.upper: - self.dispatch(t.upper) - if t.step: - self.write(":") - self.dispatch(t.step) - - def _ExtSlice(self, t): - _interleave(lambda: self.write(', '), self.dispatch, t.dims) - - # argument - def _arg(self, t): - self.write(t.arg) - if t.annotation: - self.write(": ") - self.dispatch(t.annotation) - - # others - def _arguments(self, t): - first = True - # normal arguments - defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults - for a, d in zip(t.args, defaults): - if first: - first = False - else: - self.write(", ") - self.dispatch(a) - if d: - self.write("=") - self.dispatch(d) - - # varargs, or bare '*' if no varargs but keyword-only arguments present - if t.vararg or t.kwonlyargs: - if first: - first = False - else: - self.write(", ") - self.write("*") - if t.vararg: - self.write(t.vararg.arg) - if t.vararg.annotation: - self.write(": ") - self.dispatch(t.vararg.annotation) - - # keyword-only arguments - if t.kwonlyargs: - for a, d in zip(t.kwonlyargs, t.kw_defaults): - if first: - first = False - else: - self.write(", ") - self.dispatch(a), - if d: - self.write("=") - self.dispatch(d) - - # kwargs - if t.kwarg: - if first: - first = False - else: - self.write(", ") - self.write("**"+t.kwarg.arg) - if t.kwarg.annotation: - self.write(": ") - self.dispatch(t.kwarg.annotation) - - def _keyword(self, t): - if t.arg is None: - self.write("**") - else: - self.write(t.arg) - self.write("=") - self.dispatch(t.value) - - def _Lambda(self, t): - self.write("(") - self.write("lambda ") - self.dispatch(t.args) - self.write(": ") - self.dispatch(t.body) - self.write(")") - - def _alias(self, t): - self.write(t.name) - if t.asname: - self.write(" as "+t.asname) - - def _withitem(self, t): - self.dispatch(t.context_expr) - if t.optional_vars: - self.write(" as ") - self.dispatch(t.optional_vars) - - -def unparse(tree): - unparser = _Unparser(tree) - return unparser.result