def macroexpand(ast, env): while is_macro_call(ast, env): fn_name, *arguments = ast macro_fn = env.get(fn_name) new_env = Env(macro_fn.env, macro_fn.params, make_list(arguments)) ast = EVAL(macro_fn.ast, new_env) return ast
def _flatten(args): concatenated = [] for elt in args: if is_iterable(elt) and not is_empty(elt): concatenated += elt else: concatenated.append(elt) if len(args) == 1: return args[0] return make_list(concatenated)
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
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)
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
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 py_eval(expression): result = eval(expression) if isinstance(result, (tuple, list)): return make_list(result) elif isinstance(result, str): return result elif isinstance(result, bool): return result elif isinstance(result, dict): return make_hashmap_from_pydict(result) elif isinstance(result, (int, float)): return make_number(result) elif result is None: return NIL raise TypeError(f'Can\'t convert python type {type(result)} to mal type')
def mal_map(fn, mal_iter): if is_mal_function(fn): fn = fn.fn return make_list(map(fn, mal_iter))
def seq(entity): if ((is_iterable(entity) or is_string(entity)) and len(entity)): return make_list(entity) return NIL
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')
'*': mul, '/': truediv, '<': lt, '<=': le, '>': gt, '>=': ge, '=': equal, 'list': lambda *args: make_list(args), 'list?': is_list, 'prn': prn, 'println': println, 'pr-str': pr_str_, 'str': str_, 'empty?': is_empty, 'count': count, 'read-string':