Beispiel #1
0
def test_int_hash_key():
    a1 = mobject.Integer(value=1)
    a2 = mobject.Integer(value=1)
    diff = mobject.Integer(value=30)

    assert a1.hash_key == a2.hash_key, "int with same content have different hash keys"
    assert a1.hash_key != diff.hash_key, "int with different content have same hash keys"
Beispiel #2
0
 def test_integer_hash_key(self) -> None:
     hello1 = monkey_object.Integer(1)
     hello2 = monkey_object.Integer(1)
     diff1 = monkey_object.Integer(2)
     diff2 = monkey_object.Integer(2)
     self.assertEqual(hello1.hash_key(), hello2.hash_key())
     self.assertEqual(diff1.hash_key(), diff2.hash_key())
Beispiel #3
0
 def _eval_integer_infix_expression(self, operator: str, left: monkey_object.MonkeyObject,
                                    right: monkey_object.MonkeyObject) -> \
                                    monkey_object.MonkeyObject:
     # Called from _eval_infix_expression which type type assertion. mypy
     # cannot infer, so we have to explicitly guard with asserts.
     assert isinstance(left, monkey_object.Integer)
     assert isinstance(right, monkey_object.Integer)
     left_val = left.value
     right_val = right.value
     if operator == "+":
         return monkey_object.Integer(left_val + right_val)
     if operator == "-":
         return monkey_object.Integer(left_val - right_val)
     if operator == "*":
         return monkey_object.Integer(left_val * right_val)
     if operator == "/":
         return monkey_object.Integer(left_val // right_val)
     if operator == "<":
         return self._native_bool_to_boolean_object(left_val < right_val)
     if operator == ">":
         return self._native_bool_to_boolean_object(left_val > right_val)
     if operator == "==":
         return self._native_bool_to_boolean_object(left_val == right_val)
     if operator == "!=":
         return self._native_bool_to_boolean_object(left_val != right_val)
     return monkey_object.Error(
         f"unknown operator: {left.type_().value} {operator} {right.type_().value}"
     )
Beispiel #4
0
def _len(args: List[monkey_object.MonkeyObject]) -> monkey_object.MonkeyObject:
    if len(args) != 1:
        return monkey_object.Error(
            f"wrong number of arguments. Got {len(args)}, want 1")
    if isinstance(args[0], monkey_object.String):
        return monkey_object.Integer(len(args[0].value))
    if isinstance(args[0], monkey_object.Array):
        return monkey_object.Integer(len(args[0].elements))
    return monkey_object.Error(
        f"argument to 'len' not supported. Got {args[0].type_().value}")
Beispiel #5
0
def len_builtin(*args) -> mobject.Integer:
    err = check_args_len(args, 1)
    if err is not None:
        return err
    if isinstance(args[0], mobject.String):
        return mobject.Integer(value=len(args[0].value))
    elif isinstance(args[0], mobject.Array):
        return mobject.Integer(value=len(args[0].elements))
    else:
        return new_error("argument to 'len' not supported, got {}",
                         args[0].typ)
Beispiel #6
0
 def _eval_minus_prefix_operator_expression(self, right: monkey_object.MonkeyObject) -> \
                                            monkey_object.MonkeyObject:
     if right.type_() != monkey_object.ObjectType.INTEGER:
         return monkey_object.Error(
             f"unknown operator: -{right.type_().value}")
     value = cast(monkey_object.Integer, right).value
     return monkey_object.Integer(-value)
Beispiel #7
0
def test_hash_literals():
    input = """
    let two = "two";
    {
        "one": 10 - 9,
        two: 1 + 1,
        "thr" + "ee": 6/2,
        4: 4,
        true: 5,
        false: 6
    }
    """

    expected = {
        mobject.String(value="one").hash_key: 1,
        mobject.String(value="two").hash_key: 2,
        mobject.String(value="three").hash_key: 3,
        mobject.Integer(value=4).hash_key: 4,
        mobject.Boolean(value=True).hash_key: 5,
        mobject.Boolean(value=False).hash_key: 6
    }

    evaluated = eval_test(input)
    assert type(evaluated
                ) == mobject.Hash, f"object is not Hash, got {type(evaluated)}"
    assert len(evaluated.pairs) == len(
        expected
    ), f"hash has wrong number of pairs. got {len(evaluated.pairs)}, want {len(expected)}"

    for k, v in expected.items():
        pair = evaluated.pairs.get(k)
        assert pair is not None, f"no pair for key {k} can be found in hash"
        integer_object_test(pair.value, v)
Beispiel #8
0
 def execute_minus_operator(self):
     operand = self.pop()
     if operand.type() == monkey_object.ObjectType.INTEGER:
         self.push(monkey_object.Integer(-operand.value))
     else:
         raise RuntimeError(
             f"unsupported type for negation: {operand.type()}")
Beispiel #9
0
 def test_hash_literals(self):
     text = """
         let two = "two";
         {
             "one": 10 - 9,
             two: 1 + 1,
             "thr" + "ee": 6 / 2,
             4: 4,
             true: 5,
             false: 6
         }
     """
     evaluated = self.eval_setup(text)
     if isinstance(evaluated, monkey_object.Error):
         print(f"ERROR: {evaluated.message}")
     assert isinstance(evaluated, monkey_object.Hash)
     expected = {
         monkey_object.String("one").hash_key(): 1,
         monkey_object.String("two").hash_key(): 2,
         monkey_object.String("three").hash_key(): 3,
         monkey_object.Integer(4).hash_key(): 4,
         TRUE.hash_key(): 5,
         FALSE.hash_key(): 6,
     }
     assert len(evaluated.pairs) == len(expected)
     for expected_pair in evaluated.pairs.values():
         pair = evaluated.pairs[expected_pair.key.hash_key()]
         self.check_integer_object(pair.value, expected_pair.value.value)
Beispiel #10
0
def eval_integer_infix_expression(operator: str, left: MonkeyObject,
                                  right: MonkeyObject) -> MonkeyObject:
    if operator == "+":
        return mobject.Integer(value=left.value + right.value)
    elif operator == "-":
        return mobject.Integer(value=left.value - right.value)
    elif operator == "*":
        return mobject.Integer(value=left.value * right.value)
    elif operator == "/":
        return mobject.Integer(value=left.value // right.value)
    elif operator == "<":
        return native_bool_to_boolean_object(left.value < right.value)
    elif operator == ">":
        return native_bool_to_boolean_object(left.value > right.value)
    elif operator == "!=":
        return native_bool_to_boolean_object(left.value != right.value)
    elif operator == "==":
        return native_bool_to_boolean_object(left.value == right.value)
    else:
        return new_error("unknown operator: {} {} {}", left.typ, operator,
                         right.typ)
Beispiel #11
0
 def execute_binary_integer_operation(self, op: code.Opcode,
                                      left: monkey_object.Object,
                                      right: monkey_object.Object):
     result = None
     if op == code.Opcode.ADD:
         result = left.value + right.value
     elif op == code.Opcode.SUB:
         result = left.value - right.value
     elif op == code.Opcode.MUL:
         result = left.value * right.value
     elif op == code.Opcode.DIV:
         result = left.value // right.value
     else:
         raise RuntimeError(f"unknown integer operator: {op}")
     self.push(monkey_object.Integer(result))
 def test_hash_literals(self) -> None:
     source = """let two = "two";
                 {
                   "one": 10 - 9,
                   two: 1 + 1,
                   "thr" + "ee": 6 / 2,
                   4: 4,
                   true: 5,
                   false: 6
                 }"""
     evaluated = cast(monkey_object.Hash, self._test_eval(source))
     self.assertIsInstance(evaluated, monkey_object.Hash)
     expected: Dict[monkey_object.HashKey, int] = {
         monkey_object.String("one").hash_key(): 1,
         monkey_object.String("two").hash_key(): 2,
         monkey_object.String("three").hash_key(): 3,
         monkey_object.Integer(4).hash_key(): 4,
         Evaluator.true.hash_key(): 5,
         Evaluator.false.hash_key(): 6
     }
     self.assertEqual(len(evaluated.pairs), len(expected))
     for key, value in expected.items():
         pair = evaluated.pairs[key]
         self._test_integer_object(pair.value, value)
Beispiel #13
0
def eval_minus_prefix_operator_expression(right: MonkeyObject) -> MonkeyObject:
    if right.typ != mobject.INTEGER_OBJ:
        return new_error("unknown operator: -{}", right.typ)

    return mobject.Integer(value=-right.value)
Beispiel #14
0
    def compile(self, node):
        if isinstance(node, ast.Program):
            for s in node.statements:
                self.compile(s)
        elif isinstance(node, ast.BlockStatement):
            for s in node.statements:
                self.compile(s)
        elif isinstance(node, ast.ExpressionStatement):
            self.compile(node.expression)
            self._emit(Opcode.POP)
        elif isinstance(node, ast.LetStatement):
            self.compile(node.value)
            symbol = self._symbol_table.define(node.name.value)
            self._emit(Opcode.SET_GLOBAL, symbol.index)
        elif isinstance(node, ast.IfExpression):
            self.compile(node.condition)
            # this jump offset is bogus.
            jump_not_truthy_pos = self._emit(Opcode.JUMP_NOT_TRUTHY, 9999)
            self.compile(node.consequence)
            if self._last_instruction_is_pop():
                self._remove_last_pop()
            jump_pos = self._emit(Opcode.JUMP, 9999)
            after_consequence_pos = len(self._instructions)
            self._change_operand(jump_not_truthy_pos, after_consequence_pos)
            if node.alternative is None:
                self._emit(Opcode.NULL)
            else:
                self.compile(node.alternative)
                if self._last_instruction_is_pop():
                    self._remove_last_pop()
            after_alternative_pos = len(self._instructions)
            self._change_operand(jump_pos, after_alternative_pos)
        elif isinstance(node, ast.InfixExpression):
            if node.operator == "<":
                self.compile(node.right)
                self.compile(node.left)
                self._emit(Opcode.GREATER_THAN)
                return
            self.compile(node.left)
            self.compile(node.right)
            if node.operator == "+":
                self._emit(Opcode.ADD)
            elif node.operator == "-":
                self._emit(Opcode.SUB)
            elif node.operator == "*":
                self._emit(Opcode.MUL)
            elif node.operator == "/":
                self._emit(Opcode.DIV)
            elif node.operator == ">":
                self._emit(Opcode.GREATER_THAN)
            elif node.operator == "==":
                self._emit(Opcode.EQUAL)
            elif node.operator == "!=":
                self._emit(Opcode.NOT_EQUAL)
            else:
                raise RuntimeError(f"unknown operator {node.operator}")
        elif isinstance(node, ast.PrefixExpression):
            self.compile(node.right)
            if node.operator == "!":
                self._emit(Opcode.BANG)
            elif node.operator == "-":
                self._emit(Opcode.MINUS)
            else:
                raise RuntimeError(f"unknown operator {node.operator}")
        elif isinstance(node, ast.IntegerLiteral):
            integer = monkey_object.Integer(node.value)
            self._emit(Opcode.CONSTANT, self._add_constant(integer))
        elif isinstance(node, ast.Boolean):
            if node.value:
                self._emit(Opcode.TRUE)
            else:
                self._emit(Opcode.FALSE)
        elif isinstance(node, ast.Identifier):
            try:
                symbol = self._symbol_table.resolve(node.value)
            except KeyError:
                raise RuntimeError(f"undefined variable {node.value}")

            self._emit(Opcode.GET_GLOBAL, symbol.index)
Beispiel #15
0
def range_(n: mobject.Integer) -> mobject.Array:
    return mobject.Array(
        elements=[mobject.Integer(value=val) for val in range(n.value)])
Beispiel #16
0
    def eval(self, node: ast.Node,
             env: environment.Environment) -> monkey_object.MonkeyObject:
        # statements
        if isinstance(node, ast.Program):
            return self._eval_program(
                cast(List[ast.BlockStatement], node.statements), env)
        if isinstance(node, ast.ExpressionStatement):
            return self.eval(node.expression, env)
        if isinstance(node, ast.BlockStatement):
            return self._eval_block_statement(
                cast(List[ast.Statement], node.statements), env)
        if isinstance(node, ast.ReturnStatement):
            value = self.eval(node.return_value, env)

            # Check for errors whenever Eval is called inside Eval in order to
            # stop errors from being passed around and bubbling up far from
            # their origin.
            if self._is_error(value):
                return value
            return monkey_object.ReturnValue(value)
        if isinstance(node, ast.LetStatement):
            value = self.eval(node.value, env)
            if self._is_error(value):
                return value
            return env.set(node.name.value, value)

        # expressions
        if isinstance(node, ast.IntegerLiteral):
            return monkey_object.Integer(node.value)
        if isinstance(node, ast.StringLiteral):
            return monkey_object.String(node.value)
        if isinstance(node, ast.Boolean):
            return self._native_bool_to_boolean_object(node.value)
        if isinstance(node, ast.PrefixExpression):
            right = self.eval(node.right, env)
            if self._is_error(right):
                return right
            return self._eval_prefix_expression(node.operator, right)
        if isinstance(node, ast.InfixExpression):
            left = self.eval(node.left, env)
            if self._is_error(left):
                return left
            right = self.eval(node.right, env)
            if self._is_error(right):
                return right
            return self._eval_infix_expression(node.operator, left, right)
        if isinstance(node, ast.IfExpression):
            return self._eval_if_expression(node, env)
        if isinstance(node, ast.Identifier):
            return self._eval_identifier(node, env)
        if isinstance(node, ast.FunctionLiteral):
            params = node.parameters
            body = node.body
            return monkey_object.Function(params, body, env)
        if isinstance(node, ast.CallExpression):
            function = self.eval(node.function, env)
            if self._is_error(function):
                return function
            args = self._eval_expressions(node.arguments, env)
            if len(args) == 1 and self._is_error(args[0]):
                return args[0]
            return self._apply_function(function, args)
        if isinstance(node, ast.ArrayLiteral):
            elements = self._eval_expressions(node.elements, env)
            if len(elements) == 1 and self._is_error(elements[0]):
                return elements[0]
            return monkey_object.Array(elements)
        if isinstance(node, ast.IndexExpression):
            left = self.eval(node.left, env)
            if self._is_error(left):
                return left
            index = self.eval(node.index, env)
            if self._is_error(index):
                return index
            return self._eval_index_expression(left, index)
        if isinstance(node, ast.HashLiteral):
            return self._eval_hash_literal(node, env)

        raise NotImplementedError
Beispiel #17
0
def randint(n: mobject.Integer) -> mobject.Integer:
    return mobject.Integer(value=random.randint(0, n.value))
Beispiel #18
0
def eval(node: ast.Node, env: mobject.Environment) -> MonkeyObject:
    typ = type(node)
    if typ == ast.Program:
        return eval_program(node, env)
    elif typ == ast.ExpressionStatement:
        return eval(node.expression, env)
    elif typ == ast.IntegerLiteral:
        return mobject.Integer(value=node.value)
    elif typ == ast.Boolean:
        return native_bool_to_boolean_object(node.value)
    elif typ == ast.StringLiteral:
        return mobject.String(value=node.value)
    elif typ == ast.Identifier:
        return eval_identifier(node, env)
    elif typ == ast.PrefixExpression:
        right = eval(node.right, env)
        if is_error(right):
            return right
        return eval_prefix_expression(node.operator, right)
    elif typ == ast.InfixExpression:
        left = eval(node.left, env)
        if is_error(left):
            return left
        right = eval(node.right, env)
        if is_error(right):
            return right
        return eval_infix_expression(node.operator, left, right)
    elif typ == ast.AssignExpression:
        value = eval(node.value, env)
        if is_error(value):
            return value
        val = env.reset(node.name.value, value)
        if val is None:
            return new_error("variable '{}' does not exist. Can't reassign",
                             node.name.value)
        return val
    elif typ == ast.BlockStatement:
        return eval_block_statement(node, env)
    elif typ == ast.IfExpression:
        return eval_if_expression(node, env)
    elif typ == ast.ForExpression:
        return eval_for_expression(node, env)
    elif typ == ast.WhileExpression:
        return eval_while_expression(node, env)
    elif typ == ast.ReturnStatement:
        val = eval(node.return_value, env)
        if is_error(val):
            return val
        return mobject.ReturnValue(val)
    elif typ == ast.BreakStatement:
        return mobject.Break()
    elif typ == ast.ContinueStatement:
        return mobject.Continue()
    elif typ == ast.LetStatement:
        val = eval(node.value, env)
        if is_error(val):
            return val
        env.set(node.name.value, val)
    elif typ == ast.FunctionLiteral:
        params = node.parameters
        body = node.body
        return mobject.Function(parameters=params, body=body, env=env)
    elif typ == ast.CallExpression:
        function = eval(node.function, env)
        if is_error(function):
            return function
        args = eval_expressions(node.arguments, env)
        if len(args) == 1 and is_error(args[0]):
            return args[0]
        return apply_function(function, args)
    elif typ == ast.ArrayLiteral:
        elements = eval_expressions(node.elements, env)
        if len(elements) == 1 and is_error(elements[0]):
            return elements[0]
        return mobject.Array(elements=elements)
    elif typ == ast.IndexExpression:
        left = eval(node.left, env)
        if is_error(left):
            return left
        index = eval(node.index, env)
        if is_error(index):
            return index
        return eval_index_expression(left, index)
    elif typ == ast.HashLiteral:
        return eval_hash_literal(node, env)
    else:
        return NULL