Пример #1
0
def EVAL(ast, env):
    """
    Evaluate set of mal instructions.
    """
    while True:
        if not is_list(ast):
            return eval_ast(ast, env)
        elif is_empty(ast):
            return ast

        else:
            ast = macroexpand(ast, env)
            if not is_list(ast):
                return eval_ast(ast, env)
            elif not ast:
                return ast

        if first(ast) == make_symbol('def!'):
            try:
                operands = rest(ast)
                symbol = first(operands)
                value = EVAL(first(rest(operands)), env)
            except ValueError:
                raise RuntimeError('def! syntax is (def! /symbol/ /value/)')
            if is_nil(value):
                return env.get(symbol)
            env.set(symbol, value)
            return value

        elif first(ast) == make_symbol('let*'):
            let_error = RuntimeError('let* syntax is (let* /list_of definitions/ /list_of_instructions/)')  # noqa
            new_env = Env(env)
            try:
                operands = rest(ast)
                definitions, instructions = operands
            except Exception:
                raise let_error
            if len(definitions) % 2 != 0:
                raise let_error
            symbol_value_pairs = list(zip(
                definitions[0::2],
                definitions[1::2],
            ))
            for symb, value in symbol_value_pairs:
                new_env.set(symb, EVAL(value, new_env))
            ast = instructions
            env = new_env
            continue

        elif first(ast) == make_symbol('if'):
            elements = rest(ast)
            condition = first(elements)
            true_branch = first(rest(elements))
            false_branch = first(rest(rest(elements)))
            condition = EVAL(condition, env)
            # empty lists, strings and 0 are 'truthy', only false and nil are 'falsy'
            if is_nil(condition) or is_bool(condition) and condition == FALSE:
                ast = false_branch
            else:
                ast = true_branch
            continue

        elif first(ast) == make_symbol('fn*'):
            try:
                op, binds, body = ast
            except ValueError:
                raise RuntimeError('fn* syntax us (fn* /arguments/ /function_body/)')  # noqa

            def closure(*arguments):
                try:
                    new_env = Env(outer=env, binds=binds, exprs=arguments)
                except ValueError:
                    raise RuntimeError(
                        'Error: function is called with wrong number of parameters'
                        f'expected: {len(binds)}, actual: {len(arguments)}'
                    )
                return EVAL(body, new_env)

            return make_function(body, binds, env, closure)

        elif first(ast) == make_symbol('do'):
            op, *exprs = ast
            for expr in exprs[:-1]:
                EVAL(expr, env)
            ast = exprs[-1]
            continue

        # quoting element
        elif ast[0] == make_symbol('quote'):
            return ast[1]

        elif ast[0] == make_symbol('quasiquote'):
            ast = quasiquote(ast[1])
            continue

        elif ast[0] == make_symbol('defmacro!'):
            try:
                op, symbol, operation_ast = ast
                fn_sym, binds, body = operation_ast
                if fn_sym != make_symbol('fn*'):
                    raise ValueError
            except ValueError:
                raise RuntimeError('defmacro! syntax is (def! /symbol/ /function_body/)')
            fn = make_function(body, binds, env, None, True)  # fn.fn is set to None. Check in step 9 is it ok
            env.set(symbol, fn)
            return NIL

        elif ast[0] == make_symbol('macroexpand'):
            return macroexpand(ast[1], env)

        elif ast[0] == make_symbol('try*'):
            try:
                op, try_branch, catch = ast
            except ValueError:
                op, try_branch = ast
                return EVAL(try_branch, env)
            try:
                return EVAL(try_branch, env)
            except Exception as exc:
                catch_symbol, exception_symbol, catch_branch = catch
                return EVAL(catch_branch, Env(env, [exception_symbol], [exc]))

        func, *args = eval_ast(ast, env)
        if not is_mal_function(func):
            # core function
            return func(*args)
        ast = func.ast
        env = Env(func.env, func.params, args)
Пример #2
0
def apply_(fn, *args):
    if is_mal_function(fn):
        fn = fn.fn
    args = _flatten(args)
    return fn(*args)
Пример #3
0
def EVAL(ast, env):
    """
    Evaluate set of mal instructions.
    """
    while True:
        if not is_list(ast):
            return eval_ast(ast, env)
        elif is_empty(ast):
            return ast

        elif first(ast) == make_symbol('def!'):
            try:
                operands = rest(ast)
                symbol = first(operands)
                value = EVAL(first(rest(operands)), env)
            except ValueError:
                raise RuntimeError('def! syntax is (def! /symbol/ /value/)')
            env.set(symbol, value)
            return value

        elif first(ast) == make_symbol('let*'):
            let_error = RuntimeError(
                'let* syntax is (let* /list_of definitions/ /list_of_instructions/)'
            )  # noqa
            new_env = Env(env)
            try:
                operands = rest(ast)
                definitions, instructions = operands
            except Exception:
                raise let_error
            if len(definitions) % 2 != 0:
                raise let_error
            symbol_value_pairs = list(
                zip(
                    definitions[0::2],
                    definitions[1::2],
                ))
            for symb, value in symbol_value_pairs:
                new_env.set(symb, EVAL(value, new_env))
            ast = instructions
            env = new_env
            continue

        elif first(ast) == make_symbol('if'):
            elements = rest(ast)
            condition = first(elements)
            true_branch = first(rest(elements))
            false_branch = first(rest(rest(elements)))
            condition = EVAL(condition, env)
            # empty lists, strings and 0 are 'truthy', only false and nil are 'falsy'
            if is_nil(condition) or is_bool(condition) and condition == FALSE:
                ast = false_branch
            else:
                ast = true_branch
            continue

        elif first(ast) == make_symbol('fn*'):
            try:
                op, binds, body = ast
            except ValueError:
                raise RuntimeError(
                    'fn* syntax us (fn* /arguments/ /function_body/)')  # noqa

            def closure(*arguments):
                try:
                    new_env = Env(outer=env, binds=binds, exprs=arguments)
                except ValueError:
                    raise RuntimeError(
                        'Error: function is called with wrong number of parameters'
                        f'expected: {len(binds.value)}, actual: {len(arguments)}'
                    )
                return EVAL(body, new_env)

            return make_function(body, binds, env, closure)

        elif first(ast) == make_symbol('do'):
            op, *exprs = ast
            for expr in exprs[:-1]:
                EVAL(expr, env)
            ast = exprs[-1]
            continue

        func, *args = eval_ast(ast, env)
        if not is_mal_function(func):
            # core function
            return func(*args)
        ast = func.ast
        env = Env(func.env, func.params, args)
Пример #4
0
def mal_map(fn, mal_iter):
    if is_mal_function(fn):
        fn = fn.fn
    return make_list(map(fn, mal_iter))
Пример #5
0
    'dissoc':
    dissoc,
    'readline':
    mal_readline,
    '*host-language*':
    make_string("\"python-by-davemus\""),
    'time-ms':
    lambda: time() / 1000,
    # metadata is not supported in my mal implementation
    'meta':
    meta,
    'with-meta':
    with_meta,
    'fn?':
    lambda entity: is_function(entity) or
    (is_mal_function(entity) and not entity.is_macro),
    'macro?':
    lambda entity: is_mal_function(entity) and entity.is_macro,
    'string?':
    is_string,
    'number?':
    is_number,
    'seq':
    seq,
    'conj':
    conj,
    'py-eval':
    py_eval,
}

namespace = {make_symbol(k): v for k, v in namespace_.items()}