Esempio n. 1
0
def _setup_repl_env():
    repl_env = Env()
    repl_env.set(make_symbol('+'), lambda a, b: a + b)
    repl_env.set(make_symbol('-'), lambda a, b: a - b)
    repl_env.set(make_symbol('*'), lambda a, b: a * b)
    repl_env.set(make_symbol('/'), lambda a, b: a / b)
    return repl_env
Esempio n. 2
0
def read_form(reader):
    curr_token = reader.peek()
    if curr_token in tuple('([{'):
        token_to_type = {
            '(': make_list,
            '[': make_vector,
            '{': make_hashmap,
        }
        sequential = token_to_type[curr_token]
        return read_list(reader, sequential)
    elif curr_token == '@':
        reader.next()
        return make_list([make_symbol('deref'), read_form(reader)])
    elif curr_token == '\'':
        reader.next()
        return make_list([make_symbol('quote'), read_form(reader)])
    elif curr_token == '`':
        reader.next()
        return make_list([make_symbol('quasiquote'), read_form(reader)])
    elif curr_token == '~':
        reader.next()
        return make_list([make_symbol('unquote'), read_form(reader)])
    elif curr_token == '~@':
        reader.next()
        return make_list([make_symbol('splice-unquote'), read_form(reader)])
    elif curr_token == '^':
        reader.next()
        term2 = read_form(reader)
        term1 = read_form(reader)
        return make_list([make_symbol('with-meta'), term1, term2])
    return read_atom(reader)
Esempio n. 3
0
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)
Esempio n. 4
0
def read_atom(reader):
    token = reader.next()
    if token == 'nil':
        return NIL
    elif token == 'true':
        return TRUE
    elif token == 'false':
        return FALSE
    elif re.match(r'-?\d+\.*\d*', token):
        return make_number(token)
    elif token.startswith('"'):
        if not valid_string(token):
            raise MalException('EOF')
        return make_string(token)
    elif token.startswith(':'):
        return make_keyword(token)
    elif re.match(r'.*', token):
        return make_symbol(token)
    raise MalException('Input/output error')
Esempio n. 5
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
Esempio n. 6
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)
Esempio n. 7
0
    return make_list(concatenated)


def apply_(fn, *args):
    if is_mal_function(fn):
        fn = fn.fn
    args = _flatten(args)
    return fn(*args)


def rep(arg):
    return PRINT(EVAL(READ(arg), repl_env))


# setup env step 2
repl_env.set(make_symbol('eval'), eval_)
rep("(def! not (fn* (a) (if a false true)))")
rep("""(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))""")
rep("""(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))""")  # noqa
repl_env.set(make_symbol('apply'), apply_)
repl_env.set(make_symbol('map'), mal_map)
repl_env.set(make_symbol('throw'), throw)

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--interactive', action='store_true')
parser.add_argument('filename', nargs='?', help='Filename to be executed')
parser.add_argument('prog_args', nargs='*', help='Arguments passed to program')
args = parser.parse_args()


if __name__ == '__main__':
Esempio n. 8
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)
Esempio n. 9
0
    """
    Convert result of mal instructions into string representation.
    """
    return pr_str(mal_type)


def eval_(ast):
    return EVAL(ast, repl_env)


def rep(arg):
    return PRINT(EVAL(READ(arg), repl_env))


# setup env step 2
repl_env.set(make_symbol('eval'), eval_)
rep("(def! not (fn* (a) (if a false true)))")
rep("""(def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) "\nnil)")))))"""
    )

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--interactive', action='store_true')
parser.add_argument('filename', nargs='?', help='Filename to be executed')
parser.add_argument('prog_args', nargs='*', help='Arguments passed to program')
args = parser.parse_args()

if __name__ == '__main__':
    arg_to_str = lambda arg: f'"{arg}"'
    rep(f'(def! *ARGV* {"(list " +  " ".join(arg_to_str(arg) for arg in args.prog_args) + ")" })'
        )
    if args.filename is not None:
Esempio n. 10
0
File: env.py Progetto: davemus/mal
from mal_types import (
    make_symbol, make_list, is_symbol, MalException
)


VARIADIC_ASSIGNMENT_SYMBOL = make_symbol('&')


class Env:
    def __init__(self, outer=None, binds=[], exprs=[]):
        self._scope = {}
        self._outer = outer
        if (
            len(binds) != len(exprs)
            and VARIADIC_ASSIGNMENT_SYMBOL not in binds
        ):
            raise ValueError
        for idx, elem in enumerate(binds):
            if elem == VARIADIC_ASSIGNMENT_SYMBOL:
                self.set(binds[idx + 1], make_list(exprs[idx:]))
                return
            self.set(elem, exprs[idx])

    def set(self, name, value):
        self._scope[name] = value

    def find(self, name):
        if name in self._scope:
            return self
        elif self._outer is not None:
            return self._outer.find(name)
Esempio n. 11
0
File: core.py Progetto: davemus/mal
    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()}
Esempio n. 12
0
from mal_types import (
    is_vector,
    make_vector,
    is_hashmap,
    make_hashmap_from_pydict,
    items,
    is_list,
    make_list,
    is_empty,
    iterate,
    is_symbol,
    make_symbol,
)

repl_env = {
    make_symbol('+'): lambda a, b: a + b,
    make_symbol('-'): lambda a, b: a - b,
    make_symbol('*'): lambda a, b: a * b,
    make_symbol('/'): lambda a, b: a / b
}


def eval_ast(ast, env):
    if is_vector(ast):
        return make_vector(EVAL(elem, env) for elem in iterate(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)