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 test_string_hash_key(): hello1 = object.String('Hello World') hello2 = object.String('Hello World') diff1 = object.String('My name is johnny') diff2 = object.String('My name is johnny') assert hello1.hash_key() == hello2.hash_key(), \ 'strings with same content have different hash keys' assert diff1.hash_key() == diff2.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'
def test_string_hash_key(self): hello1 = object.String(Value='Hello World') hello2 = object.String(Value='Hello World') diff1 = object.String(Value='My name is johnny') diff2 = object.String(Value='My name is johnny') if object.GetHashKey(hello1) != object.GetHashKey(hello2): self.fail('strings with same content have different hash keys') if object.GetHashKey(diff1) != object.GetHashKey(diff2): self.fail('strings with same content have different hash keys') if object.GetHashKey(hello1) == object.GetHashKey(diff1): self.fail('strings with different content have same hash keys')
def eval_infix_expression(operator: str, lhs: monkey_obj.Object, rhs: monkey_obj.Object) -> monkey_obj.Object: if isinstance(lhs, monkey_obj.Integer) and isinstance( rhs, monkey_obj.Integer): return eval_integer_infix_expression(operator, lhs, rhs) elif type(lhs) != type(rhs): return monkey_obj.Error( f"type mismatch: {lhs.object_type()} {operator} {rhs.object_type()}" ) elif isinstance(lhs, monkey_obj.String) and isinstance( rhs, monkey_obj.String): if operator != "+": return monkey_obj.Error( f"unknown operator: {lhs.object_type()} {operator} {rhs.object_type()}" ) return monkey_obj.String(value=lhs.value + rhs.value) elif operator == "==": return native_bool_to_boolean_object(lhs == rhs) elif operator == "!=": return native_bool_to_boolean_object(lhs != rhs) return monkey_obj.Error( f"unknown operator: {lhs.object_type()} {operator} {rhs.object_type()}" )
def evalStringInfixExpression(operator: str, left: object.Object, right: object.Object) -> object.Object: if operator != '+': return newError('unknown operator: %s %s %s', (left.Type.TypeName, operator, right.Type.TypeName)) leftVal = left.Value rightVal = right.Value return object.String(Value=leftVal + rightVal)
def eval_string_infix_operation(operator: str, left: object.Object, right: object.Object) -> object.Object: if operator != '+': return new_error('unknown operator: {} {} {}'.format( left.type(), operator, right.type())) left_val = left.value right_val = right.value return object.String(left_val + right_val)
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_string_infix_expression(operator, left, right): if operator is not "+": return object.Error("unknown operator: {} {} {}".format(left.object_type, operator, right.object_type)) # use python string concat underneath return object.String(value=left.value + right.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