forked from M-Labs/artiq
Move transforms to artiq.compiler.transforms, add artiq.Module.
This commit is contained in:
parent
1702251ee5
commit
7ce9bdf54d
|
@ -0,0 +1 @@
|
||||||
|
from .module import Module
|
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
The :class:`Module` class encapsulates a single Python
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pythonparser import source, diagnostic, parse_buffer
|
||||||
|
from . import prelude, types, transforms
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
def __init__(self, source_buffer, engine=diagnostic.Engine(all_errors_are_fatal=True)):
|
||||||
|
parsetree, comments = parse_buffer(source_buffer, engine=engine)
|
||||||
|
self.name = os.path.basename(source_buffer.name)
|
||||||
|
|
||||||
|
asttyped_rewriter = transforms.ASTTypedRewriter(engine=engine)
|
||||||
|
typedtree = asttyped_rewriter.visit(parsetree)
|
||||||
|
self.globals = asttyped_rewriter.globals
|
||||||
|
|
||||||
|
inferencer = transforms.Inferencer(engine=engine)
|
||||||
|
inferencer.visit(typedtree)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(klass, source_string, name="input.py", first_line=1):
|
||||||
|
return klass(source.Buffer(source_string + "\n", name, first_line))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_filename(klass, filename):
|
||||||
|
with open(filename) as f:
|
||||||
|
return klass(source.Buffer(f.read(), filename, 1))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
printer = types.TypePrinter()
|
||||||
|
globals = ["%s: %s" % (var, printer.name(self.globals[var])) for var in self.globals]
|
||||||
|
return "<artiq.compiler.Module %s {\n %s\n}>" % (repr(self.name), ",\n ".join(globals))
|
|
@ -0,0 +1,75 @@
|
||||||
|
import sys, fileinput, os
|
||||||
|
from pythonparser import source, diagnostic, algorithm, parse_buffer
|
||||||
|
from .. import prelude, types
|
||||||
|
from ..transforms import ASTTypedRewriter, Inferencer
|
||||||
|
|
||||||
|
class Printer(algorithm.Visitor):
|
||||||
|
"""
|
||||||
|
:class:`Printer` prints ``:`` and the node type after every typed node,
|
||||||
|
and ``->`` and the node type before the colon in a function definition.
|
||||||
|
|
||||||
|
In almost all cases (except function definition) this does not result
|
||||||
|
in valid Python syntax.
|
||||||
|
|
||||||
|
:ivar rewriter: (:class:`pythonparser.source.Rewriter`) rewriter instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, buf):
|
||||||
|
self.rewriter = source.Rewriter(buf)
|
||||||
|
self.type_printer = types.TypePrinter()
|
||||||
|
|
||||||
|
def rewrite(self):
|
||||||
|
return self.rewriter.rewrite()
|
||||||
|
|
||||||
|
def visit_FunctionDefT(self, node):
|
||||||
|
super().generic_visit(node)
|
||||||
|
|
||||||
|
self.rewriter.insert_before(node.colon_loc,
|
||||||
|
"->{}".format(self.type_printer.name(node.return_type)))
|
||||||
|
|
||||||
|
def visit_ExceptHandlerT(self, node):
|
||||||
|
super().generic_visit(node)
|
||||||
|
|
||||||
|
if node.name_loc:
|
||||||
|
self.rewriter.insert_after(node.name_loc,
|
||||||
|
":{}".format(self.type_printer.name(node.name_type)))
|
||||||
|
|
||||||
|
def generic_visit(self, node):
|
||||||
|
super().generic_visit(node)
|
||||||
|
|
||||||
|
if hasattr(node, "type"):
|
||||||
|
self.rewriter.insert_after(node.loc,
|
||||||
|
":{}".format(self.type_printer.name(node.type)))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == '+diag':
|
||||||
|
del sys.argv[1]
|
||||||
|
def process_diagnostic(diag):
|
||||||
|
print("\n".join(diag.render(only_line=True)))
|
||||||
|
if diag.level == 'fatal':
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
def process_diagnostic(diag):
|
||||||
|
print("\n".join(diag.render()))
|
||||||
|
if diag.level in ('fatal', 'error'):
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
engine = diagnostic.Engine()
|
||||||
|
engine.process = process_diagnostic
|
||||||
|
|
||||||
|
buf = source.Buffer("".join(fileinput.input()).expandtabs(),
|
||||||
|
os.path.basename(fileinput.filename()))
|
||||||
|
parsed, comments = parse_buffer(buf, engine=engine)
|
||||||
|
typed = ASTTypedRewriter(globals=prelude.globals(), engine=engine).visit(parsed)
|
||||||
|
Inferencer(engine=engine).visit(typed)
|
||||||
|
|
||||||
|
printer = Printer(buf)
|
||||||
|
printer.visit(typed)
|
||||||
|
for comment in comments:
|
||||||
|
if comment.text.find("CHECK") >= 0:
|
||||||
|
printer.rewriter.remove(comment.loc)
|
||||||
|
print(printer.rewrite().source)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .asttyped_rewriter import ASTTypedRewriter
|
||||||
|
from .inferencer import Inferencer
|
|
@ -0,0 +1,377 @@
|
||||||
|
from pythonparser import algorithm, diagnostic
|
||||||
|
from .. import asttyped, types, builtins, prelude
|
||||||
|
|
||||||
|
# This visitor will be called for every node with a scope,
|
||||||
|
# i.e.: class, function, comprehension, lambda
|
||||||
|
class LocalExtractor(algorithm.Visitor):
|
||||||
|
def __init__(self, env_stack, engine):
|
||||||
|
super().__init__()
|
||||||
|
self.env_stack = env_stack
|
||||||
|
self.engine = engine
|
||||||
|
|
||||||
|
self.in_root = False
|
||||||
|
self.in_assign = False
|
||||||
|
self.typing_env = {}
|
||||||
|
|
||||||
|
# which names are global have to be recorded in the current scope
|
||||||
|
self.global_ = set()
|
||||||
|
|
||||||
|
# which names are nonlocal only affects whether the current scope
|
||||||
|
# gets a new binding or not, so we throw this away
|
||||||
|
self.nonlocal_ = set()
|
||||||
|
|
||||||
|
# parameters can't be declared as global or nonlocal
|
||||||
|
self.params = set()
|
||||||
|
|
||||||
|
def visit_in_assign(self, node):
|
||||||
|
try:
|
||||||
|
self.in_assign = True
|
||||||
|
return self.visit(node)
|
||||||
|
finally:
|
||||||
|
self.in_assign = False
|
||||||
|
|
||||||
|
def visit_Assign(self, node):
|
||||||
|
self.visit(node.value)
|
||||||
|
for target in node.targets:
|
||||||
|
self.visit_in_assign(target)
|
||||||
|
|
||||||
|
def visit_For(self, node):
|
||||||
|
self.visit(node.iter)
|
||||||
|
self.visit_in_assign(node.target)
|
||||||
|
self.visit(node.body)
|
||||||
|
self.visit(node.orelse)
|
||||||
|
|
||||||
|
def visit_withitem(self, node):
|
||||||
|
self.visit(node.context_expr)
|
||||||
|
if node.optional_vars is not None:
|
||||||
|
self.visit_in_assign(node.optional_vars)
|
||||||
|
|
||||||
|
def visit_comprehension(self, node):
|
||||||
|
self.visit(node.iter)
|
||||||
|
self.visit_in_assign(node.target)
|
||||||
|
for if_ in node.ifs:
|
||||||
|
self.visit(node.ifs)
|
||||||
|
|
||||||
|
def visit_root(self, node):
|
||||||
|
if self.in_root:
|
||||||
|
return
|
||||||
|
self.in_root = True
|
||||||
|
self.generic_visit(node)
|
||||||
|
|
||||||
|
visit_Module = visit_root # don't look at inner scopes
|
||||||
|
visit_ClassDef = visit_root
|
||||||
|
visit_Lambda = visit_root
|
||||||
|
visit_DictComp = visit_root
|
||||||
|
visit_ListComp = visit_root
|
||||||
|
visit_SetComp = visit_root
|
||||||
|
visit_GeneratorExp = visit_root
|
||||||
|
|
||||||
|
def visit_FunctionDef(self, node):
|
||||||
|
if self.in_root:
|
||||||
|
self._assignable(node.name)
|
||||||
|
self.visit_root(node)
|
||||||
|
|
||||||
|
def _assignable(self, name):
|
||||||
|
if name not in self.typing_env and name not in self.nonlocal_:
|
||||||
|
self.typing_env[name] = types.TVar()
|
||||||
|
|
||||||
|
def visit_arg(self, node):
|
||||||
|
if node.arg in self.params:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"duplicate parameter '{name}'", {"name": node.arg},
|
||||||
|
node.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
return
|
||||||
|
self._assignable(node.arg)
|
||||||
|
self.params.add(node.arg)
|
||||||
|
|
||||||
|
def visit_Name(self, node):
|
||||||
|
if self.in_assign:
|
||||||
|
# code like:
|
||||||
|
# x = 1
|
||||||
|
# def f():
|
||||||
|
# x = 1
|
||||||
|
# creates a new binding for x in f's scope
|
||||||
|
self._assignable(node.id)
|
||||||
|
|
||||||
|
def _check_not_in(self, name, names, curkind, newkind, loc):
|
||||||
|
if name in names:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"name '{name}' cannot be {curkind} and {newkind} simultaneously",
|
||||||
|
{"name": name, "curkind": curkind, "newkind": newkind}, loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def visit_Global(self, node):
|
||||||
|
for name, loc in zip(node.names, node.name_locs):
|
||||||
|
if self._check_not_in(name, self.nonlocal_, "nonlocal", "global", loc) or \
|
||||||
|
self._check_not_in(name, self.params, "a parameter", "global", loc):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.global_.add(name)
|
||||||
|
self._assignable(name)
|
||||||
|
self.env_stack[1][name] = self.typing_env[name]
|
||||||
|
|
||||||
|
def visit_Nonlocal(self, node):
|
||||||
|
for name, loc in zip(node.names, node.name_locs):
|
||||||
|
if self._check_not_in(name, self.global_, "global", "nonlocal", loc) or \
|
||||||
|
self._check_not_in(name, self.params, "a parameter", "nonlocal", loc):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# nonlocal does not search prelude and global scopes
|
||||||
|
found = False
|
||||||
|
for outer_env in reversed(self.env_stack[2:]):
|
||||||
|
if name in outer_env:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"cannot declare name '{name}' as nonlocal: it is not bound in any outer scope",
|
||||||
|
{"name": name},
|
||||||
|
loc, [node.keyword_loc])
|
||||||
|
self.engine.process(diag)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.nonlocal_.add(name)
|
||||||
|
|
||||||
|
def visit_ExceptHandler(self, node):
|
||||||
|
self.visit(node.type)
|
||||||
|
self._assignable(node.name)
|
||||||
|
for stmt in node.body:
|
||||||
|
self.visit(stmt)
|
||||||
|
|
||||||
|
|
||||||
|
class ASTTypedRewriter(algorithm.Transformer):
|
||||||
|
"""
|
||||||
|
:class:`ASTTypedRewriter` converts an untyped AST to a typed AST
|
||||||
|
where all type fields of non-literals are filled with fresh type variables,
|
||||||
|
and type fields of literals are filled with corresponding types.
|
||||||
|
|
||||||
|
:class:`ASTTypedRewriter` also discovers the scope of variable bindings
|
||||||
|
via :class:`LocalExtractor`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, engine):
|
||||||
|
self.engine = engine
|
||||||
|
self.globals = None
|
||||||
|
self.env_stack = [prelude.globals()]
|
||||||
|
|
||||||
|
def _find_name(self, name, loc):
|
||||||
|
for typing_env in reversed(self.env_stack):
|
||||||
|
if name in typing_env:
|
||||||
|
return typing_env[name]
|
||||||
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
|
"name '{name}' is not bound to anything", {"name":name}, loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
# Visitors that replace node with a typed node
|
||||||
|
#
|
||||||
|
def visit_Module(self, node):
|
||||||
|
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
||||||
|
extractor.visit(node)
|
||||||
|
|
||||||
|
node = asttyped.ModuleT(
|
||||||
|
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
||||||
|
body=node.body, loc=node.loc)
|
||||||
|
self.globals = node.typing_env
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.env_stack.append(node.typing_env)
|
||||||
|
return self.generic_visit(node)
|
||||||
|
finally:
|
||||||
|
self.env_stack.pop()
|
||||||
|
|
||||||
|
def visit_FunctionDef(self, node):
|
||||||
|
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
||||||
|
extractor.visit(node)
|
||||||
|
|
||||||
|
node = asttyped.FunctionDefT(
|
||||||
|
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
||||||
|
signature_type=self._find_name(node.name, node.name_loc), return_type=types.TVar(),
|
||||||
|
name=node.name, args=node.args, returns=node.returns,
|
||||||
|
body=node.body, decorator_list=node.decorator_list,
|
||||||
|
keyword_loc=node.keyword_loc, name_loc=node.name_loc,
|
||||||
|
arrow_loc=node.arrow_loc, colon_loc=node.colon_loc, at_locs=node.at_locs,
|
||||||
|
loc=node.loc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.env_stack.append(node.typing_env)
|
||||||
|
return self.generic_visit(node)
|
||||||
|
finally:
|
||||||
|
self.env_stack.pop()
|
||||||
|
|
||||||
|
def visit_arg(self, node):
|
||||||
|
return asttyped.argT(type=self._find_name(node.arg, node.loc),
|
||||||
|
arg=node.arg, annotation=self.visit(node.annotation),
|
||||||
|
arg_loc=node.arg_loc, colon_loc=node.colon_loc, loc=node.loc)
|
||||||
|
|
||||||
|
def visit_Num(self, node):
|
||||||
|
if isinstance(node.n, int):
|
||||||
|
typ = builtins.TInt()
|
||||||
|
elif isinstance(node.n, float):
|
||||||
|
typ = builtins.TFloat()
|
||||||
|
else:
|
||||||
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
|
"numeric type {type} is not supported", {"type": node.n.__class__.__name__},
|
||||||
|
node.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
return asttyped.NumT(type=typ,
|
||||||
|
n=node.n, loc=node.loc)
|
||||||
|
|
||||||
|
def visit_Name(self, node):
|
||||||
|
return asttyped.NameT(type=self._find_name(node.id, node.loc),
|
||||||
|
id=node.id, ctx=node.ctx, loc=node.loc)
|
||||||
|
|
||||||
|
def visit_NameConstant(self, node):
|
||||||
|
if node.value is True or node.value is False:
|
||||||
|
typ = builtins.TBool()
|
||||||
|
elif node.value is None:
|
||||||
|
typ = builtins.TNone()
|
||||||
|
return asttyped.NameConstantT(type=typ, value=node.value, loc=node.loc)
|
||||||
|
|
||||||
|
def visit_Tuple(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
return asttyped.TupleT(type=types.TTuple([x.type for x in node.elts]),
|
||||||
|
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
||||||
|
|
||||||
|
def visit_List(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.ListT(type=builtins.TList(),
|
||||||
|
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_Attribute(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.AttributeT(type=types.TVar(),
|
||||||
|
value=node.value, attr=node.attr, ctx=node.ctx,
|
||||||
|
dot_loc=node.dot_loc, attr_loc=node.attr_loc, loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_Subscript(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.SubscriptT(type=types.TVar(),
|
||||||
|
value=node.value, slice=node.slice, ctx=node.ctx,
|
||||||
|
loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_BoolOp(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.BoolOpT(type=types.TVar(),
|
||||||
|
op=node.op, values=node.values,
|
||||||
|
op_locs=node.op_locs, loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_UnaryOp(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.UnaryOpT(type=types.TVar(),
|
||||||
|
op=node.op, operand=node.operand,
|
||||||
|
loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_BinOp(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.BinOpT(type=types.TVar(),
|
||||||
|
left=node.left, op=node.op, right=node.right,
|
||||||
|
loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_Compare(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.CompareT(type=types.TVar(),
|
||||||
|
left=node.left, ops=node.ops, comparators=node.comparators,
|
||||||
|
loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_IfExp(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.IfExpT(type=types.TVar(),
|
||||||
|
test=node.test, body=node.body, orelse=node.orelse,
|
||||||
|
if_loc=node.if_loc, else_loc=node.else_loc, loc=node.loc)
|
||||||
|
return self.visit(node)
|
||||||
|
|
||||||
|
def visit_ListComp(self, node):
|
||||||
|
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
||||||
|
extractor.visit(node)
|
||||||
|
|
||||||
|
node = asttyped.ListCompT(
|
||||||
|
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
||||||
|
type=types.TVar(),
|
||||||
|
elt=node.elt, generators=node.generators,
|
||||||
|
begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.env_stack.append(node.typing_env)
|
||||||
|
return self.generic_visit(node)
|
||||||
|
finally:
|
||||||
|
self.env_stack.pop()
|
||||||
|
|
||||||
|
def visit_Call(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.CallT(type=types.TVar(),
|
||||||
|
func=node.func, args=node.args, keywords=node.keywords,
|
||||||
|
starargs=node.starargs, kwargs=node.kwargs,
|
||||||
|
star_loc=node.star_loc, dstar_loc=node.dstar_loc,
|
||||||
|
begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def visit_Lambda(self, node):
|
||||||
|
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
||||||
|
extractor.visit(node)
|
||||||
|
|
||||||
|
node = asttyped.LambdaT(
|
||||||
|
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
||||||
|
type=types.TVar(),
|
||||||
|
args=node.args, body=node.body,
|
||||||
|
lambda_loc=node.lambda_loc, colon_loc=node.colon_loc, loc=node.loc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.env_stack.append(node.typing_env)
|
||||||
|
return self.generic_visit(node)
|
||||||
|
finally:
|
||||||
|
self.env_stack.pop()
|
||||||
|
|
||||||
|
def visit_ExceptHandler(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
node = asttyped.ExceptHandlerT(
|
||||||
|
name_type=self._find_name(node.name, node.name_loc),
|
||||||
|
filter=node.type, name=node.name, body=node.body,
|
||||||
|
except_loc=node.except_loc, as_loc=node.as_loc, name_loc=node.name_loc,
|
||||||
|
colon_loc=node.colon_loc, loc=node.loc)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def visit_Raise(self, node):
|
||||||
|
node = self.generic_visit(node)
|
||||||
|
if node.cause:
|
||||||
|
diag = diagnostic.Diagnostic("error",
|
||||||
|
"'raise from' syntax is not supported", {},
|
||||||
|
node.from_loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
return node
|
||||||
|
|
||||||
|
# Unsupported visitors
|
||||||
|
#
|
||||||
|
def visit_unsupported(self, node):
|
||||||
|
diag = diagnostic.Diagnostic("fatal",
|
||||||
|
"this syntax is not supported", {},
|
||||||
|
node.loc)
|
||||||
|
self.engine.process(diag)
|
||||||
|
|
||||||
|
# expr
|
||||||
|
visit_Dict = visit_unsupported
|
||||||
|
visit_DictComp = visit_unsupported
|
||||||
|
visit_Ellipsis = visit_unsupported
|
||||||
|
visit_GeneratorExp = visit_unsupported
|
||||||
|
visit_Set = visit_unsupported
|
||||||
|
visit_SetComp = visit_unsupported
|
||||||
|
visit_Str = visit_unsupported
|
||||||
|
visit_Starred = visit_unsupported
|
||||||
|
visit_Yield = visit_unsupported
|
||||||
|
visit_YieldFrom = visit_unsupported
|
||||||
|
|
||||||
|
# stmt
|
||||||
|
visit_Assert = visit_unsupported
|
||||||
|
visit_ClassDef = visit_unsupported
|
||||||
|
visit_Delete = visit_unsupported
|
||||||
|
visit_Import = visit_unsupported
|
||||||
|
visit_ImportFrom = visit_unsupported
|
|
@ -1,378 +1,6 @@
|
||||||
from pythonparser import source, ast, algorithm, diagnostic, parse_buffer
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from . import asttyped, types, builtins
|
from pythonparser import algorithm, diagnostic, ast
|
||||||
|
from .. import asttyped, types, builtins
|
||||||
# This visitor will be called for every node with a scope,
|
|
||||||
# i.e.: class, function, comprehension, lambda
|
|
||||||
class LocalExtractor(algorithm.Visitor):
|
|
||||||
def __init__(self, env_stack, engine):
|
|
||||||
super().__init__()
|
|
||||||
self.env_stack = env_stack
|
|
||||||
self.engine = engine
|
|
||||||
|
|
||||||
self.in_root = False
|
|
||||||
self.in_assign = False
|
|
||||||
self.typing_env = {}
|
|
||||||
|
|
||||||
# which names are global have to be recorded in the current scope
|
|
||||||
self.global_ = set()
|
|
||||||
|
|
||||||
# which names are nonlocal only affects whether the current scope
|
|
||||||
# gets a new binding or not, so we throw this away
|
|
||||||
self.nonlocal_ = set()
|
|
||||||
|
|
||||||
# parameters can't be declared as global or nonlocal
|
|
||||||
self.params = set()
|
|
||||||
|
|
||||||
if len(self.env_stack) == 1:
|
|
||||||
self.env_stack.append(self.typing_env)
|
|
||||||
|
|
||||||
def visit_in_assign(self, node):
|
|
||||||
try:
|
|
||||||
self.in_assign = True
|
|
||||||
return self.visit(node)
|
|
||||||
finally:
|
|
||||||
self.in_assign = False
|
|
||||||
|
|
||||||
def visit_Assign(self, node):
|
|
||||||
self.visit(node.value)
|
|
||||||
for target in node.targets:
|
|
||||||
self.visit_in_assign(target)
|
|
||||||
|
|
||||||
def visit_For(self, node):
|
|
||||||
self.visit(node.iter)
|
|
||||||
self.visit_in_assign(node.target)
|
|
||||||
self.visit(node.body)
|
|
||||||
self.visit(node.orelse)
|
|
||||||
|
|
||||||
def visit_withitem(self, node):
|
|
||||||
self.visit(node.context_expr)
|
|
||||||
if node.optional_vars is not None:
|
|
||||||
self.visit_in_assign(node.optional_vars)
|
|
||||||
|
|
||||||
def visit_comprehension(self, node):
|
|
||||||
self.visit(node.iter)
|
|
||||||
self.visit_in_assign(node.target)
|
|
||||||
for if_ in node.ifs:
|
|
||||||
self.visit(node.ifs)
|
|
||||||
|
|
||||||
def visit_root(self, node):
|
|
||||||
if self.in_root:
|
|
||||||
return
|
|
||||||
self.in_root = True
|
|
||||||
self.generic_visit(node)
|
|
||||||
|
|
||||||
visit_Module = visit_root # don't look at inner scopes
|
|
||||||
visit_ClassDef = visit_root
|
|
||||||
visit_Lambda = visit_root
|
|
||||||
visit_DictComp = visit_root
|
|
||||||
visit_ListComp = visit_root
|
|
||||||
visit_SetComp = visit_root
|
|
||||||
visit_GeneratorExp = visit_root
|
|
||||||
|
|
||||||
def visit_FunctionDef(self, node):
|
|
||||||
if self.in_root:
|
|
||||||
self._assignable(node.name)
|
|
||||||
self.visit_root(node)
|
|
||||||
|
|
||||||
def _assignable(self, name):
|
|
||||||
if name not in self.typing_env and name not in self.nonlocal_:
|
|
||||||
self.typing_env[name] = types.TVar()
|
|
||||||
|
|
||||||
def visit_arg(self, node):
|
|
||||||
if node.arg in self.params:
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
|
||||||
"duplicate parameter '{name}'", {"name": node.arg},
|
|
||||||
node.loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
return
|
|
||||||
self._assignable(node.arg)
|
|
||||||
self.params.add(node.arg)
|
|
||||||
|
|
||||||
def visit_Name(self, node):
|
|
||||||
if self.in_assign:
|
|
||||||
# code like:
|
|
||||||
# x = 1
|
|
||||||
# def f():
|
|
||||||
# x = 1
|
|
||||||
# creates a new binding for x in f's scope
|
|
||||||
self._assignable(node.id)
|
|
||||||
|
|
||||||
def _check_not_in(self, name, names, curkind, newkind, loc):
|
|
||||||
if name in names:
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
|
||||||
"name '{name}' cannot be {curkind} and {newkind} simultaneously",
|
|
||||||
{"name": name, "curkind": curkind, "newkind": newkind}, loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def visit_Global(self, node):
|
|
||||||
for name, loc in zip(node.names, node.name_locs):
|
|
||||||
if self._check_not_in(name, self.nonlocal_, "nonlocal", "global", loc) or \
|
|
||||||
self._check_not_in(name, self.params, "a parameter", "global", loc):
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.global_.add(name)
|
|
||||||
self._assignable(name)
|
|
||||||
self.env_stack[1][name] = self.typing_env[name]
|
|
||||||
|
|
||||||
def visit_Nonlocal(self, node):
|
|
||||||
for name, loc in zip(node.names, node.name_locs):
|
|
||||||
if self._check_not_in(name, self.global_, "global", "nonlocal", loc) or \
|
|
||||||
self._check_not_in(name, self.params, "a parameter", "nonlocal", loc):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# nonlocal does not search prelude and global scopes
|
|
||||||
found = False
|
|
||||||
for outer_env in reversed(self.env_stack[2:]):
|
|
||||||
if name in outer_env:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
|
||||||
"cannot declare name '{name}' as nonlocal: it is not bound in any outer scope",
|
|
||||||
{"name": name},
|
|
||||||
loc, [node.keyword_loc])
|
|
||||||
self.engine.process(diag)
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.nonlocal_.add(name)
|
|
||||||
|
|
||||||
def visit_ExceptHandler(self, node):
|
|
||||||
self.visit(node.type)
|
|
||||||
self._assignable(node.name)
|
|
||||||
for stmt in node.body:
|
|
||||||
self.visit(stmt)
|
|
||||||
|
|
||||||
|
|
||||||
class ASTTypedRewriter(algorithm.Transformer):
|
|
||||||
"""
|
|
||||||
:class:`ASTTypedRewriter` converts an untyped AST to a typed AST
|
|
||||||
where all type fields of non-literals are filled with fresh type variables,
|
|
||||||
and type fields of literals are filled with corresponding types.
|
|
||||||
|
|
||||||
:class:`ASTTypedRewriter` also discovers the scope of variable bindings
|
|
||||||
via :class:`LocalExtractor`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, engine, globals={}):
|
|
||||||
self.engine = engine
|
|
||||||
self.env_stack = [globals]
|
|
||||||
|
|
||||||
def _find_name(self, name, loc):
|
|
||||||
for typing_env in reversed(self.env_stack):
|
|
||||||
if name in typing_env:
|
|
||||||
return typing_env[name]
|
|
||||||
diag = diagnostic.Diagnostic("fatal",
|
|
||||||
"name '{name}' is not bound to anything", {"name":name}, loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
|
|
||||||
# Visitors that replace node with a typed node
|
|
||||||
#
|
|
||||||
def visit_Module(self, node):
|
|
||||||
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
|
||||||
extractor.visit(node)
|
|
||||||
|
|
||||||
node = asttyped.ModuleT(
|
|
||||||
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
|
||||||
body=node.body, loc=node.loc)
|
|
||||||
return self.generic_visit(node)
|
|
||||||
|
|
||||||
def visit_FunctionDef(self, node):
|
|
||||||
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
|
||||||
extractor.visit(node)
|
|
||||||
|
|
||||||
node = asttyped.FunctionDefT(
|
|
||||||
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
|
||||||
signature_type=self._find_name(node.name, node.name_loc), return_type=types.TVar(),
|
|
||||||
name=node.name, args=node.args, returns=node.returns,
|
|
||||||
body=node.body, decorator_list=node.decorator_list,
|
|
||||||
keyword_loc=node.keyword_loc, name_loc=node.name_loc,
|
|
||||||
arrow_loc=node.arrow_loc, colon_loc=node.colon_loc, at_locs=node.at_locs,
|
|
||||||
loc=node.loc)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.env_stack.append(node.typing_env)
|
|
||||||
return self.generic_visit(node)
|
|
||||||
finally:
|
|
||||||
self.env_stack.pop()
|
|
||||||
|
|
||||||
def visit_arg(self, node):
|
|
||||||
return asttyped.argT(type=self._find_name(node.arg, node.loc),
|
|
||||||
arg=node.arg, annotation=self.visit(node.annotation),
|
|
||||||
arg_loc=node.arg_loc, colon_loc=node.colon_loc, loc=node.loc)
|
|
||||||
|
|
||||||
def visit_Num(self, node):
|
|
||||||
if isinstance(node.n, int):
|
|
||||||
typ = builtins.TInt()
|
|
||||||
elif isinstance(node.n, float):
|
|
||||||
typ = builtins.TFloat()
|
|
||||||
else:
|
|
||||||
diag = diagnostic.Diagnostic("fatal",
|
|
||||||
"numeric type {type} is not supported", {"type": node.n.__class__.__name__},
|
|
||||||
node.loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
return asttyped.NumT(type=typ,
|
|
||||||
n=node.n, loc=node.loc)
|
|
||||||
|
|
||||||
def visit_Name(self, node):
|
|
||||||
return asttyped.NameT(type=self._find_name(node.id, node.loc),
|
|
||||||
id=node.id, ctx=node.ctx, loc=node.loc)
|
|
||||||
|
|
||||||
def visit_NameConstant(self, node):
|
|
||||||
if node.value is True or node.value is False:
|
|
||||||
typ = builtins.TBool()
|
|
||||||
elif node.value is None:
|
|
||||||
typ = builtins.TNone()
|
|
||||||
return asttyped.NameConstantT(type=typ, value=node.value, loc=node.loc)
|
|
||||||
|
|
||||||
def visit_Tuple(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
return asttyped.TupleT(type=types.TTuple([x.type for x in node.elts]),
|
|
||||||
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
|
||||||
|
|
||||||
def visit_List(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.ListT(type=builtins.TList(),
|
|
||||||
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_Attribute(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.AttributeT(type=types.TVar(),
|
|
||||||
value=node.value, attr=node.attr, ctx=node.ctx,
|
|
||||||
dot_loc=node.dot_loc, attr_loc=node.attr_loc, loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_Subscript(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.SubscriptT(type=types.TVar(),
|
|
||||||
value=node.value, slice=node.slice, ctx=node.ctx,
|
|
||||||
loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_BoolOp(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.BoolOpT(type=types.TVar(),
|
|
||||||
op=node.op, values=node.values,
|
|
||||||
op_locs=node.op_locs, loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_UnaryOp(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.UnaryOpT(type=types.TVar(),
|
|
||||||
op=node.op, operand=node.operand,
|
|
||||||
loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_BinOp(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.BinOpT(type=types.TVar(),
|
|
||||||
left=node.left, op=node.op, right=node.right,
|
|
||||||
loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_Compare(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.CompareT(type=types.TVar(),
|
|
||||||
left=node.left, ops=node.ops, comparators=node.comparators,
|
|
||||||
loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_IfExp(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.IfExpT(type=types.TVar(),
|
|
||||||
test=node.test, body=node.body, orelse=node.orelse,
|
|
||||||
if_loc=node.if_loc, else_loc=node.else_loc, loc=node.loc)
|
|
||||||
return self.visit(node)
|
|
||||||
|
|
||||||
def visit_ListComp(self, node):
|
|
||||||
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
|
||||||
extractor.visit(node)
|
|
||||||
|
|
||||||
node = asttyped.ListCompT(
|
|
||||||
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
|
||||||
type=types.TVar(),
|
|
||||||
elt=node.elt, generators=node.generators,
|
|
||||||
begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.env_stack.append(node.typing_env)
|
|
||||||
return self.generic_visit(node)
|
|
||||||
finally:
|
|
||||||
self.env_stack.pop()
|
|
||||||
|
|
||||||
def visit_Call(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.CallT(type=types.TVar(),
|
|
||||||
func=node.func, args=node.args, keywords=node.keywords,
|
|
||||||
starargs=node.starargs, kwargs=node.kwargs,
|
|
||||||
star_loc=node.star_loc, dstar_loc=node.dstar_loc,
|
|
||||||
begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc)
|
|
||||||
return node
|
|
||||||
|
|
||||||
def visit_Lambda(self, node):
|
|
||||||
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
|
|
||||||
extractor.visit(node)
|
|
||||||
|
|
||||||
node = asttyped.LambdaT(
|
|
||||||
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
|
||||||
type=types.TVar(),
|
|
||||||
args=node.args, body=node.body,
|
|
||||||
lambda_loc=node.lambda_loc, colon_loc=node.colon_loc, loc=node.loc)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.env_stack.append(node.typing_env)
|
|
||||||
return self.generic_visit(node)
|
|
||||||
finally:
|
|
||||||
self.env_stack.pop()
|
|
||||||
|
|
||||||
def visit_ExceptHandler(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
node = asttyped.ExceptHandlerT(
|
|
||||||
name_type=self._find_name(node.name, node.name_loc),
|
|
||||||
filter=node.type, name=node.name, body=node.body,
|
|
||||||
except_loc=node.except_loc, as_loc=node.as_loc, name_loc=node.name_loc,
|
|
||||||
colon_loc=node.colon_loc, loc=node.loc)
|
|
||||||
return node
|
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
|
||||||
node = self.generic_visit(node)
|
|
||||||
if node.cause:
|
|
||||||
diag = diagnostic.Diagnostic("error",
|
|
||||||
"'raise from' syntax is not supported", {},
|
|
||||||
node.from_loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
return node
|
|
||||||
|
|
||||||
# Unsupported visitors
|
|
||||||
#
|
|
||||||
def visit_unsupported(self, node):
|
|
||||||
diag = diagnostic.Diagnostic("fatal",
|
|
||||||
"this syntax is not supported", {},
|
|
||||||
node.loc)
|
|
||||||
self.engine.process(diag)
|
|
||||||
|
|
||||||
# expr
|
|
||||||
visit_Dict = visit_unsupported
|
|
||||||
visit_DictComp = visit_unsupported
|
|
||||||
visit_Ellipsis = visit_unsupported
|
|
||||||
visit_GeneratorExp = visit_unsupported
|
|
||||||
visit_Set = visit_unsupported
|
|
||||||
visit_SetComp = visit_unsupported
|
|
||||||
visit_Str = visit_unsupported
|
|
||||||
visit_Starred = visit_unsupported
|
|
||||||
visit_Yield = visit_unsupported
|
|
||||||
visit_YieldFrom = visit_unsupported
|
|
||||||
|
|
||||||
# stmt
|
|
||||||
visit_Assert = visit_unsupported
|
|
||||||
visit_ClassDef = visit_unsupported
|
|
||||||
visit_Delete = visit_unsupported
|
|
||||||
visit_Import = visit_unsupported
|
|
||||||
visit_ImportFrom = visit_unsupported
|
|
||||||
|
|
||||||
|
|
||||||
class Inferencer(algorithm.Visitor):
|
class Inferencer(algorithm.Visitor):
|
||||||
"""
|
"""
|
||||||
|
@ -444,7 +72,7 @@ class Inferencer(algorithm.Visitor):
|
||||||
def visit_ListT(self, node):
|
def visit_ListT(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
for elt in node.elts:
|
for elt in node.elts:
|
||||||
self._unify(node.type, builtins.TList(elt.type),
|
self._unify(node.type["elt"], elt.type,
|
||||||
node.loc, elt.loc, self._makenotes_elts(node.elts, "a list element"))
|
node.loc, elt.loc, self._makenotes_elts(node.elts, "a list element"))
|
||||||
|
|
||||||
def visit_AttributeT(self, node):
|
def visit_AttributeT(self, node):
|
||||||
|
@ -1173,77 +801,3 @@ class Inferencer(algorithm.Visitor):
|
||||||
else:
|
else:
|
||||||
self._unify(self.function.return_type, node.value.type,
|
self._unify(self.function.return_type, node.value.type,
|
||||||
self.function.name_loc, node.value.loc, makenotes)
|
self.function.name_loc, node.value.loc, makenotes)
|
||||||
|
|
||||||
class Printer(algorithm.Visitor):
|
|
||||||
"""
|
|
||||||
:class:`Printer` prints ``:`` and the node type after every typed node,
|
|
||||||
and ``->`` and the node type before the colon in a function definition.
|
|
||||||
|
|
||||||
In almost all cases (except function definition) this does not result
|
|
||||||
in valid Python syntax.
|
|
||||||
|
|
||||||
:ivar rewriter: (:class:`pythonparser.source.Rewriter`) rewriter instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, buf):
|
|
||||||
self.rewriter = source.Rewriter(buf)
|
|
||||||
self.type_printer = types.TypePrinter()
|
|
||||||
|
|
||||||
def rewrite(self):
|
|
||||||
return self.rewriter.rewrite()
|
|
||||||
|
|
||||||
def visit_FunctionDefT(self, node):
|
|
||||||
super().generic_visit(node)
|
|
||||||
|
|
||||||
self.rewriter.insert_before(node.colon_loc,
|
|
||||||
"->{}".format(self.type_printer.name(node.return_type)))
|
|
||||||
|
|
||||||
def visit_ExceptHandlerT(self, node):
|
|
||||||
super().generic_visit(node)
|
|
||||||
|
|
||||||
if node.name_loc:
|
|
||||||
self.rewriter.insert_after(node.name_loc,
|
|
||||||
":{}".format(self.type_printer.name(node.name_type)))
|
|
||||||
|
|
||||||
def generic_visit(self, node):
|
|
||||||
super().generic_visit(node)
|
|
||||||
|
|
||||||
if hasattr(node, "type"):
|
|
||||||
self.rewriter.insert_after(node.loc,
|
|
||||||
":{}".format(self.type_printer.name(node.type)))
|
|
||||||
|
|
||||||
def main():
|
|
||||||
import sys, fileinput, os
|
|
||||||
from . import prelude
|
|
||||||
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == '+diag':
|
|
||||||
del sys.argv[1]
|
|
||||||
def process_diagnostic(diag):
|
|
||||||
print("\n".join(diag.render(only_line=True)))
|
|
||||||
if diag.level == 'fatal':
|
|
||||||
exit()
|
|
||||||
else:
|
|
||||||
def process_diagnostic(diag):
|
|
||||||
print("\n".join(diag.render()))
|
|
||||||
if diag.level in ('fatal', 'error'):
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
engine = diagnostic.Engine()
|
|
||||||
engine.process = process_diagnostic
|
|
||||||
|
|
||||||
buf = source.Buffer("".join(fileinput.input()).expandtabs(),
|
|
||||||
os.path.basename(fileinput.filename()))
|
|
||||||
parsed, comments = parse_buffer(buf, engine=engine)
|
|
||||||
typed = ASTTypedRewriter(globals=prelude.globals(), engine=engine).visit(parsed)
|
|
||||||
Inferencer(engine=engine).visit(typed)
|
|
||||||
|
|
||||||
printer = Printer(buf)
|
|
||||||
printer.visit(typed)
|
|
||||||
for comment in comments:
|
|
||||||
if comment.text.find("CHECK") >= 0:
|
|
||||||
printer.rewriter.remove(comment.loc)
|
|
||||||
print(printer.rewrite().source)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: bool:<constructor bool>():bool
|
# CHECK-L: bool:<constructor bool>():bool
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
1 | 2
|
1 | 2
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
a = 1
|
a = 1
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: error: cannot call this expression of type int
|
# CHECK-L: ${LINE:+1}: error: cannot call this expression of type int
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: error: expected '<<' operand to be of integer type, not float
|
# CHECK-L: ${LINE:+1}: error: expected '<<' operand to be of integer type, not float
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: error: return statement outside of a function
|
# CHECK-L: ${LINE:+1}: error: return statement outside of a function
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
try:
|
try:
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: error: type int(width='a) is not iterable
|
# CHECK-L: ${LINE:+1}: error: type int(width='a) is not iterable
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+1}: fatal: name 'x' is not bound to anything
|
# CHECK-L: ${LINE:+1}: fatal: name 'x' is not bound to anything
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
x = 1
|
x = 1
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with NoneType
|
# CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with NoneType
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing +diag %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer +diag %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
a = 1
|
a = 1
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: Exception:<constructor Exception>
|
# CHECK-L: Exception:<constructor Exception>
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
|
|
||||||
def _gcd(a, b):
|
def _gcd(a, b):
|
||||||
if a < 0:
|
if a < 0:
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
# CHECK-L: x:<function len>
|
# CHECK-L: x:<function len>
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
def f():
|
def f():
|
|
@ -1,4 +1,4 @@
|
||||||
# RUN: %python -m artiq.compiler.typing %s >%t
|
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
a = 1
|
a = 1
|
Loading…
Reference in New Issue