Example #1
0
def eval_numeric_infix_exp(
    operator: str,
    left: typing.Union[objects.Integer, objects.Float],
    right: typing.Union[objects.Integer, objects.Float],
) -> objects.Object:
    left_val = left.value
    right_val = right.value

    if operator == "+":
        return create_numeric_object(left_val + right_val)
    elif operator == "-":
        return create_numeric_object(left_val - right_val)
    elif operator == "*":
        return create_numeric_object(left_val * right_val)
    elif operator == "/":
        return create_numeric_object(left_val / right_val)
    elif operator == "%":
        return create_numeric_object(left_val % right_val)
    elif operator == "<":
        return to_boolean_singleton(left_val < right_val)
    elif operator == ">":
        return to_boolean_singleton(left_val > right_val)
    elif operator == "==":
        return to_boolean_singleton(left_val == right_val)
    elif operator == "!=":
        return to_boolean_singleton(left_val != right_val)
    else:
        return objects.Error(
            f"unknown operator: {left.tp().value} {operator} {right.tp().value}"
        )
Example #2
0
def eval_prefix_exp(operator: str, right: objects.Object) -> objects.Object:
    if operator == "!":
        return eval_bang_operator_exp(right)
    elif operator == "-":
        return eval_minus_preoperator_exp(right)
    else:
        return objects.Error(f"unknown operator: {operator}{right.tp().value}")
Example #3
0
def create_numeric_object(value: typing.Union[int, float]) -> objects.Object:
    if isinstance(value, float):
        return objects.Float(value)
    elif isinstance(value, int):
        return objects.Integer(value)
    else:
        return objects.Error(
            f"Cannot create a numeric object out of value {value}.")
Example #4
0
def eval_index_expression(left: objects.Object,
                          index: objects.Object) -> objects.Object:
    if isinstance(left, objects.Array) and isinstance(index, objects.Integer):
        return eval_array_index_exp(left, index)
    elif isinstance(left, objects.Dictionary):
        return eval_dict_index_exp(left, index)
    else:
        return objects.Error(
            f"index operator not supported: {left.tp().value}")
Example #5
0
def eval_minus_preoperator_exp(right: objects.Object) -> objects.Object:
    if not isinstance(right, objects.Integer) and not isinstance(
            right, objects.Float):
        return objects.Error(f"unknown operator: -{right.tp().value}")

    if isinstance(right, objects.Integer):
        return objects.Integer(-right.value)
    else:
        return objects.Float(-right.value)
Example #6
0
def eval_bool_infix_exp(operator: str, left: objects.Boolean,
                        right: objects.Boolean) -> objects.Object:
    if operator == "==":
        return to_boolean_singleton(left is right)
    elif operator == "!=":
        return to_boolean_singleton(left is not right)
    else:
        return objects.Error(
            f"unknown operator: {left.tp().value} {operator} {right.tp().value}"
        )
Example #7
0
def eval_dict_index_exp(dictionary: objects.Dictionary,
                        index: objects.Object) -> objects.Object:

    if not isinstance(index, objects.Hashable):
        return objects.Error(f"unusable as dictionary key: {index.tp().value}")

    pair = dictionary.pairs.get(index.hashkey(), None)
    if pair is None:
        return objects.Null()

    return pair.value
Example #8
0
def eval_identifier(node: ast.Identifier,
                    env: objects.Environment) -> objects.Object:
    val = env.get_var(node.value)
    if val is not None:
        return val

    val = objects.BUILTINS.get(node.value, None)
    if val is not None:
        return val

    return objects.Error(f"identifier not found: {node.value}")
Example #9
0
def eval_string_infix_exp(operator: str, left: objects.String,
                          right: objects.String) -> objects.Object:
    if operator == "+":
        return objects.String(left.value + right.value)
    elif operator == "==":
        return to_boolean_singleton(left.value == right.value)
    elif operator == "!=":
        return to_boolean_singleton(left.value != right.value)
    else:
        return objects.Error(
            f"unknown operator: {left.tp().value} {operator} {right.tp().value}"
        )
Example #10
0
def extend_function_env(
    fn: objects.Function, args: typing.List[objects.Object]
) -> typing.Union[objects.Environment, objects.Error]:
    env = objects.Environment(outer=fn.env)

    for count, param in enumerate(fn.parameters):
        try:
            env.set_var(param.value, args[count])
        except IndexError:
            return objects.Error(
                f"{param.value} argument missing from function call.")

    return env
Example #11
0
def eval_infix_exp(operator: str, left: objects.Object,
                   right: objects.Object) -> objects.Object:
    if (isinstance(left, objects.Integer)
            or isinstance(left, objects.Float)) and (isinstance(
                right, objects.Integer) or isinstance(right, objects.Float)):
        return eval_numeric_infix_exp(operator, left, right)
    elif isinstance(left, objects.Boolean) and isinstance(
            right, objects.Boolean):
        return eval_bool_infix_exp(operator, left, right)
    elif isinstance(left, objects.String) and isinstance(
            right, objects.String):
        return eval_string_infix_exp(operator, left, right)
    elif operator == "==":
        return to_boolean_singleton(left is right)
    elif operator == "!=":
        return to_boolean_singleton(left is not right)
    elif left.tp() != right.tp():
        return objects.Error(
            f"type mismatch: {left.tp().value} {operator} {right.tp().value}")
    else:
        return objects.Error(
            f"unknown operator: {left.tp().value} {operator} {right.tp().value}"
        )
