Beispiel #1
0
def eval_index_expression(left: MonkeyObject,
                          index: MonkeyObject) -> MonkeyObject:
    if left.typ == mobject.ARRAY_OBJ and index.typ == mobject.INTEGER_OBJ:
        return eval_array_index_expression(left, index)
    elif left.typ == mobject.HASH_OBJ:
        if not isinstance(index, mobject.Hashable):
            return new_error("unusable as hash key: {}", index.typ)
        return eval_hash_index_expression(left, index)
    else:
        return new_error("index operator not supported: {}", left.typ)
Beispiel #2
0
def apply_function(fn: MonkeyObject, args: List[MonkeyObject]) -> MonkeyObject:
    if isinstance(fn, mobject.Function):
        if len(fn.parameters) > len(args):
            return new_error(
                "function call missing required arguments: {}",
                ', '.join([param.value
                           for param in fn.parameters[len(args):]]))
        extended_env = extend_function_env(fn, args)
        evaluated = eval(fn.body, extended_env)
        return unwrap_return_value(evaluated)
    elif isinstance(fn, mobject.Builtin):
        return fn.fn(*args)
    else:
        return new_error("not a function: {}", fn.typ)
Beispiel #3
0
def eval_infix_expression(operator: str, left: MonkeyObject,
                          right: MonkeyObject) -> MonkeyObject:
    if left.typ == mobject.INTEGER_OBJ and right.typ == mobject.INTEGER_OBJ:
        return eval_integer_infix_expression(operator, left, right)
    elif left.typ == mobject.BOOLEAN_OBJ and right.typ == mobject.BOOLEAN_OBJ:
        if operator == "==":
            return native_bool_to_boolean_object(left == right)
        elif operator == "!=":
            return native_bool_to_boolean_object(left != right)
        else:
            return new_error("unknown operator: {} {} {}", left.typ, operator,
                             right.typ)
    elif left.typ == mobject.STRING_OBJ and right.typ == mobject.STRING_OBJ:
        return eval_string_infix_expression(operator, left, right)
    else:
        return new_error("type mismatch: {} {} {}", left.typ, operator,
                         right.typ)
Beispiel #4
0
 def inner(*args):
     err = check_args_len(args, len(typs))
     if err is not None:
         return err
     for arg, typ in zip(args, typs):
         if typ is not None and arg.typ != typ:
             return new_error("wrong argument type")
     return f(*args)
Beispiel #5
0
 def inner(*args):
     err = check_args_len(args, 1)
     if err is not None:
         return err
     arg = args[0]
     if arg.typ != typ:
         return new_error("argument must be {}, got {}", typ, arg.typ)
     return f(arg)
Beispiel #6
0
def puts(*args):
    for arg in args:
        if arg.typ not in (mobject.STRING_OBJ, mobject.INTEGER_OBJ,
                           mobject.BOOLEAN_OBJ):
            return new_error("wrong argument type, 'puts' does not accept {}",
                             arg.typ)
    for arg in args:
        print(arg.value)
Beispiel #7
0
def eval_program(program: ast.Program,
                 env: mobject.Environment) -> MonkeyObject:
    result: MonkeyObject

    for statement in program.statements:
        result = eval(statement, env)
        if result is None:
            continue
        elif result.typ == mobject.RETURN_VALUE_OBJ:
            return result.value
        elif result.typ == mobject.ERROR_OBJ:
            return result
        elif is_break(result):
            return new_error("break cannot be used outside of a loop")
        elif is_continue(result):
            return new_error("continue cannot be used outside of a loop")

    return result
Beispiel #8
0
def eval_prefix_expression(operator: str, right: MonkeyObject) -> MonkeyObject:
    if operator == "!":
        return eval_bang_operator_expression(right)
    elif operator == "-":
        return eval_minus_prefix_operator_expression(right)
    else:
        return new_error("unknown operator: {operator}{typ}",
                         operator=operator,
                         typ=right.typ)
Beispiel #9
0
def eval_identifier(node: ast.Identifier,
                    env: mobject.Environment) -> MonkeyObject:
    val = env.get(node.value)
    if val is not None:
        return val
    builtin = builtins.get(node.value)
    if builtin is not None:
        return builtin
    return new_error("identifier not found: {}", node.value)
Beispiel #10
0
def last(*args) -> MonkeyObject:
    err = check_args_len(args, 1)
    if err is not None:
        return err
    arg = args[0]
    if arg.typ != mobject.ARRAY_OBJ:
        return new_error("argument to 'last' must be ARRAY, got {}", arg.typ)
    if len(arg.elements) > 0:
        return arg.elements[-1]
    return NULL
Beispiel #11
0
def rest(*args) -> mobject.Array:
    err = check_args_len(args, 1)
    if err is not None:
        return err
    arg = args[0]
    if arg.typ != mobject.ARRAY_OBJ:
        return new_error("argument to 'last' must be ARRAY, got {}", arg.typ)
    if len(arg.elements) > 0:
        return mobject.Array(elements=arg.elements[1:])
    return NULL
Beispiel #12
0
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)
Beispiel #13
0
def eval_for_expression(exp: ast.ForExpression,
                        env: mobject.Environment) -> MonkeyObject:
    iterator: mobject.Array = eval(exp.iterator, env)
    if not iterator.typ == mobject.ARRAY_OBJ:
        return new_error("iterator must be ARRAY. found {}", iterator.typ)
    evaluated = NULL
    for value in iterator.elements:
        extended_env = extend_for_body_env(env, exp.element, value)
        evaluated = eval_loop_block_statement(exp.body, extended_env)
        if is_error(evaluated) or is_return(evaluated):
            return evaluated
        if is_break(evaluated):
            return NULL
        if is_continue(evaluated):
            evaluated = NULL
    return evaluated
Beispiel #14
0
def eval_hash_literal(node: ast.HashLiteral,
                      env: mobject.Environment) -> MonkeyObject:
    pairs: Dict[mobject.HashKey, mobject.HashPair] = {}

    for key_node, value_node in node.pairs:
        key = eval(key_node, env)
        if is_error(key):
            return key
        if not isinstance(key, mobject.Hashable):
            return new_error("unusable as hash key: {}", key.typ)
        value = eval(value_node, env)
        if is_error(value):
            return value

        hashed = key.hash_key
        pairs[hashed] = mobject.HashPair(key=key, value=value)

    return mobject.Hash(pairs=pairs)
Beispiel #15
0
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)
Beispiel #16
0
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
Beispiel #17
0
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)
Beispiel #18
0
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)
Beispiel #19
0
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))
Beispiel #20
0
def check_args_len(args, n) -> mobject.Error:
    if len(args) != n:
        return new_error("wrong number of arguments. got {}, want {}",
                         len(args), n)
    return None