Esempio n. 1
0
 def __call__(self, interpreter, arguments):
     self.environment = Environment(self.closure)
     for i in range(len(self.declaration.params)):
         self.environment.define(
             self.declaration.params[i].lexeme, arguments[i])
     try:
         interpreter._execute_block(self.declaration.body, self.environment)
     except Return as ret:
         return ret.value
Esempio n. 2
0
 def call(self, interpreter, arguments: List[object]):
     # create an environment for this call, inside the calling env
     self.call_env = Environment(self.closure)
     # Add the parameters to this env with their "calling" value
     for i in range(len(arguments)):
         self.call_env.define(self.fundec.params[i].lexeme, arguments[i])
     # call the function: execute the block wit the current env
     try:
         interpreter.executeblock(self.fundec.body, self.call_env)
     except ReturnException as exc:
         if self.fundec.functiontype is FunctionType.INIT:
             return self.closure.getat(0, Tk.lexeme_from_type[Tk.THIS])
         return exc.value
Esempio n. 3
0
    def call(self, interpreter, args: List[Any]):
        environment = Environment(self.environment)

        for token, expression in zip(self.declaration.params, args):
            environment.define(token.lexeme, expression)

        try:
            interpreter.execute_block(self.declaration.body, environment)
        except ReturnValue as return_val:
            if self.is_initializer:
                return self.environment.get_at(0, 'this')
            else:
                return return_val.val

        if self.is_initializer:
            return self.environment.get_at(0, 'this')
Esempio n. 4
0
class LoxFunction(LoxCallable):
    def __init__(self, declaration, closure):
        self.declaration = declaration
        self.closure = closure

    def __call__(self, interpreter, arguments):
        self.environment = Environment(self.closure)
        for i in range(len(self.declaration.params)):
            self.environment.define(
                self.declaration.params[i].lexeme, arguments[i])
        try:
            interpreter._execute_block(self.declaration.body, self.environment)
        except Return as ret:
            return ret.value

    @property
    def arity(self):
        return len(self.declaration.params)

    def __str__(self):
        return f"<fn {self.declaration.name.lexeme}>"
Esempio n. 5
0
class LoxFunction(LoxCallable):
    """Runtime for function."""
    def __init__(self, name: "LoxToken", fundec: FunctionExp, closure):
        self.name = name
        self.fundec = fundec
        self.call_env = None
        self.closure = closure

    def arity(self) -> int:
        return len(self.fundec.params)

    def call(self, interpreter, arguments: List[object]):
        # create an environment for this call, inside the calling env
        self.call_env = Environment(self.closure)
        # Add the parameters to this env with their "calling" value
        for i in range(len(arguments)):
            self.call_env.define(self.fundec.params[i].lexeme, arguments[i])
        # call the function: execute the block wit the current env
        try:
            interpreter.executeblock(self.fundec.body, self.call_env)
        except ReturnException as exc:
            if self.fundec.functiontype is FunctionType.INIT:
                return self.closure.getat(0, Tk.lexeme_from_type[Tk.THIS])
            return exc.value

    def bind(self, instance):
        """Bind the instance to the method."""
        this_env = Environment(self.closure)
        this_env.define(Tk.lexeme_from_type[Tk.THIS], instance)
        return LoxFunction(self.name, self.fundec, this_env)

    def __str__(self):
        if self.name is None:
            return "<function>"
        else:
            return "<function: " + self.name.lexeme + ">"
Esempio n. 6
0
 def bind(self, instance):
     env = Environment(self.environment)
     env.define('this', instance)
     return LoxCallable(self.declaration, env)
