Source

Castile / src / castile / eval.py

Full commit
from castile.builtins import BUILTINS, TaggedValue


### Evaluator ###


OPS = {
    'and': (lambda x, y: x and y),
    'or': (lambda x, y: x or y),
    '+': (lambda x, y: x + y),
    '-': (lambda x, y: x - y),
    '*': (lambda x, y: x * y),
    '/': (lambda x, y: x / y),
    '==': (lambda x, y: x == y),
    '!=': (lambda x, y: x != y),
    '>=': (lambda x, y: x >= y),
    '<=': (lambda x, y: x <= y),
    '>': (lambda x, y: x > y),
    '<': (lambda x, y: x < y),
}


class StructDict(dict):
    def __init__(self, name, fields):
        dict.__init__(self)
        self.name = name
        self.fields = fields

    def __repr__(self):
        h = '{'
        if self.fields:
            for key in self.fields[:-1]:
                h += '%r: %r, ' % (key, self[key])
            h += '%r: %r' % (self.fields[-1], self[self.fields[-1]])
        return h + '}'


class FunctionReturn(Exception):
    pass


class WhileBreak(Exception):
    pass


# TODO not really a closure
class Closure(object):
    def __init__(self, program, ast):
        self.program = program
        assert callable(ast) or ast.tag == 'FunLit'
        self.ast = ast
        self.locals = {}

    def call(self, actuals):
        self.locals = {}
        if callable(self.ast):  # for builtins
            return self.ast(*actuals)
        formals = self.ast.children[0]
        assert formals.tag == 'Args'
        i = 0
        for formal in formals.children:
            assert formal.tag == 'Arg'
            self.locals[formal.value] = actuals[i]
            i += 1
        try:
            return self.eval()
        except FunctionReturn as e:
            return e.message

    def eval(self, ast=None):
        if ast is None:
            ast = self.ast.children[1]
        if ast.tag == 'Body':
            self.eval(ast.children[0])  # to collect locals
            return self.eval(ast.children[1])
        elif ast.tag == 'VarDecls':
            for child in ast.children:
                self.eval(child)
            return None
        elif ast.tag == 'VarDecl':
            name = ast.value
            v = self.eval(ast.children[0])
            self.locals[name] = v
            return None
        elif ast.tag == 'Block':
            v1 = None
            for stmt in ast.children:
                v1 = self.eval(stmt)
            return v1
        elif ast.tag == 'If':
            v1 = self.eval(ast.children[0])
            if len(ast.children) == 3:  # if-else
                if v1:
                    return self.eval(ast.children[1])
                else:
                    return self.eval(ast.children[2])
            else:  # just-if
                if v1:
                    self.eval(ast.children[1])
                return None
        elif ast.tag == 'Return':
            v1 = self.eval(ast.children[0])
            raise FunctionReturn(v1)
        elif ast.tag == 'Break':
            raise WhileBreak()
        elif ast.tag == 'While':
            v1 = self.eval(ast.children[0])
            try:
                while v1:
                    self.eval(ast.children[1])
                    v1 = self.eval(ast.children[0])
            except WhileBreak:
                pass
            return None
        elif ast.tag == 'Op':
            v1 = self.eval(ast.children[0])
            op = OPS[ast.value]
            v2 = self.eval(ast.children[1])
            return op(v1, v2)
        elif ast.tag == 'Not':
            return not self.eval(ast.children[0])
        elif ast.tag == 'VarRef':
            name = ast.value
            if name in self.locals:
                return self.locals[name]
            return self.program.stab[name]
        elif ast.tag in ['IntLit', 'StrLit', 'BoolLit']:
            return ast.value
        elif ast.tag == 'None':
            return None
        elif ast.tag == 'FunLit':
            return Closure(self.program, ast)
        elif ast.tag == 'FunCall':
            fun_val = self.eval(ast.children[0])
            actuals = [self.eval(arg) for arg in ast.children[1:]]
            return fun_val.call(actuals)
        elif ast.tag == 'Assignment':
            var_ref = ast.children[0]
            name = var_ref.value
            v = self.eval(ast.children[1])
            self.locals[name] = v
            return None
        elif ast.tag == 'Index':
            v = self.eval(ast.children[0])
            return v[ast.value]
        elif ast.tag == 'Make':
            v = StructDict(ast.value, [arg.value for arg in ast.children[1:]])
            for arg in ast.children[1:]:
                v[arg.value] = self.eval(arg.children[0])
            return v
        elif ast.tag == 'TypeCast':
            v = self.eval(ast.children[0])
            return TaggedValue(typeof(v), v)
        elif ast.tag == 'TypeCase':
            r = self.eval(ast.children[0])
            assert isinstance(r, TaggedValue)
            if r.tag == ast.value:
                var_ref = ast.children[0]
                name = var_ref.value
                saved_value = self.locals[name]
                self.locals[name] = r.value
                v = self.eval(ast.children[2])
                self.locals[name] = saved_value
            return None
        else:
            raise NotImplementedError(repr(ast))


def typeof(x):
    if x is None:
        return "Type(void:)"
    if isinstance(x, int):
        return "Type(integer:)"
    if isinstance(x, str):
        return "Type(string:)"
    elif isinstance(x, StructDict):
        return x.name
    else:
        return "wtf"


class Program(object):
    def __init__(self):
        self.stab = {}
        for (name, (value, type)) in BUILTINS.iteritems():
            if callable(value):
                value = Closure(self, value)
            self.stab[name] = value

    def load(self, ast):
        assert ast.tag == 'Program'
        # dummy closure for evaluating literals at toplevel
        toplevel = Closure(self, lambda x: x)
        for child in ast.children:
            if child.tag == 'Defn':
                self.stab[child.value] = toplevel.eval(child.children[0])

    def run(self):
        return self.stab['main'].call([])