Beispiel #1
0
    def call(self, interpreter, arguments):
        environment = Environment(self.closure)

        for (param, val) in zip(self.declaration.params, arguments):
            environment.define(param.lexeme, val)

        try:
            interpreter.execute_block(self.declaration.body.statements,
                                      environment)
        except ReturnException as ret:
            if not self.initializer:
                return ret.value

        if self.initializer:
            return self.closure.get_at(0, 'this')
Beispiel #2
0
    def visit_class_stmt(self, stmt: Class) -> object:
        environment = self.env
        superclass = None
        if stmt.superclass:
            superclass = self.evaluate(stmt.superclass)

            if not isinstance(superclass, LoxClass):
                msg = "Superclass must be a class."
                raise PloxRuntimeError(stmt.superclass.name, msg)

        environment.define(stmt.name.lexeme, None)

        if stmt.superclass:
            environment = Environment(environment)
            environment.define('super', superclass)

        methods = {}
        for method in stmt.methods:
            is_initializer = method.name.lexeme == 'init'
            is_getter = method.getter
            function = LoxFunction(method, environment, is_initializer,
                                   is_getter)
            methods[method.name.lexeme] = function

        klass = LoxClass(stmt.name.lexeme, superclass, methods)

        if superclass:
            environment = environment.enclosing

        environment.assign(stmt.name, klass)
        return None
Beispiel #3
0
    def call(self, interpreter, arguments):
        environment = Environment(self.closure)

        for param, arg in zip(self.declaration.params, arguments):
            environment.define(param.lexeme, None)
            environment.assign(param, arg)

        try:
            interpreter._execute_block(self.declaration.body, environment)
        except PloxReturnException as return_value:
            if self.is_initializer:
                return self.closure.get_at(0, 'this')
            return return_value.value

        if self.is_initializer:
            return self.closure.get_at(0, "this")
        return None
Beispiel #4
0
 def visit_block_stmt(self, stmt: Stmt.Block) -> object:
     self._execute_block(stmt.statements, Environment(self.env))
     return None
Beispiel #5
0
 def __init__(self, error):
     self.error = error
     self.env = Environment()
     self.globals = self.env
     self.globals.define('clock', _Clock())
     self._locals = {}