Esempio n. 7
0
class Interpreter(Visitor):
    # self.environment points towards the current environment, whereas
    # `global_env` always points to the outermost environment.
    # However, due to the implementation, it is shared amongst all instances.
    global_env = Environment()

    def __init__(self, lox):
        self.lox = lox
        self.global_env.define("clock", Clock())
        self.environment = self.global_env
        self.locals = {}

    def _execute(self, stmt):
        return self.visit(stmt)

    def _evaluate(self, expr):
        return self.visit(expr)

    def _lookup_variable(self, name, expr):
        distance = self.locals.get(expr, None)
        if distance != None:
            return self.environment.getat(distance, name)
        return self.global_env.get(name)

    def interpret(self, statements):
        try:
            for statement in statements:
                self._execute(statement)
        except RuntimeException as err:
            self.lox.runtime_error(err)

    def resolve(self, expr, depth):
        self.locals[expr] = depth

    def visit_LiteralExpr(self, expr):
        return expr.value

    def visit_GroupingExpr(self, expr):
        return self._evaluate(expr.expression)

    def visit_UnaryExpr(self, expr):
        right = self._evaluate(expr.right)

        if expr.operator.tokentype == TokenType.MINUS:
            _check_num_operand(expr.operator, right)
            return -float(right)
        elif expr.operator.tokentype == TokenType.BANG:
            return not _is_true(right)
        return

    def visit_BinaryExpr(self, expr):
        left = self._evaluate(expr.left)
        right = self._evaluate(expr.right)

        if expr.operator.tokentype == TokenType.PLUS:
            # This handles both the cases - when (left, right) are float or str
            try:
                return left + right
            except TypeError:
                raise RuntimeException(
            expr.operator, 'Operands must be two numbers or two strings.')
        elif expr.operator.tokentype == TokenType.MINUS:
            _check_num_operand(expr.operator, left, right)
            return left - right
        elif expr.operator.tokentype == TokenType.STAR:
            _check_num_operand(expr.operator, left, right)
            return left * right
        elif expr.operator.tokentype == TokenType.SLASH:
            _check_num_operand(expr.operator, left, right)
            if right == 0:
                raise RuntimeException(expr.operator, "Cannot divide by zero.")
            return left / right
        elif expr.operator.tokentype == TokenType.GREATER:
            _check_num_operand(expr.operator, left, right)
            return left > right
        elif expr.operator.tokentype == TokenType.GREATER_EQUAL:
            _check_num_operand(expr.operator, left, right)
            return left >= right
        elif expr.operator.tokentype == TokenType.LESS:
            _check_num_operand(expr.operator, left, right)
            return left < right
        elif expr.operator.tokentype == TokenType.LESS_EQUAL:
            _check_num_operand(expr.operator, left, right)
            return left <= right
        elif expr.operator.tokentype == TokenType.BANG_EQUAL:
            return left != right
        elif expr.operator.tokentype == TokenType.EQUAL_EQUAL:
            return left == right
        return

    def visit_VariableExpr(self, expr):
        return self._lookup_variable(expr.name, expr)

    def visit_AssignExpr(self, expr):
        value = self._evaluate(expr.value)
        distance = self.locals.get(expr, None)
        if distance != None:
            self.environment.assignat(distance, expr.name, value)
        else:
            self.global_env.assign(expr.name, value)
        return value

    def visit_LogicalExpr(self, expr):
        left = self._evaluate(expr.left)
        if expr.operator.tokentype == TokenType.OR:
            if _is_true(left):
                return left
        else: # AND
            if not _is_true(left):
                return left
        return self._evaluate(expr.right)

    def visit_CallExpr(self, expr):
        callee = self._evaluate(expr.callee)
        arguments = []
        for argument in expr.arguments:
            arguments.append(self._evaluate(argument))
        # callee - what would be the type of callee?
        if not isinstance(callee, LoxCallable):
            raise RuntimeException(
    expr.paren, "Can only call functions and classes.")

        # The original author, while writing this compiler in Java typecasted
        # the callee to LoxCallable, even after checking `isinstance(callee,
        # LoxCallable`. It isn't clear why. But, I'm apprehensive that this was
        # to satisfy some weird Inheritance rules of Java.

        # Since there is no explicit type casting mechanism in Python, (or there
        # is, and I'm not aware of it.) I will assume that the `isinstance`
        # check is enough and won't do the check.

        # The following line is commented out for documentation reasons.
        # function = LoxCallable(callee)

        if len(arguments) != callee.arity:
            raise RuntimeException(
    expr.paren,
    f"Expected {function.arity} arguments, but got {len(arguments)}.")
        return callee.__call__(self, arguments)

    def visit_ExpressionStmt(self, stmt):
        self._evaluate(stmt.expression)

    def visit_PrintStmt(self, stmt):
        print(stringify(self._evaluate(stmt.expression)))

    def visit_VarStmt(self, stmt):
        value = None
        if stmt.initializer != None:
            value = self._evaluate(stmt.initializer)
        self.environment.define(stmt.name.lexeme, value)

    def _execute_block(self, statements, environment):
        previous = self.environment
        try:
            self.environment = environment
            for statement in statements:
                self._execute(statement)
        finally:
            self.environment = previous

    def visit_BlockStmt(self, stmt):
        self._execute_block(stmt.statements, Environment(self.environment))

    def visit_IfStmt(self, stmt):
        if _is_true(self._evaluate(stmt.condition)):
            self._execute(stmt.then_branch)
        elif stmt.else_branch != None:
            self._execute(stmt.else_branch)

    def visit_WhileStmt(self, stmt):
        while _is_true(self._evaluate(stmt.condition)):
            self._execute(stmt.body)

    def visit_FunctionStmt(self, stmt):
        function = LoxFunction(stmt, self.environment)
        self.environment.define(stmt.name.lexeme, function)

    def visit_ReturnStmt(self, stmt):
        value = stmt.value
        if value != None:
            value = self._evaluate(stmt.value)
        raise Return(value)
Esempio n. 8
0
 def visit_BlockStmt(self, stmt):
     self._execute_block(stmt.statements, Environment(self.environment))
Esempio n. 9
0
 def bind(self, instance):
     """Bind the instance to the method."""
     this_env = Environment(self.closure)
     this_env.define(Tk.lexeme_from_type[Tk.THIS], instance)
     return LoxFunction(self.name, self.fundec, this_env)