示例#1
0
文件: step2_eval.py 项目: davemus/mal
def EVAL(ast, env):
    if not is_list(ast):
        return eval_ast(ast, repl_env)
    elif is_empty(ast):
        return ast
    func, *args = iterate(eval_ast(ast, repl_env))
    return func(*args)
示例#2
0
def is_macro_call(ast, env):
    try:
        return (
            is_list(ast)
            and is_symbol(ast[0])
            and env.get(ast[0]).is_macro
        )  # function, that is macros
    except (AttributeError, RuntimeError):
        return False
示例#3
0
def quasiquote(ast):
    if is_list(ast):
        if is_empty(ast):
            return ast
        if ast[0] == make_symbol('unquote'):
            return ast[1]
        else:
            processed = []
            for elt in ast[::-1]:
                if is_list(elt) and not is_empty(elt) and elt[0] == make_symbol('splice-unquote'):
                    processed = make_list([make_symbol('concat'), elt[1], processed])
                else:
                    processed = make_list([make_symbol('cons'), quasiquote(elt), processed])
            return make_list(processed)
    elif is_vector(ast):
        return make_list([make_symbol('vec'), *ast])
    elif is_symbol(ast) or is_hashmap(ast):
        return make_list([make_symbol('quote'), ast])
    return ast
示例#4
0
def eval_ast(ast, env):
    if is_vector(ast):
        return make_vector(EVAL(elem, env) for elem in ast)
    if is_hashmap(ast):
        return make_hashmap_from_pydict(
            {key: EVAL(value, env) for key, value in items(ast)}
        )
    if is_symbol(ast):
        return env.get(ast)
    elif is_list(ast):
        return make_list(EVAL(elem, env) for elem in ast)
    else:
        return ast
示例#5
0
文件: step3_env.py 项目: davemus/mal
def EVAL(ast, env):
    """
    Evaluate set of mal instructions.
    """
    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))
        return EVAL(instructions, new_env)
    func, *args = eval_ast(ast, env)
    return func(*args)
示例#6
0
文件: printer.py 项目: davemus/mal
def pr_str(entity, print_readably=True):
    if isinstance(entity, MalException):
        return f'{pr_str(entity.value)}'
    if isinstance(entity, Exception):
        return entity.args[0]
    elif is_function(entity):
        return '#function'
    elif is_nil(entity):
        return 'nil'
    elif is_bool(entity):
        return {
            TRUE: 'true',
            FALSE: 'false',
        }[entity]
    elif is_number(entity):
        return str(entity)
    elif is_symbol(entity):
        return str(entity, 'utf-8')
    elif is_keyword(entity):
        return ':' + entity[1:]
    elif is_string(entity):
        if print_readably:
            return '"' + entity.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n') + '"'
        return entity
    elif is_atom(entity):
        return f'(atom {deref(entity)})'
    elif is_list(entity):
        return '(' + ' '.join(pr_str(inner, print_readably) for inner in entity) + ')'
    elif is_vector(entity):
        return '[' + ' '.join(pr_str(inner, print_readably) for inner in entity) + ']'
    elif is_hashmap(entity):
        return (
            '{'
            + ' '.join(f'{pr_str(k, print_readably)} {pr_str(v, print_readably)}' for k, v in entity.items())
            + '}'
        )
    raise RuntimeError(f'pr_str: unknown type {type(entity)}')
示例#7
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)
示例#8
0
文件: step5_tco.py 项目: davemus/mal
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)
示例#9
0
文件: core.py 项目: davemus/mal
def conj(collection, *args):
    if is_list(collection):
        return make_list([*reversed(args), *collection])
    if is_vector(collection):
        return make_vector([*collection, *args])
    raise TypeError('conj element 1 should be a collection')