def _eval_infix_expression( self, operator: str, left: monkey_object.MonkeyObject, right: monkey_object.MonkeyObject) -> monkey_object.MonkeyObject: if left.type_() == monkey_object.ObjectType.INTEGER and \ right.type_() == monkey_object.ObjectType.INTEGER: return self._eval_integer_infix_expression(operator, left, right) if left.type_() == monkey_object.ObjectType.STRING and \ right.type_() == monkey_object.ObjectType.STRING: return self._eval_string_infix_expression(operator, left, right) # For booleans we can use reference comparison to check for equality. It # works because of our singleton True and False instances but wouldn't # work for integers since they aren't singletons. 5 == 5 would be false # when comparing references. To compare integer we must unwrap the # integer stored inside each Integer object and compare their values. if operator == "==": return self._native_bool_to_boolean_object(left == right) if operator == "!=": return self._native_bool_to_boolean_object(left != right) if left.type_() != right.type_(): return monkey_object.Error( f"type mismatch: {left.type_().value} {operator} {right.type_().value}" ) 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 _push(args: List[monkey_object.MonkeyObject]) -> monkey_object.MonkeyObject: if len(args) != 2: return monkey_object.Error( f"wrong number of arguments. Got {len(args)}, want 2") if args[0].type_() != monkey_object.ObjectType.ARRAY: return monkey_object.Error( f"argument to 'push' must be ARRAY. Got {args[0].type_().value}") array = cast(monkey_object.Array, args[0]) # Monkey arrays are immutable so we must clone the underlying Python type new_elements = array.elements.copy() new_elements.append(args[1]) return monkey_object.Array(new_elements)
def _first(args: List[monkey_object.MonkeyObject]) -> monkey_object.MonkeyObject: from evaluator import Evaluator if len(args) != 1: return monkey_object.Error( f"wrong number of arguments. Got {len(args)}, want 1") if args[0].type_() != monkey_object.ObjectType.ARRAY: return monkey_object.Error( f"argument to 'first' must be ARRAY. Got {args[0].type_().value}") array = cast(monkey_object.Array, args[0]) if len(array.elements) > 0: return array.elements[0] return Evaluator.null
def _rest(args: List[monkey_object.MonkeyObject]) -> monkey_object.MonkeyObject: from evaluator import Evaluator if len(args) != 1: return monkey_object.Error( f"wrong number of arguments. Got {len(args)}, want 1") if args[0].type_() != monkey_object.ObjectType.ARRAY: return monkey_object.Error( f"argument to 'rest' must be ARRAY. Got {args[0].type_().value}") array = cast(monkey_object.Array, args[0]) length = len(array.elements) if length > 0: new_elements = array.elements[1:].copy() return monkey_object.Array(new_elements) return Evaluator.null
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 _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 _eval_identifier(self, node: ast.Identifier, env: environment.Environment) -> \ monkey_object.MonkeyObject: value = env.get(node.value) if value is not None: return value if node.value in builtin.builtins: return builtin.builtins[node.value] return monkey_object.Error(f"identifier not found: {node.value}")
def _eval_hash_index_expression(self, expr: monkey_object.MonkeyObject, index: monkey_object.MonkeyObject) -> \ monkey_object.MonkeyObject: if not isinstance(index, monkey_object.Hashable): return monkey_object.Error( f"unusable as hash key: {index.type_().value}") if not index.hash_key() in expr.pairs: return Evaluator.null return expr.pairs[index.hash_key()].value
def _eval_prefix_expression(self, operator: str, right: monkey_object.MonkeyObject) -> \ monkey_object.MonkeyObject: if operator == "!": return self._eval_bang_operator_expression(right) if operator == "-": return self._eval_minus_prefix_operator_expression(right) return monkey_object.Error( f"unknown operator: {operator}{right.type_().value}")
def _eval_index_expression(self, left: monkey_object.MonkeyObject, index: monkey_object.MonkeyObject) -> \ monkey_object.MonkeyObject: if left.type_() == monkey_object.ObjectType.ARRAY and \ index.type_() == monkey_object.ObjectType.INTEGER: return self._eval_array_index_expression(left, index) if left.type_() == monkey_object.ObjectType.HASH: return self._eval_hash_index_expression(left, index) return monkey_object.Error( f"index operator not supported: {left.type_().value}")
def _apply_function( self, function: monkey_object.MonkeyObject, args: List[monkey_object.MonkeyObject] ) -> monkey_object.MonkeyObject: if isinstance(function, monkey_object.Function): extended_env = self._extend_function_environment(function, args) evaluated = self.eval(function.body, extended_env) return self._unwrap_return_value(evaluated) if isinstance(function, monkey_object.Builtin): return function.function(args) return monkey_object.Error(f"not a function: {function.type_().value}")
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 _eval_hash_literal( self, node: ast.HashLiteral, env: environment.Environment) -> monkey_object.MonkeyObject: pairs: Dict[monkey_object.HashKey, monkey_object.HashPair] = {} for key_node, value_node in node.pairs.items(): key = self.eval(key_node, env) if self._is_error(key): return key if not isinstance(key, monkey_object.Hashable): return monkey_object.Error( f"unusable as hash key: {key.type_().value}") value = self.eval(value_node, env) if self._is_error(value): return value hashed = key.hash_key() pairs[hashed] = monkey_object.HashPair(key, value) return monkey_object.Hash(pairs)
def new_error(fmt: str, *args, **kwargs) -> mobject.Error: return mobject.Error(fmt.format(*args, **kwargs))