#!/usr/bin/env python3 import inspect import ast import sys valid_types = { "int": int, } call_types = { "analogRead": ([int], int), "analogWrite": ([int, int], int), } class CompileContext: def __init__(self, stream, fn): self.stream = stream self.fn = fn def compile_fn(fn, stream=sys.stdout): stream.write(type_to_str(fn.returns)) stream.write(" ") stream.write(fn.name) stream.write("(") inferred = {} ctx = CompileContext(stream, fn) for arg in fn.args.args: typ = infer_arg(ctx, arg) if typ is None: raise TypeError(f'could not infer type for "{fn.name}" argument "{arg.arg}"') stream.write(type_to_str(typ)) stream.write(" ") stream.write(arg.arg) stream.write(")\n{\n") for expr in fn.body: indent(ctx.stream, 1) compile_expr(ctx, expr) ctx.stream.write(";\n") stream.write("}\n") def compile_expr(ctx, expr): if isinstance(expr, ast.Assign): compile_assign(ctx, expr) elif isinstance(expr, ast.Constant): ctx.stream.write(str(expr.value)) elif isinstance(expr, ast.Call): compile_call(ctx, expr) elif isinstance(expr, ast.Name): ctx.stream.write(expr.id) else: ctx.stream.write(ast.dump(expr)) def compile_call(ctx, expr): ctx.stream.write(expr.func.id) ctx.stream.write("(") for arg in expr.args: compile_expr(ctx, arg) ctx.stream.write(")") def compile_assign(ctx, asn): if len(asn.targets) > 1: raise TypeError("more than 1 return value not supported in assignments") target = asn.targets[0] ident = target.id typ = infer_from_value(asn.value) ctx.stream.write(type_to_str(typ)) ctx.stream.write(" ") ctx.stream.write(ident) ctx.stream.write(" = ") return compile_expr(ctx, asn.value) def indent(stream, depth=1): for n in range(depth): stream.write("\t") def type_to_str(typ): if typ is None: return "void" return typ.__name__ def infer_arg(ctx, arg): if arg.annotation is not None: typ = valid_types.get(expr.annotation.id) if typ is None: raise TypeError(f"unknown type: '{arg.annotation.id}'") return typ return infer_type(ctx, arg.arg) def infer_type(ctx, ident): res = None for e in ctx.fn.body: if isinstance(e, ast.Assign): res = infer_from_assign(ident, e) if res is not None: return res return None def infer_from_assign(ident, asn: ast.Assign): for target in asn.targets: if isinstance(target, ast.Name) and target.id == ident: return infer_from_value(asn.value) return None def infer_from_value(val): if isinstance(val, ast.Call): return infer_from_call(val) elif isinstance(val, ast.Constant): return infer_from_constant(val) return None def infer_from_constant(c): return type(c.value) def infer_from_call(call): fn_name = call.func.id typ = call_types.get(fn_name) if typ is None: raise TypeError(f"Identifier not found: '{fn_name}'") (arg_types, ret) = typ return ret def compile_fn_test(fn): if not inspect.isfunction(fn): print("not a function") src = inspect.getsource(fn) module = ast.parse(src) for fn in ast.iter_child_nodes(module): compile_fn(fn) def add_one(a): return a + 1 def loop(a): val = analogRead(A3) a = 1 b = 1 compile_fn_test(loop) #def main(): # print_fn(add_one) # # # #if __name__ == '__main__': # main()