Example #12
0
def apply_function(
        fn: objects.Object,
        args: typing.List[objects.Object]) -> typing.Optional[objects.Object]:
    if isinstance(fn, objects.Function):
        extended_env = extend_function_env(fn, args)
        if isinstance(extended_env, objects.Error):
            return extended_env
        evaluated = evaluate(fn.body, extended_env)
        if evaluated is None:
            return evaluated

        return unwrap_return_value(evaluated)
    elif isinstance(fn, objects.Builtin):
        return fn.function(args)
    else:
        return objects.Error(f"not a function: {fn.tp()}")
Example #13
0
def eval_dictionary_literal(
        node: ast.DictionaryLiteral,
        env: objects.Environment) -> typing.Optional[objects.Object]:
    pairs = {}

    for keynode, valnode in node.pairs.items():
        key = evaluate(keynode, env)
        if is_error(key) or key is None:
            return key

        if not isinstance(key, objects.Hashable):
            return objects.Error(f"unusable as hash key: {key.tp().value}")

        value = evaluate(valnode, env)
        if is_error(value) or value is None:
            return value

        hashed = key.hashkey()
        pairs[hashed] = objects.HashPair(key, value)

    return objects.Dictionary(pairs)
Example #14
0
def evaluate(node: ast.Node,
             env: objects.Environment) -> typing.Optional[objects.Object]:
    """Evaluate AST recursively."""
    if isinstance(node, ast.Program):
        return eval_program(node.statements, env)
    elif isinstance(node, ast.ExpressionStatement):
        return evaluate(node.expression, env)
    elif isinstance(node, ast.IntegerLiteral):
        return objects.Integer(node.value)
    elif isinstance(node, ast.FloatLiteral):
        return objects.Float(node.value)
    elif isinstance(node, ast.BooleanLiteral):
        if node.value:
            return objects.Boolean(True)

        return objects.Boolean(False)
    elif isinstance(node, ast.StringLiteral):
        value = str(node.value)
        return objects.String(value)
    elif isinstance(node, ast.PrefixExpression):
        right = evaluate(node.right, env)
        if is_error(right) or right is None:
            return right

        return eval_prefix_exp(node.operator, right)
    elif isinstance(node, ast.InfixExpression):
        left = evaluate(node.left, env)
        if is_error(left) or left is None:
            return left

        right = evaluate(node.right, env)
        if is_error(right) or right is None:
            return right

        return eval_infix_exp(node.operator, left, right)
    elif isinstance(node, ast.BlockStatement):
        return eval_block_statements(node, env)
    elif isinstance(node, ast.IfExpression):
        return eval_if_exp(node, env)
    elif isinstance(node, ast.ReturnStatement):
        val = evaluate(node.return_value, env)
        if is_error(val) or val is None:
            return val

        return objects.ReturnValue(val)
    elif isinstance(node, ast.LetStatement):
        exp = node.value
        if not isinstance(exp, ast.Expression):
            return None

        val = evaluate(exp, env)
        if is_error(val) or val is None:
            return val

        env.set_var(node.name.value, val)
    elif isinstance(node, ast.ReassignStatement):
        exp = node.value
        if not isinstance(exp, ast.Expression):
            return None

        val = evaluate(exp, env)
        if is_error(val) or val is None:
            return val

        success = env.reassign_var(node.name.value, val)
        if success is None:
            return objects.Error(
                f"cannot modify const identifier: {node.name.value}")
        elif not success:
            return objects.Error(f"identifier not found: {node.name.value}")
    elif isinstance(node, ast.WhileStatement):
        return eval_while_stmt(node, env)
    elif isinstance(node, ast.ConstStatement):
        exp = node.value
        if not isinstance(exp, ast.Expression):
            return None

        val = evaluate(exp, env)
        if is_error(val) or val is None:
            return val

        env.set_var(node.name.value, val, const=True)
    elif isinstance(node, ast.Identifier):
        return eval_identifier(node, env)
    elif isinstance(node, ast.FunctionLiteral):
        params = node.parameters
        body = node.body
        return objects.Function(params, body, env)
    elif isinstance(node, ast.CallExpression):
        function = evaluate(node.function, env)
        if is_error(function) or function is None:
            return function

        args = eval_exps(node.arguments, env)
        if args is None:
            return args

        if len(args) == 1 and isinstance(args[0], objects.Error):
            return args[0]

        return apply_function(function, args)
    elif isinstance(node, ast.ArrayLiteral):
        elements = eval_exps(node.elements, env)
        if elements is None:
            return elements

        if len(elements) == 1 and isinstance(elements[0], objects.Error):
            return elements[0]

        return objects.Array(elements)
    elif isinstance(node, ast.IndexExpression):
        left = evaluate(node.left, env)
        if is_error(left) or left is None:
            return left

        index = evaluate(node.index, env)
        if is_error(index) or index is None:
            return index

        return eval_index_expression(left, index)
    elif isinstance(node, ast.DictionaryLiteral):
        return eval_dictionary_literal(node, env)

    return None