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"
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())
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}" )
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}")
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)
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)
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)
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()}")
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)
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)
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)
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)
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)
def range_(n: mobject.Integer) -> mobject.Array: return mobject.Array( elements=[mobject.Integer(value=val) for val in range(n.value)])
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
def randint(n: mobject.Integer) -> mobject.Integer: return mobject.Integer(value=random.randint(0, n.value))
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