Beispiel #6
0
class Interpreter(Expr.ExprVisitor, Stmt.StmtVisitor):
    def __init__(self, error):
        self.error = error
        self.env = Environment()
        self.globals = self.env
        self.globals.define('clock', _Clock())
        self._locals = {}

    def evaluate(self, expr: Expr.Expr):
        return expr.accept(self)

    def visit_block_stmt(self, stmt: Stmt.Block) -> object:
        self._execute_block(stmt.statements, Environment(self.env))
        return None

    def visit_break_stmt(self, stmt: Stmt.Break) -> object:
        raise _PloxBreakException()

    def visit_class_stmt(self, stmt: Class) -> object:
        environment = self.env
        superclass = None
        if stmt.superclass:
            superclass = self.evaluate(stmt.superclass)

            if not isinstance(superclass, LoxClass):
                msg = "Superclass must be a class."
                raise PloxRuntimeError(stmt.superclass.name, msg)

        environment.define(stmt.name.lexeme, None)

        if stmt.superclass:
            environment = Environment(environment)
            environment.define('super', superclass)

        methods = {}
        for method in stmt.methods:
            is_initializer = method.name.lexeme == 'init'
            is_getter = method.getter
            function = LoxFunction(method, environment, is_initializer,
                                   is_getter)
            methods[method.name.lexeme] = function

        klass = LoxClass(stmt.name.lexeme, superclass, methods)

        if superclass:
            environment = environment.enclosing

        environment.assign(stmt.name, klass)
        return None

    def visit_expression_stmt(self, stmt: Stmt.Expression) -> object:
        self.evaluate(stmt.expression)
        return None

    def visit_function_stmt(self, stmt: Stmt.Function) -> object:
        if not stmt.anonymous:
            function = LoxFunction(stmt, self.env, False)
            self.env.define(stmt.name.lexeme, function)
            return None

        # anonymous function from expression
        return LoxFunction(stmt, self.env, False)

    def visit_if_stmt(self, stmt: Stmt.If) -> object:
        if is_plox_truthy(self.evaluate(stmt.condition)):
            self._execute(stmt.then_branch)
        elif stmt.else_branch is not None:
            self._execute(stmt.else_branch)

        return None

    def visit_print_stmt(self, stmt: Stmt.Print) -> object:
        value = self.evaluate(stmt.expression)
        print(stringify(value))
        return None

    def visit_return_stmt(self, stmt: Stmt.Return) -> object:
        value = None
        if stmt.value is not None:
            value = self.evaluate(stmt.value)
        raise PloxReturnException(value)

    def visit_var_stmt(self, stmt: Stmt.Var) -> object:
        value = None

        if stmt.initializer is not None:
            value = self.evaluate(stmt.initializer)

        self.env.define(stmt.name.lexeme, value)
        return None

    def visit_while_stmt(self, stmt: Stmt.While) -> object:
        while is_plox_truthy(self.evaluate(stmt.condition)):
            try:
                self._execute(stmt.body)
            except _PloxBreakException:
                break
        return None

    def visit_assign_expr(self, expr: Expr.Assign) -> object:
        value = self.evaluate(expr.value)

        if expr in self._locals:
            distance = self._locals[expr]
            self.env.assign_at(distance, expr.name, value)
        else:
            self.globals.assign(expr.name, value)

        return value

    def visit_ternary_expr(self, expr: Expr.Ternary) -> object:
        condition = self.evaluate(expr.condition)
        if is_plox_truthy(condition):
            return self.evaluate(expr.then_branch)
        else:
            return self.evaluate(expr.else_branch)

    def visit_logical_expr(self, expr: Expr.Logical) -> object:
        left = self.evaluate(expr.left)
        if expr.operator.type == TokenType.OR:
            if is_plox_truthy(left):
                return left
        else:
            if not is_plox_truthy(left):
                return left

        return self.evaluate(expr.right)

    def visit_variable_expr(self, expr: Expr.Variable) -> object:
        return self._look_up_variable(expr.name, expr)

    def visit_literal_expr(self, expr: Expr.Literal) -> object:
        return expr.value

    def visit_get_expr(self, expr: Expr.Get) -> object:
        obj = self.evaluate(expr.objct)
        if isinstance(obj, LoxInstance):
            result = obj.get(expr.name)
            if isinstance(result, LoxFunction) and result.is_getter:
                result = result.call(self, [])
            return result

        raise PloxRuntimeError(expr.name, "Only instances have properties.")

    def visit_grouping_expr(self, expr: Expr.Grouping) -> object:
        return self.evaluate(expr.expression)

    def visit_set_expr(self, expr: Expr.Set) -> object:
        objct = self.evaluate(expr.objct)

        if not isinstance(objct, LoxInstance):
            raise PloxRuntimeError(expr.name, "Only instances have fields.")
        value = self.evaluate(expr.value)
        objct.set(expr.name, value)
        return value

    def visit_subscript_expr(self, expr: Expr.Subscript) -> object:
        obj = self.evaluate(expr.objct)
        if isinstance(obj, str):
            return obj[self.evaluate(expr.index)]
        elif isinstance(obj, LoxInstance):
            subscript = obj.find_method('__get__')
            if subscript:
                return subscript.call(self, [self.evaluate(expr.index)])

        raise PloxRuntimeError(expr.bracket, "Subscript not supported.")

    def visit_super_expr(self, expr: Expr.Super) -> object:
        distance = self._locals.get(expr)
        superclass = self.env.get_at(distance, "super")

        # "this" is always one level nearer than "super"'s environment.
        objct = self.env.get_at(distance - 1, "this")
        method = superclass.find_method(expr.method.lexeme)

        if not method:
            msg = f"Undefined property '{expr.method.lexeme}'."
            raise PloxRuntimeError(expr.method, msg)

        return method.bind(objct)

    def visit_this_expr(self, expr: Expr.This) -> object:
        return self._look_up_variable(expr.keyword, expr)

    def visit_unary_expr(self, expr: Expr.Unary) -> object:
        right = self.evaluate(expr.right)
        opt = expr.operator.type
        if opt == TokenType.MINUS:
            check_number_operand(expr.operator, right)
            return -right
        elif opt == TokenType.BANG:
            return is_plox_truthy(right).notify()

        return None

    def visit_binary_expr(self, expr: Expr.Binary) -> object:
        left = self.evaluate(expr.left)
        right = self.evaluate(expr.right)
        opt = expr.operator.type

        if opt == TokenType.PLUS:
            if is_plox_number(left) and is_plox_number(right):
                return BINARY_OPS[opt](left, right)
            if is_plox_string(left) and is_plox_string(right):
                return BINARY_OPS[opt](left, right)
            if is_plox_string(left) or is_plox_string(right):
                return BINARY_OPS[opt](str(left), str(right))
            raise PloxTypeError(expr.operator, left, right)

        elif opt in EQUALITY_TOKENS:
            return BINARY_OPS[opt](left, right)

        elif opt in COMPARISON_TOKENS:
            if is_plox_number(left) and is_plox_number(right):
                return BINARY_OPS[opt](left, right)
            if is_plox_string(left) and is_plox_string(right):
                return BINARY_OPS[opt](left, right)
            raise PloxTypeError(expr.operator, left, right)

        elif opt in BINARY_OPS:
            check_number_operands(expr.operator, left, right)
            if opt == TokenType.SLASH and right == 0:
                raise PloxRuntimeError(expr.operator, 'Division by zero.')

            return BINARY_OPS[opt](left, right)

        return None

    def visit_call_expr(self, expr: Expr.Call) -> object:
        func = self.evaluate(expr.callee)
        arguments = []
        for arg in expr.arguments:
            arguments.append(self.evaluate(arg))

        if not isinstance(func, LoxCallable):
            msg = "Can only call functions and classes."
            raise PloxRuntimeError(expr.paren, msg)

        if len(arguments) != func.arity():
            msg = f"Expected {func.arity()} arguments but got {len(arguments)}."
            raise PloxRuntimeError(expr.paren, msg)

        return func.call(self, arguments)

    def _execute(self, stmt):
        stmt.accept(self)

    def resolve(self, expr: Expr.Expr, depth: int):
        self._locals[expr] = depth

    def _look_up_variable(self, name: Token, expr: Expr.Expr):
        if expr in self._locals:
            distance = self._locals[expr]
            return self.env.get_at(distance, name.lexeme)
        else:
            return self.globals.get(name)

    def _execute_block(self, statements, environment):
        previous = self.env
        try:
            self.env = environment

            for stmt in statements:
                self._execute(stmt)
        finally:
            self.env = previous

    def interpret(self, statements):
        try:
            for statement in statements:
                self._execute(statement)
        except PloxRuntimeError as e:
            self.error(e)
Beispiel #7
0
    def bind(self, instance):
        environment = Environment(self.closure)
        environment.define('this', instance)

        return LoxFunction(self.declaration, environment, self.initializer)