Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3adb4150f4ea
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 752031147dba
Choose a head ref
  • 4 commits
  • 6 files changed
  • 1 contributor

Commits on Jun 24, 2015

  1. Add types.TBuiltin.

    whitequark committed Jun 24, 2015
    Copy the full SHA
    8762729 View commit details
  2. Copy the full SHA
    710a04c View commit details
  3. Implement prelude.

    whitequark committed Jun 24, 2015
    Copy the full SHA
    4d407ac View commit details
  4. Add valid forms for builtin calls.

    whitequark committed Jun 24, 2015
    Copy the full SHA
    7520311 View commit details
Showing with 153 additions and 9 deletions.
  1. +22 −2 artiq/py2llvm/builtins.py
  2. +17 −0 artiq/py2llvm/prelude.py
  3. +32 −0 artiq/py2llvm/types.py
  4. +70 −7 artiq/py2llvm/typing.py
  5. +2 −0 lit-test/py2llvm/typing/builtin_calls.py
  6. +10 −0 lit-test/py2llvm/typing/prelude.py
24 changes: 22 additions & 2 deletions artiq/py2llvm/builtins.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""
The :mod:`builtins` module contains the builtin Python and ARTIQ
types, such as int or float.
The :mod:`builtins` module contains the builtin Python
and ARTIQ types, such as int or float.
"""

from . import types

# Types

class TNone(types.TMono):
def __init__(self):
super().__init__("NoneType")
@@ -29,6 +31,19 @@ def __init__(self, elt=None):
elt = types.TVar()
super().__init__("list", {"elt": elt})

def fn_len():
return types.TBuiltin("function len")

def fn_round():
return types.TBuiltin("function round")

def fn_range():
return types.TBuiltin("function range")

def fn_syscall():
return types.TBuiltin("function syscall")

# Accessors

def is_none(typ):
return types.is_mono(typ, "NoneType")
@@ -64,3 +79,8 @@ def is_collection(typ):
typ = typ.find()
return isinstance(typ, types.TTuple) or \
types.is_mono(typ, "list")

def is_function(typ, name):
typ = typ.find()
return isinstance(typ, types.TBuiltin) and \
typ.name == "function " + name
17 changes: 17 additions & 0 deletions artiq/py2llvm/prelude.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
The :mod:`prelude` module contains the initial global environment
in which ARTIQ kernels are evaluated.
"""

from . import builtins

def globals():
return {
"bool": builtins.TBool(),
"int": builtins.TInt(),
"float": builtins.TFloat(),
"len": builtins.fn_len(),
"round": builtins.fn_round(),
"range": builtins.fn_range(),
"syscall": builtins.fn_syscall(),
}
32 changes: 32 additions & 0 deletions artiq/py2llvm/types.py
Original file line number Diff line number Diff line change
@@ -183,6 +183,33 @@ def __eq__(self, other):
def __ne__(self, other):
return not (self == other)

class TBuiltin(Type):
"""
An instance of builtin type. Every instance of a builtin
type is treated specially according to its name.
"""

def __init__(self, name):
self.name = name

def find(self):
return self

def unify(self, other):
if self != other:
raise UnificationError(self, other)

def __repr__(self):
return "py2llvm.types.TBuiltin(%s)" % repr(self.name)

def __eq__(self, other):
return isinstance(other, TBuiltin) and \
self.name == other.name

def __ne__(self, other):
return not (self == other)


class TValue(Type):
"""
A type-level value (such as the integer denoting width of
@@ -235,6 +262,9 @@ def is_tuple(typ, elts=None):
def is_function(typ):
return isinstance(typ.find(), TFunction)

def is_builtin(typ):
return isinstance(typ.find(), TBuiltin)

def get_value(typ):
typ = typ.find()
if isinstance(typ, TVar):
@@ -276,6 +306,8 @@ def name(self, typ):
args += [ "%s:%s" % (arg, self.name(typ.args[arg])) for arg in typ.args]
args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs]
return "(%s)->%s" % (", ".join(args), self.name(typ.ret))
elif isinstance(typ, TBuiltin):
return "<built-in %s>" % typ.name
elif isinstance(typ, TValue):
return repr(typ.value)
else:
77 changes: 70 additions & 7 deletions artiq/py2llvm/typing.py
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ def __init__(self, env_stack, engine):
# parameters can't be declared as global or nonlocal
self.params = set()

if len(self.env_stack) == 0:
if len(self.env_stack) == 1:
self.env_stack.append(self.typing_env)

def visit_in_assign(self, node):
@@ -115,17 +115,17 @@ def visit_Global(self, node):

self.global_.add(name)
self._assignable(name)
self.env_stack[0][name] = self.typing_env[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 global scope
# nonlocal does not search prelude and global scopes
found = False
for outer_env in reversed(self.env_stack[1:]):
for outer_env in reversed(self.env_stack[2:]):
if name in outer_env:
found = True
break
@@ -156,9 +156,9 @@ class ASTTypedRewriter(algorithm.Transformer):
via :class:`LocalExtractor`.
"""

