def eval_integer_infix_expression( operator: str, left: object.Object, right: object.Object) -> Union[object.Object, None]: left_val = left.value right_val = right.value if operator == '+': return object.Integer(left_val + right_val) elif operator == '-': return object.Integer(left_val - right_val) elif operator == '*': return object.Integer(left_val * right_val) elif operator == '/': return object.Integer(left_val / right_val) elif operator == '<': return native_bool_to_boolean_object(left_val < right_val) elif operator == '>': return native_bool_to_boolean_object(left_val > right_val) elif operator == '==': return native_bool_to_boolean_object(left_val == right_val) elif operator == '!=': return native_bool_to_boolean_object(left_val != right_val) else: return new_error('unknown operator: {} {} {}', left.type(), operator, right.type())
def _len(*args) -> object.Object: if len(args) != 1: return evaluator.new_error('wrong number of arguments. got={}, want=1'.format(len(args))) if type(args[0]) == object.Array: return object.Integer(len(args[0].elements)) elif type(args[0]) == object.String: return object.Integer(len(args[0].value)) else: return evaluator.new_error('argument to `len` not supported, got {}'.format(args[0].type()))
def _builtin_len(args: List[monkey_obj.Object]) -> monkey_obj.Object: if len(args) != 1: return monkey_obj.Error( f"wrong number of arguments. got={len(args)}, want=1") item = next(iter(args)) if isinstance(item, monkey_obj.String): return monkey_obj.Integer(value=len(item.value)) elif isinstance(item, monkey_obj.Array): return monkey_obj.Integer(value=len(item.elements)) else: return monkey_obj.Error( f"argument to `len` not supported, got {item.object_type()}")
def builtin_len(args: List[object.Object]) -> object.Object: if len(args) != 1: return newError('wrong number of arguments. got=%s, want=1', (len(args), )) arg = args[0] if type(arg) == object.Array: arg = cast(object.Array, arg) return object.Integer(Value=int(len(arg.Elements))) elif type(arg) == object.String: return object.Integer(Value=int(len(arg.Value))) else: return newError('argument to \'len\' not supported, got %s', (args[0].Type.TypeName, ))
def test_integer_hash_key(): one1 = object.Integer(1) one2 = object.Integer(1) two1 = object.Integer(2) two2 = object.Integer(2) assert one1.hash_key() == one2.hash_key(), \ 'integers with same content have twoerent hash keys' assert two1.hash_key() == two2.hash_key(), \ 'integers with same content have twoerent hash keys' assert one1.hash_key() != two1.hash_key(), \ 'integers with tworent content have same hash keys'
def eval_minus_prefix_expression(right: object.Object) -> object.Object: if right.type() != object.INTEGER_OBJ: return new_error('unknown operator: -{}', right.type()) value = right.value return object.Integer(-value)
def test_hash_literals(self): input = '''let two = "two"; { "one": 10 - 9, two: 1 + 1, "thr" + "ee": 6 / 2, 4: 4, true: 5, false: 6 }''' evaluated = testEval(input) result = evaluated if not result: self.fail('Eval didn\'t return Hash. got=%s (%s)' % (evaluated, evaluated)) expected = [ (object.GetHashKey(object.String(Value='one')), 1), (object.GetHashKey(object.String(Value='two')), 2), (object.GetHashKey(object.String(Value='three')), 3), (object.GetHashKey(object.Integer(Value=4)), 4), (object.GetHashKey(evaluator.TRUE), 5), (object.GetHashKey(evaluator.FALSE), 6), ] if len(result.Pairs) != len(expected): self.fail('Hash has wrong num of pairs. got=%s' % len(result.Pairs)) for expectedKey, expectedValue in expected: # TODO: xxx pair = [x for x in result.Pairs if x[0].Value == expectedKey.Value][0][1] if not pair: self.fail('no pair for given key in Pairs') testIntegerObject(self, pair.Value, expectedValue)
def test_hash_literals(): input = ''' let two = "two"; { "one": 10 - 9, two: 1 + 1, "thr" + "ee": 6 / 2, 4: 4, true: 5, false: 6 } ''' evaluated = _test_eval(input) assert issubclass(evaluated.__class__, object.Hash), \ "Eval didn't return Hash. got={} ({})".format(evaluated.__class__.__name__, evaluated) expected = { object.String('one').hash_key(): 1, object.String('two').hash_key(): 2, object.String('three').hash_key(): 3, object.Integer(4).hash_key(): 4, evaluator.TRUE.hash_key(): 5, evaluator.FALSE.hash_key(): 6, } assert len(evaluated.pairs) == len(expected), \ 'Hash has wrong num of pairs. got={}'.format(len(evaluated.pairs)) for expected_key, expected_value in expected.items(): assert expected_key in evaluated.pairs, \ 'no pair for given key in pairs: ' + str(expected_key) pair = evaluated.pairs[expected_key] _test_integer_object(pair.value, expected_value)
def len_builtin(*args): if len(args) != 1: return object.Error( 'wrong number of arguments, got= {}, want = 1'.format(len(args))) if type(args[0]) is object.String: return object.Integer(value=len(args[0].value)) else: return object.Error("argument to 'len' not supported, got {}".format( args[0].object_type))
def evalMinusPrefixOperatorExpression( right: Optional[object.Object]) -> object.Object: if not right: return NULL if right.Type.TypeName != object.INTEGER_OBJ: return newError('unknown operator: -%s', (right.Type.TypeName, )) value = right.Value return object.Integer(Value=-value)
def eval_integer_infix_expression(operator, left, right): left_val = left.value right_val = right.value if operator == '+': return object.Integer(value=left_val + right_val) elif operator == '-': return object.Integer(value=left_val - right_val) elif operator == '*': return object.Integer(value=left_val * right_val) elif operator == '/': return object.Integer(value=left_val / right_val) elif operator == '<': return native_bool_to_boolean_object(left_val < right_val) elif operator == '>': return native_bool_to_boolean_object(left_val > right_val) elif operator == '==': return native_bool_to_boolean_object(left_val == right_val) elif operator == '!=': return native_bool_to_boolean_object(left_val != right_val) else: # an error return object.NULL
def evalIntegerInfixExpression(operator: str, left: object.Object, right: object.Object) -> object.Object: leftVal = left.Value rightVal = right.Value if operator == '+': return object.Integer(Value=leftVal + rightVal) elif operator == '-': return object.Integer(Value=leftVal - rightVal) elif operator == '*': return object.Integer(Value=leftVal * rightVal) elif operator == '/': return object.Integer(Value=leftVal / rightVal) elif operator == '<': return nativeBoolToBooleanObject(leftVal < rightVal) elif operator == '>': return nativeBoolToBooleanObject(leftVal > rightVal) elif operator == '==': return nativeBoolToBooleanObject(leftVal == rightVal) elif operator == '!=': return nativeBoolToBooleanObject(leftVal != rightVal) else: return newError('unknown operator: %s %s %s', (left.Type.TypeName, operator, right.Type.TypeName))
def eval_integer_infix_expression( operator: str, lhs: monkey_obj.Integer, rhs: monkey_obj.Integer) -> monkey_obj.Object: if operator == "+": return monkey_obj.Integer(value=lhs.value + rhs.value) elif operator == "-": return monkey_obj.Integer(value=lhs.value - rhs.value) elif operator == "*": return monkey_obj.Integer(value=lhs.value * rhs.value) elif operator == "/": return monkey_obj.Integer(value=lhs.value // rhs.value) elif operator == "<": return native_bool_to_boolean_object(lhs.value < rhs.value) elif operator == ">": return native_bool_to_boolean_object(lhs.value > rhs.value) elif operator == "==": return native_bool_to_boolean_object(lhs.value == rhs.value) elif operator == "!=": return native_bool_to_boolean_object(lhs.value != rhs.value) else: return monkey_obj.Error( f"unknown operator: {lhs.object_type()} {operator} {rhs.object_type()}" )
def eval(node: ast.Node, env: object.Environment): if type(node) is ast.Program: return eval_program(node.statements, env) elif type(node) is ast.ExpressionStatement: return eval(node.expression, env) elif type(node) is ast.LetStatement: val = eval(node.value, env) if is_error(val): return val env.set(node.name.value, val) elif type(node) is ast.Identifier: return eval_identifier(node, env) elif type(node) is ast.StringLiteral: return object.String(value=node.value) elif type(node) is ast.FunctionLiteral: parameters = node.parameters body = node.body return object.Function(parameters=parameters, body=body, env=env) elif type(node) is ast.CallExpression: fun = eval(node.function, env) if is_error(fun): return fun args = eval_expressions(node.arguments, env) if len(args) == 1 and is_error(args[0]): return args[0] return apply_function(fun, args) elif type(node) is ast.PrefixExpression: right = eval(node.right, env) if is_error(right): return right return eval_prefix_expression(node.operator, right) elif type(node) is 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 type(node) is ast.IntegerLiteral: return object.Integer(value=node.value) elif type(node) is ast.BlockStatement: return eval_block_statements(node.statements, env) elif type(node) is ast.IfExpression: return eval_if_expression(node, env) elif type(node) is ast.ReturnStatement: val = eval(node.return_value, env) return object.ReturnValue(value=val) elif type(node) is ast.Boolean: return object.TRUE if node.value is True else object.FALSE
def Eval(node: Any, env: object.Environment) -> Optional[object.Object]: if type(node) == ast.Program: return evalProgram(node, env) elif type(node) == ast.ExpressionStatement: return Eval(node.ExpressionValue, env) elif type(node) == ast.IntegerLiteral: return object.Integer(Value=node.Value) elif type(node) == ast.Boolean: return nativeBoolToBooleanObject(node.Value) elif type(node) == ast.PrefixExpression: right = Eval(node.Right, env) if right: if isError(right): return right return evalPrefixExpression(node.Operator, right) else: return None elif type(node) == ast.InfixExpression: left = Eval(node.Left, env) if not left: return None if isError(left): return left right = Eval(node.Right, env) if not right: return None if isError(right): return right evaluated = evalInfixExpression(node.Operator, left, right) return evaluated elif type(node) == ast.BlockStatement: return evalBlockStatement(node, env) elif type(node) == ast.IfExpression: return evalIfExpression(node, env) elif type(node) == ast.ReturnStatement: val = Eval(node.ReturnValue, env) if val: if isError(val): return val return object.ReturnValue(Value=val) else: return None elif type(node) == ast.LetStatement: val = Eval(node.Value, env) if val: if isError(val): return val env.Set(node.Name.Value, val) else: return None elif type(node) == ast.Identifier: return evalIdentifier(node, env) elif type(node) == ast.FunctionLiteral: params = node.Parameters body = node.Body return object.Function(Parameters=params, Env=env, Body=body) elif type(node) == ast.CallExpression: if node.Function.TokenLiteral() == 'quote': return quote(node.Arguments[0], env) function = Eval(node.Function, env) if function: if isError(function): return function args = evalExpressions(node.Arguments, env) if len(args) == 1 and isError(args[0]): return args[0] if not function: return None return applyFunction(function, args) elif type(node) == ast.StringLiteral: return object.String(Value=node.Value) elif type(node) == ast.ArrayLiteral: elements = evalExpressions(node.Elements, env) if len(elements) == 1 and isError(elements[0]): return elements[0] return object.Array(Elements=elements) elif type(node) == ast.IndexExpression: left = Eval(node.Left, env) if not left: return None if isError(left): return left index = Eval(node.Index, env) if not index: return None if isError(index): return index return evalIndexExpression(left, index) elif type(node) == ast.HashLiteral: return evalHashLiteral(node, env) return None
def Eval(node: ast.Node, env) -> monkey_obj.Object: if isinstance(node, ast.Program): return eval_program(node.statements, env) elif isinstance(node, ast.HashLiteral): return eval_hash_literal(node, env) elif isinstance(node, ast.ArrayLiteral): elements = [Eval(e, env) for e in node.elements] if len(elements) == 1 and isinstance(elements[0], monkey_obj.Error): return elements[0] return monkey_obj.Array(elements=elements) elif isinstance(node, ast.IndexExpression): left = Eval(node.left, env) if left is not None and isinstance(left, monkey_obj.Error): return left index = Eval(node.index, env) if index is not None and isinstance(index, monkey_obj.Error): return index return eval_index_expression(left, index) elif isinstance(node, ast.StringLiteral): return monkey_obj.String(value=node.value) elif isinstance(node, ast.CallExpression): function = Eval(node.function, env) if function is not None and isinstance(function, monkey_obj.Error): return function args = [Eval(e, env) for e in node.arguments] for arg in args: if arg is not None and isinstance(function, monkey_obj.Error): return arg return apply_function(function, args) elif isinstance(node, ast.FunctionLiteral): params = node.params body = node.body return monkey_obj.Function(params, env, body) elif isinstance(node, ast.BlockStatement): return eval_block_statements(node.statements, env) elif isinstance(node, ast.IfExpression): return eval_if_expression(node, env) elif isinstance(node, ast.Identifier): return eval_identifier(node, env) elif isinstance(node, ast.LetStatement): val = Eval(node.value, env) if val is not None and isinstance(val, monkey_obj.Error): return val env[node.name.value] = val return monkey_obj.NULL elif isinstance(node, ast.ReturnStatement): val = Eval(node.value, env) if val is not None and isinstance(val, monkey_obj.Error): return val return monkey_obj.ReturnValue(value=val) elif isinstance(node, ast.ExpressionStatement): return Eval(node.expression, env) elif isinstance(node, ast.Boolean): return native_bool_to_boolean_object(node.value) elif isinstance(node, ast.IntegerLiteral): return monkey_obj.Integer(value=node.value) elif isinstance(node, ast.PrefixExpression): right = Eval(node.right, env) if right is not None and isinstance(right, monkey_obj.Error): return val return eval_prefix_expression(node.operator, right) elif isinstance(node, ast.InfixExpression): left = Eval(node.left, env) if left is not None and isinstance(left, monkey_obj.Error): return left right = Eval(node.right, env) if right is not None and isinstance(right, monkey_obj.Error): return val return eval_infix_expression(node.operator, left, right) else: raise NotImplementedError(str(node), node.token_literal())
def eval_minus_prefix_operator(rhs: monkey_obj.Object) -> monkey_obj.Object: if not isinstance(rhs, monkey_obj.Integer): return monkey_obj.Error(f"unknown operator: -{rhs.object_type()}") return monkey_obj.Integer(value=-(rhs.value))
def eval(node: ast.Node, env: object.Environment) -> Union[object.Object, None]: # Statements if issubclass(node.__class__, ast.Program): return eval_program(node, env) elif issubclass(node.__class__, ast.BlockStatement): return eval_block_statement(node, env) elif issubclass(node.__class__, ast.ExpressionStatement): return eval(node.expression, env) elif issubclass(node.__class__, ast.ReturnStatement): val = eval(node.return_value, env) if is_error(val): return val return object.ReturnValue(val) elif issubclass(node.__class__, ast.LetStatement): val = eval(node.value, env) if is_error(val): return val env.set(node.name.value, val) # Expressions elif issubclass(node.__class__, ast.IntegerLiteral): return object.Integer(node.value) elif issubclass(node.__class__, ast.StringLiteral): return object.String(node.value) elif issubclass(node.__class__, ast.Boolean): return native_bool_to_boolean_object(node.value) elif issubclass(node.__class__, ast.PrefixExpression): right = eval(node.right, env) if is_error(right): return right return eval_prefix_expression(node.operator, right) elif issubclass(node.__class__, 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 issubclass(node.__class__, ast.IfExpression): return eval_if_expression(node, env) elif issubclass(node.__class__, ast.Identifier): return eval_identifier(node, env) elif issubclass(node.__class__, ast.FunctionLiteral): params = node.parameters body = node.body return object.Function(params, body, env) elif issubclass(node.__class__, 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 issubclass(node.__class__, ast.ArrayLiteral): elements = eval_expressions(node.elements, env) if len(elements) == 1 and is_error(elements[0]): return elements[0] return object.Array(elements) elif issubclass(node.__class__, 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 issubclass(node.__class__, ast.HashLiteral): return eval_hash_literal(node, env) return None
def eval_minus_prefix_operator_expression(right): if type(right) is not object.Integer: return object.Error(message="unknown operator: {}{}".format("-", right.object_type)) else: return object.Integer(value=-right.value)