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 test_string_hash_key(self) -> None: hello1 = monkey_object.String("Hello World") hello2 = monkey_object.String("Hello World") diff1 = monkey_object.String("My name is johnny") diff2 = monkey_object.String("My name is johnny") self.assertEqual(hello1.hash_key(), hello2.hash_key()) self.assertEqual(diff1.hash_key(), diff2.hash_key())
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 test_string_hash_key(): hello1 = mobject.String(value="Hello World") hello2 = mobject.String(value="Hello World") diff1 = mobject.String(value="different string") diff2 = mobject.String(value="different string") assert hello1.hash_key == hello2.hash_key, "strings with same content have different hash keys" assert hello1.hash_key != diff1.hash_key, "strings with different content have same hash keys" assert diff1.hash_key == diff2.hash_key, "strings with same content have different hash keys"
def _eval_string_infix_expression(self, operator: str, left: monkey_object.MonkeyObject, right: monkey_object.MonkeyObject) -> \ monkey_object.MonkeyObject: assert isinstance(left, monkey_object.String) assert isinstance(right, monkey_object.String) if operator != "+": return monkey_object.Error( f"unknown operator: {left.type_().value} {operator} {right.type_().value}" ) left_val = left.value right_val = right.value return monkey_object.String(left_val + right_val)
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(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 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
def eval_string_infix_expression(operator: str, left: MonkeyObject, right: MonkeyObject) -> MonkeyObject: if operator != "+": return new_error("unknown operator: {} {} {}", left.typ, operator, right.typ) return mobject.String(value=left.value + right.value)
def to_str(arg: MonkeyObject) -> mobject.String: if arg.typ not in (mobject.STRING_OBJ, mobject.INTEGER_OBJ, mobject.BOOLEAN_OBJ): return new_error("wrong argument type, 'to_str' does not accept {}", arg.typ) return mobject.String(value=str(arg.value))