def __init__(self, engine):
def __init__(self, engine, globals={}):
self.engine = engine
self.env_stack = []
self.env_stack = [globals]

def _find_name(self, name, loc):
for typing_env in reversed(self.env_stack):
@@ -715,6 +715,66 @@ def visit_comprehension(self, node):
self.generic_visit(node)
self._unify_collection(element=node.target, collection=node.iter)

def visit_builtin_call(self, node):
if types.is_mono(node.func.type):
func_name = "function " + node.func.type.find().name
elif builtins.is_function(node.func.type):
func_name = node.func.type.find().name

def valid_form(signature):
return diagnostic.Diagnostic("note",
"{func} can be invoked as: {signature}",
{"func": func_name},
node.func.loc)

def diagnose(valid_forms):
diag = diagnostic.Diagnostic("error",
"{func} cannot be invoked with these arguments",
{"func": func_name},
node.func.loc, notes=valid_forms)

if builtins.is_bool(node.type):
valid_forms = lambda: [
valid_form("bool() -> bool"),
valid_form("bool(x:'a) -> bool where 'a is numeric")
]
elif builtins.is_int(node.type):
valid_forms = lambda: [
valid_form("int() -> int(width='a)"),
valid_form("int(x:'a) -> int(width='b) where 'a is numeric"),
valid_form("int(x:'a, width='b <int literal>) -> int(width='b) where 'a is numeric")
]
elif builtins.is_float(node.type):
valid_forms = lambda: [
valid_form("float() -> float"),
valid_form("float(x:'a) -> float where 'a is numeric")
]
elif builtins.is_list(node.type):
valid_forms = lambda: [
valid_form("list() -> list(elt='a)"),
# TODO: add this form when adding iterators
# valid_form("list(x) -> list(elt='a)")
]
elif builtins.is_function(node.type, "len"):
valid_forms = lambda: [
valid_form("len(x:list(elt='a)) -> int(width='b)"),
]
elif builtins.is_function(node.type, "round"):
valid_forms = lambda: [
valid_form("round(x:float) -> int(width='a)"),
]
# TODO: add when there are range types
# elif builtins.is_function(node.type, "range"):
# valid_forms = lambda: [
# valid_form("range(max:'a) -> range(elt='a)"),
# valid_form("range(min:'a, max:'a) -> range(elt='a)"),
# valid_form("range(min:'a, max:'a, step:'a) -> range(elt='a)"),
# ]
# TODO: add when it is clear what interface syscall() has
# elif builtins.is_function(node.type, "syscall"):
# valid_Forms = lambda: [
# ]

def visit_CallT(self, node):
self.generic_visit(node)

@@ -729,6 +789,8 @@ def visit_CallT(self, node):

if types.is_var(node.func.type):
return # not enough info yet
elif types.is_mono(node.func.type) or types.is_builtin(node.func.type):
return self.visit_builtin_call(self, node)
elif not types.is_function(node.func.type):
diag = diagnostic.Diagnostic("error",
"cannot call this expression of type {type}",
@@ -990,6 +1052,7 @@ def generic_visit(self, node):

def main():
import sys, fileinput, os
from . import prelude

if len(sys.argv) > 1 and sys.argv[1] == '+diag':
del sys.argv[1]
@@ -1009,7 +1072,7 @@ def process_diagnostic(diag):
buf = source.Buffer("".join(fileinput.input()).expandtabs(),
os.path.basename(fileinput.filename()))
parsed, comments = parse_buffer(buf, engine=engine)
typed = ASTTypedRewriter(engine=engine).visit(parsed)
typed = ASTTypedRewriter(globals=prelude.globals(), engine=engine).visit(parsed)
Inferencer(engine=engine).visit(typed)

printer = Printer(buf)
2 changes: 2 additions & 0 deletions lit-test/py2llvm/typing/builtin_calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# RUN: %python -m artiq.py2llvm.typing %s >%t
# RUN: OutputCheck %s --file-to-check=%t
10 changes: 10 additions & 0 deletions lit-test/py2llvm/typing/prelude.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# RUN: %python -m artiq.py2llvm.typing %s >%t
# RUN: OutputCheck %s --file-to-check=%t

# CHECK-L: x:<built-in function len>
x = len

def f():
global len
# CHECK-L: len:int(width='a) =
len = 1