Commit 1b01c6f1 authored by jneen's avatar jneen

big huge fuckin whatever commit yay but it's rpython again

parent e750b6b8
from tulip.symbol import Symbol, SymbolTable
from sys import stdin
from tulip.libedit import readline
from tulip.debug import debug
from rpython.rlib.objectmodel import we_are_translated
from tulip.lexer import ReaderLexer, Token, LexError
from tulip.reader import StringReader, FileReader
from tulip.skeleton import parse_skeleton, UnmatchedError, ParseError
from tulip.compiler import compile_base, CompileError, with_prelude
from tulip.compiler import compile_expr, compile_module, CompileError
from tulip.scope_tracker import ScopeTracker
from tulip.byte_compiler import ByteCompiler
from tulip.module_resolver import PreludeProvider
from tulip.repl import Repl
import tulip.runtime as r
import traceback
......@@ -17,67 +19,17 @@ import tulip.tests.runtime as tests
def entry_point(argv):
if len(argv) >= 2:
return run_file(argv[1])
elif stdin.isatty:
if stdin.isatty:
return run_repl()
assert False, u'TODO: actually implement an arg parser'
repl_char_map = {
Token.LPAREN: u'(',
Token.LBRACE: u'{',
Token.LBRACK: u'[',
Token.MACRO: u'['
assert False, u'TODO: actually implement an arg parser'
repl = Repl()
def run_repl():
lines = []
last_unmatched = None
while True:
if last_unmatched is not None:
prompt = '%s: ' % str(repl_char_map[last_unmatched.tokid])
prompt = ' : '
last_unmatched = None
code = u'\n'.join(lines)
ast = compile_base(parse_skeleton(ReaderLexer(StringReader(u'<repl>', code))))
if ast is None:
print u'(no input)'
tracker = ScopeTracker([])
ast = tracker.resolve(ast)
print 'resolved: ', ast.dump()
compiler = ByteCompiler()
print compiler.dump()
lines = []
except UnmatchedError as e:
last_unmatched = e.token
except LexError as e:
print e.dump()
except ParseError as e:
print e.dump()
except CompileError as e:
print e.dump()
except EOFError:
# except StandardError as e:
# print e
if last_unmatched is None:
lines = []
return 0
def print_logo():
......@@ -89,7 +41,11 @@ def print_logo():
def run_file(fname):
reader = FileReader(fname)
print parse_skeleton(ReaderLexer(reader)).dump()
skeleton = parse_skeleton(ReaderLexer(reader)).dump()
namespace = compile_module(skeleton)
print namespace.dump()
except LexError as e:
print e.dump()
except ParseError as e:
......@@ -26,3 +26,19 @@ test 'aliasing-with-objects {
.pair (get-bar Foo) (get-bar (C 2))
} (.pair 1 2)
@module A x [
foo = x
@module B [
bar = A 1
@module B [
@module bar [
foo = 2
import tulip.value as v
builtins = {}
def invoke_builtin(name, arity, proc, arguments):
return builtins[name].invoke(arity, proc, arguments)
def lookup_builtin(name):
return builtins[name]
class Builtin(object):
def __init__(self, name, impl, arity):
self.impl = impl
self.arity = arity
def invoke(self, arity, proc, arguments):
if not arity == self.arity:
return proc.crash('builtin called with %d arguments, expected %d')
return self.impl(proc, arguments)
def declare_builtin(name, arity):
def decorate(fn):
builtins[name] = Builtin(name, fn, arity)
return fn
return decorate
@declare_builtin('prelude.add', 2)
def add(proc, arguments):
first = arguments[0]
second = arguments[1]
if not isinstance(first, v.Int) and isinstance(second, v.Int):
return proc.crash('invalid arguments to add')
return v.Int(first.value + second.value)
@declare_builtin('prelude.print', 1)
def print_builtin(proc, arguments):
arg = arguments[0]
if isinstance(arg, v.String):
print arg.value
print arg.dump()
return v.tag('ok', [])
class Instr(object):
INSTRS = """
for (i, instr) in enumerate(Instr.INSTRS):
setattr(Instr, instr, i)
class Code(object):
def locals_count(self):
assert False, 'abstract'
def dump(self):
assert False, 'abstract'
def dump_instr(self, instr):
(t, a1, a2) = instr
return " %s %s %s" % (Instr.INSTRS[t], self.dump_digit(a1), self.dump_digit(a2))
def dump_digit(self, digit):
if digit < 0:
return '-'
return str(digit)
def dump_instrs(self):
out = ''
for (i, instr) in enumerate(self.instrs):
out += '%d:%s\n' % (i, self.dump_instr(instr))
return out
class RootCode(Code):
def __init__(self, instrs, local_names, symbols, constants, lambdas, trace_name):
self.instrs = instrs
self.local_names = local_names
self.local_count = len(local_names)
self.symbols = symbols
self.constants = constants
self.lambdas = lambdas
self.trace_name = trace_name
def brief_dump(self):
return '(root)'
def dump(self):
out = ''
out += "[constants]\n"
for (i, const) in enumerate(self.constants):
out += "#%d: %s\n" % (i, const.dump())
out += "[symbols]\n"
for (i, sym) in enumerate(self.symbols):
out += "#%d: %s\n" % (i, sym)
out += "[root l:%s]\n" % self.local_count
out += self.dump_instrs()
out += "[lambdas]\n"
for (i, l) in enumerate(self.lambdas):
out += l.dump()
return out
def locals_count(self):
return self.local_count
class Lambda(Code):
def __init__(self, instrs, index, arg_count, closed_count, local_count):
self.instrs = instrs
self.index = index
self.arg_count = arg_count
self.closed_count = closed_count
self.local_count = local_count
def locals_count(self):
return self.local_count
def brief_dump(self):
return "lambda#%d(a:%d, c:%d, l:%d)" % (self.index, self.arg_count, self.closed_count, self.local_count)
def dump(self):
out = ''
out += self.brief_dump() + ':\n'
out += self.dump_instrs()
return out
import tulip.semantic as s
import tulip.value as v
import tulip.byte as b
from tulip.byte import Instr
from tulip.runtime import Frame
from tulip.builtin import lookup_builtin
from tulip.debug import debug
debug_byte = debug.check('byte') or debug.check('runtime')
class CodeBuilder(object):
def __init__(self):
assert False, 'abstract'
def register_local(self, name, index):
def dump_instr(self, instr):
(t, a1, a2) = instr
return " %s %s %s" % (Instr.INSTRS[t], self.dump_digit(a1), self.dump_digit(a2))
def dump_digit(self, digit):
if digit < 0:
return '-'
return str(digit)
class RootBuilder(CodeBuilder):
def __init__(self):
self.code = []
self.local_names = {}
self.max_local_index = -1
def emit(self, instr, arg1, arg2):
self.code.append((instr, arg1, arg2))
def register_local(self, symbol, index):
self.local_names[index] = symbol
if index > self.max_local_index:
self.max_local_index = index
def dump(self):
out = ''
out += "root:\n"
for code in self.code:
out += self.dump_instr(code) + "\n"
return out
class LambdaBuilder(CodeBuilder):
def __init__(self, index, arg_count, closed_count, local_count):
self.index = index
self.arg_count = arg_count
self.closed_count = closed_count
self.local_count = local_count
self.clauses = []
def start_clause(self):
def emit(self, instr, arg1, arg2):
self.clauses[-1].append((instr, arg1, arg2))
def as_code(self):
next_index = 0
instrs = []
for clause in self.clauses:
next_index += len(clause)
for instruction in clause:
if instruction[0] == Instr.JUMP and instruction[1] == -1:
instrs.append((Instr.JUMP, next_index, -1))
return b.Lambda(instrs, self.index, self.arg_count, self.closed_count, self.local_count)
def dump(self):
out = ''
out += "lambda#%d(%d, %d, %d):\n" % (self.index, self.arg_count, self.closed_count, self.local_count)
for (j, clause) in enumerate(self.clauses):
out += " clause#%d:\n" % j
for instr in clause:
out += self.dump_instr(instr) + "\n"
return out
class ByteCompiler(s.Visitor):
def __init__(self):
self.constants = []
self.symbols = []
# TODO: non-lambda-scoped locals
self.root = RootBuilder()
self.lambdas = []
self.current_builder = self.root
def compile(self, ast):
code = self.as_code()
if debug_byte: print 'bytecode:\n', code.dump()
return code
def as_code(self):
code = b.RootCode(self.root.code, self.root.local_names, self.symbols, self.constants, self.lambdas, 'root')
for l in self.lambdas:
l.symbols = code.symbols
l.constants = code.constants
l.lambdas = code.lambdas
return code
def dump(self):
out = ''
out += "[constants]\n"
for (i, const) in enumerate(self.constants):
out += "#%d: %s\n" % (i, const.dump())
out += "[symbols]"
for (i, sym) in enumerate(self.symbols):
out += "#%d: \"%s\"\n" % (i, sym)
out += "[root]\n"
out += self.root.dump()
out += "[lambdas]\n"
for (i, l) in enumerate(self.lambdas):
out += l.dump()
return out
def sym(self, value):
for (i, c) in enumerate(self.symbols):
if c == value:
return i
return len(self.symbols) - 1
def emit(self, instr, arg1=-1, arg2=-1):
self.current_builder.emit(instr, arg1, arg2)
def visit_scoped_let(self, let):
self.current_builder.register_local(let.bind, let.index)
self.emit(Instr.STORL, let.index)
def visit_local_name(self, local_name):
self.current_builder.register_local(local_name.symbol, local_name.index)
self.emit(Instr.LOADL, local_name.index)
def visit_argument_name(self, argument_name):
self.emit(Instr.LOADA, argument_name.index)
def visit_closure_name(self, closure_name):
self.emit(Instr.LOADC, closure_name.index)
def visit_lambda(self, _):
assert False, 'cannot compile unresolved lambda'
def visit_closed_lambda(self, clam):
for c in clam.closed:
builder = LambdaBuilder(len(self.lambdas), len(clam.binders), len(clam.closed), len(clam.locals))
self.current_builder, old_builder = builder, self.current_builder
for clause in clam.clauses:
self.current_builder = old_builder
self.emit(Instr.LAMBD, len(self.lambdas) - 1, len(clam.closed))
def visit_block(self, block):
size = len(block.nodes)
for (i, n) in enumerate(block.nodes):
# pop off the value if it's a plain expression and not
# the last expression in the block
if isinstance(n, s.ScopedLetRec): continue
if isinstance(n, s.ScopedLet): continue
if i == size - 1: continue
self.emit(Instr.POP, 1)
# if the block ends with an assignment, load
# the assigned value as the result of the block.
last = block.nodes[-1]
if isinstance(last, s.ScopedLet):
self.emit(Instr.LOADL, last.index)
elif isinstance(last, s.ScopedLetRec):
(_, index) = last.names[-1]
self.emit(Instr.LOADL, index)
def visit_forward_name(self, forward_name):
# emit a dummy value, will be patched with PATCH later,
# see visit_scoped_letrec
def visit_scoped_letrec(self, letrec):
size = len(letrec.names)
to_patch = {}
for i in xrange(0, size):
value = letrec.values[i]
(name, index) = letrec.names[i]
for (findex, c) in enumerate(value.closed):
if isinstance(c, s.ForwardName):
to_patch[c.index].append((findex, index))
except KeyError:
to_patch[c.index] = [(findex, index)]
self.current_builder.register_local(name, i)
self.emit(Instr.STORL, i)
for (findex, vindex) in to_patch[index]:
self.emit(Instr.LOADL, i)
self.emit(Instr.LOADL, vindex)
self.emit(Instr.PATCH, findex)
except KeyError:
def visit_tag(self, tag):
self.emit(Instr.MKTAG, len(tag.values), self.sym(
def visit_match_assign(self, match_assign):
self.emit(Instr.STORL, match_assign.index)
def visit_constant(self, constant):
self.emit(Instr.CONST, len(self.constants) - 1)
def visit_builtin(self, builtin):
name =
impl = lookup_builtin(name)
self.constants.append(v.Builtin(name, impl))
self.emit(Instr.CONST, len(self.constants) - 1)
except KeyError:
self.error('missing builtin %s' % name)
def visit_apply(self, apply_node):
for i in xrange(1, len(apply_node.nodes)):
self.emit(Instr.APPLY, len(apply_node.nodes) - 1)
def visit_match_guard_tag(self, guard):
self.emit(Instr.TESTT, self.sym(, guard.arity)
self.emit(Instr.JUMP, -1)
def visit_tag_destruct(self, destruct):
self.emit(Instr.DETAG, destruct.index)
......@@ -4,11 +4,15 @@ from tulip.value import rpy_list, cons_list, cons_sym, cons_each, malformed
from tulip.compiler.symbols import *
from tulip.debug import debug
debug_compiler = debug.check('compiler')
from .lam import compile_lambda
from .expr import compile_expr
from .pattern import compile_pattern
from .block import compile_block
from .prelude import with_prelude
from .module import ModuleCompiler
class CompileError(StandardError):
def __init__(self, errors):
......@@ -67,13 +71,21 @@ class CompileContext(object):
def compile_block(self, b):
return compile_block(b, self)
def compile_base(expr):
if expr.matches_tag(nil_sym, 0):
def compile_root_expr(expr):
if expr.matches_tag('nil', 0):
return None
print u'compile: %s' % expr.dump()
context = CompileContext(Compiler(), None)
out = compile_expr(rpy_list(expr), context)
out = compile_block(expr, context)
if debug_compiler: print 'compiled: ', out.dump()
return out
def compile_module(expr):
if expr.matches_tag('nil', 0):
return None
compiler = Compiler()
out = compiler.compile_module(module)
return out
from tulip.compiler.util import split_lines, try_split
from tulip.symbol import sym
from tulip.compiler.util import split_lines, try_split, is_tok, get_tok
from tulip.lexer import Token
from tulip.compiler.pattern import LetPatternBuilder
from tulip.compiler.lam import split_section, compile_clauses
import tulip.semantic as s
class BlockElement(object):
def compile(self, context):
assert False, 'abstract'
class SimpleLet(BlockElement):
def __init__(self, name, expr): = name
self.expr = expr
def compile(self, context):
symbol = sym(get_tok(
return s.Let(symbol, context.compile_expr(self.expr))
class LambdaLet(BlockElement):
def __init__(self, name, clause):
self._name = name
self.clause = clause
def name(self):
return get_tok(self._name).value
# irrefutable pattern, like
# { .foo x = thing }
class IrrefutableLet(BlockElement):
def __init__(self, pattern, body):
self.pattern = pattern
self.body = body
def compile(self, context):
context.error('TODO: irrefutable patterns')
# temp = context.gensym(u'l')
# parts.append(s.Let(temp, bound_expr))
# builder = LetPatternBuilder(context)
# builder.compile_seq(before, temp)
# parts.extend(builder.directives)
class LambdaGroup(BlockElement):
def __init__(self, name, elements): = name
self.elements = elements
def compile(self, context):
clauses = [split_section(let.clause, Token.EQ) for let in self.elements]
lam = compile_clauses(clauses, context)
return s.Let(sym(, lam)
class BlockExpr(BlockElement):
def __init__(self, expr):
self.expr = expr
def compile(self, context):
return context.compile_expr(self.expr)
def compile_block(expr, context):
elements = parse_block_elements(expr, context)
parts = []
last_let_run = []
last_lambda_name = None
last_lambda_group = []
for element in parse_block_elements(expr, context):
if isinstance(element, LambdaLet):
if last_lambda_group and not == last_lambda_name:
# flush
parts.append(LambdaGroup(last_lambda_name, last_lambda_group))
last_lambda_name = None
last_lambda_name =
if last_lambda_group:
parts.append(LambdaGroup(last_lambda_name, last_lambda_group))
last_lambda_name = None
if last_lambda_group:
parts.append(LambdaGroup(last_lambda_name, last_lambda_group))
compiled = [p.compile(context) for p in parts]
if len(compiled) == 1 and not isinstance(compiled[0], s.Let):
return compiled[0]
return s.Block(compiled)
def parse_block_elements(expr, context):
elements = []
lines = split_lines(expr)
for (i, line) in enumerate(lines):
assert len(line) > 0, u'empty line!'
(equal_sign, before, after) = try_split(line, Token.EQ)
is_let = equal_sign is not None
if is_let and i == len(lines) - 1:
context.error(equal_sign, u'block can\'t end with assignment!')
if equal_sign:
if not before:
context.error('let with nothing before the `=` sign')
if is_let:
assert False, u'TODO: let'
# last_let_run.append(before, after)
if is_tok(before[0], Token.NAME):
if len(before) == 1:
yield SimpleLet(before[0], after)