def quasiquote(ast): if not is_pair(ast): return types._list(types._symbol("quote"), ast) elif ast[0] == "unquote": return ast[1] elif is_pair(ast[0]) and ast[0][0] == "splice-unquote": return types._list(types._symbol("concat"), ast[0][1], quasiquote(ast[1:])) else: return types._list(types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast[1:]))
def quasiquote(ast): if not is_pair(ast): return types._list(types._symbol("quote"), ast) elif ast[0] == 'unquote': return ast[1] elif is_pair(ast[0]) and ast[0][0] == 'splice-unquote': return types._list(types._symbol("concat"), ast[0][1], quasiquote(ast[1:])) else: return types._list(types._symbol("cons"), quasiquote(ast[0]), quasiquote(ast[1:]))
def quasiquote(ast): if types._list_Q(ast): if len(ast) == 2 and ast[0] == u'unquote': return ast[1] else: return qq_foldr(ast) elif types._hash_map_Q(ast) or types._symbol_Q(ast): return types._list(types._symbol(u'quote'), ast) elif types._vector_Q(ast): return types._list(types._symbol(u'vec'), qq_foldr(ast)) else: return ast
def quasiquote(ast): if not is_pair(ast): return _list(_symbol(u"quote"), ast) else: a0 = ast[0] if isinstance(a0, MalSym): if a0.value == u'unquote': return ast[1] if is_pair(a0) and isinstance(a0[0], MalSym): a00 = a0[0] if (isinstance(a00, MalSym) and a00.value == u'splice-unquote'): return _list(_symbol(u"concat"), a0[1], quasiquote(ast.rest())) return _list(_symbol(u"cons"), quasiquote(a0), quasiquote(ast.rest()))
def quasiquote(ast): if types._list_Q(ast): if len(ast) == 2: fst = ast[0] if isinstance(fst, MalSym) and fst.value == u"unquote": return ast[1] return qq_foldr(ast.values) elif types._vector_Q(ast): return _list(_symbol(u"vec"), qq_foldr(ast.values)) elif types._symbol_Q(ast) or types._hash_map_Q(ast): return _list(_symbol(u"quote"), ast) else: return ast
def eval_ast(ast, env): if types._symbol_Q(ast): return env.get(ast) elif types._list_Q(ast): return types._list(*map(lambda a: EVAL(a, env), ast)) elif types._vector_Q(ast): return types._vector(*map(lambda a: EVAL(a, env), ast)) elif types._hash_map_Q(ast): return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items()) else: return ast # primitive value, return unchanged
def eval_ast(ast, env): if types._symbol_Q(ast): try: return env[ast] except: raise Exception("'" + ast + "' not found") elif types._list_Q(ast): return types._list(*map(lambda a: EVAL(a, env), ast)) elif types._vector_Q(ast): return types._vector(*map(lambda a: EVAL(a, env), ast)) elif types._hash_map_Q(ast): return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items()) else: return ast # primitive value, return unchanged
def eval_ast(ast, env): if types._symbol_Q(ast): return env.get(ast) elif types._list_Q(ast): return types._list(*map(lambda a: EVAL(a, env), ast)) elif types._vector_Q(ast): return types._vector(*map(lambda a: EVAL(a, env), ast)) elif types._hash_map_Q(ast): keyvals = [] for k in ast.keys(): keyvals.append(EVAL(k, env)) keyvals.append(EVAL(ast[k], env)) return types._hash_map(*keyvals) else: return ast # primitive value, return unchanged
def eval_ast(ast, env): if types._symbol_Q(ast): try: return env[ast] except: raise Exception("'" + ast + "' not found") elif types._list_Q(ast): return types._list(*map(lambda a: EVAL(a, env), ast)) elif types._vector_Q(ast): return types._vector(*map(lambda a: EVAL(a, env), ast)) elif types._hash_map_Q(ast): keyvals = [] for k in ast.keys(): keyvals.append(EVAL(k, env)) keyvals.append(EVAL(ast[k], env)) return types._hash_map(*keyvals) else: return ast # primitive value, return unchanged
def read_form(reader): token = reader.peek() # reader macros/transforms if token[0] == ";": reader.next() return None elif token == "'": reader.next() return _list(_symbol("quote"), read_form(reader)) elif token == "`": reader.next() return _list(_symbol("quasiquote"), read_form(reader)) elif token == "~": reader.next() return _list(_symbol("unquote"), read_form(reader)) elif token == "~@": reader.next() return _list(_symbol("splice-unquote"), read_form(reader)) elif token == "^": reader.next() meta = read_form(reader) return _list(_symbol("with-meta"), read_form(reader), meta) elif token == "@": reader.next() return _list(_symbol("deref"), read_form(reader)) # list elif token == ")": raise Exception("unexpected ')'") elif token == "(": return read_list(reader) # vector elif token == "]": raise Exception("unexpected ']'") elif token == "[": return read_vector(reader) # hash-map elif token == "}": raise Exception("unexpected '}'") elif token == "{": return read_hash_map(reader) # atom else: return read_atom(reader)
def read_form(reader): token = reader.peek() # reader macros/transforms if token[0] == ';': reader.next() return None elif token == '\'': reader.next() return _list(MalSym(u'quote'), read_form(reader)) elif token == '`': reader.next() return _list(MalSym(u'quasiquote'), read_form(reader)) elif token == '~': reader.next() return _list(MalSym(u'unquote'), read_form(reader)) elif token == '~@': reader.next() return _list(MalSym(u'splice-unquote'), read_form(reader)) elif token == '^': reader.next() meta = read_form(reader) return _list(MalSym(u'with-meta'), read_form(reader), meta) elif token == '@': reader.next() return _list(MalSym(u'deref'), read_form(reader)) # list elif token == ')': types.throw_str("unexpected ')'") elif token == '(': return read_list(reader) # vector elif token == ']': types.throw_str("unexpected ']'") elif token == '[': return read_vector(reader) # hash-map elif token == '}': types.throw_str("unexpected '}'") elif token == '{': return read_hash_map(reader) # atom else: return read_atom(reader)
def read_form(reader): token = reader.peek() # reader macros/transforms if token[0] == ';': reader.next() return None elif token == '\'': reader.next() return _list(MalSym(u'quote'), read_form(reader)) elif token == '`': reader.next() return _list(MalSym(u'quasiquote'), read_form(reader)) elif token == '~': reader.next() return _list(MalSym(u'unquote'), read_form(reader)) elif token == '~@': reader.next() return _list(MalSym(u'splice-unquote'), read_form(reader)) elif token == '^': reader.next() meta = read_form(reader) return _list(MalSym(u'with-meta'), read_form(reader), meta) elif token == '@': reader.next() return _list(MalSym(u'deref'), read_form(reader)) # list elif token == ')': types.throw_str("unexpected ')'") elif token == '(': return read_list(reader) # vector elif token == ']': types.throw_str("unexpected ']'"); elif token == '[': return read_vector(reader); # hash-map elif token == '}': types.throw_str("unexpected '}'"); elif token == '{': return read_hash_map(reader); # atom else: return read_atom(reader);
def qq_foldr(seq): return functools.reduce(qq_loop, reversed(seq), types._list())
def qq_loop(acc, elt): if types._list_Q(elt) and len(elt) == 2 and elt[0] == u'splice-unquote': return types._list(types._symbol(u'concat'), elt[1], acc) else: return types._list(types._symbol(u'cons'), quasiquote(elt), acc)
return printer._pr_str(exp) # repl repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself 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)))))))" ) if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') sys.exit(0) # repl loop while True: try:
def EVAL(ast, env): while True: #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) if len(ast) == 0: return ast # apply list ast = macroexpand(ast, env) if not types._list_Q(ast): return eval_ast(ast, env) if len(ast) == 0: return ast a0 = ast[0] if isinstance(a0, MalSym): a0sym = a0.value else: a0sym = u"__<*fn*>__" if u"def!" == a0sym: a1, a2 = ast[1], ast[2] res = EVAL(a2, env) return env.set(a1, res) elif u"let*" == a0sym: a1, a2 = ast[1], ast[2] let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) ast = a2 env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] elif u"quasiquote" == a0sym: ast = quasiquote(ast[1]) # Continue loop (TCO) elif u"defmacro!" == a0sym: func = EVAL(ast[2], env) func.ismacro = True return env.set(ast[1], func) elif u"macroexpand" == a0sym: return macroexpand(ast[1], env) elif u"try*" == a0sym: if len(ast) < 3: return EVAL(ast[1], env); a1, a2 = ast[1], ast[2] a20 = a2[0] if isinstance(a20, MalSym): if a20.value == u"catch*": try: return EVAL(a1, env); except types.MalException as exc: exc = exc.object catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) except Exception as exc: exc = MalStr(unicode("%s" % exc)) catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) return EVAL(a1, env); elif u"do" == a0sym: if len(ast) == 0: return nil elif len(ast) > 1: eval_ast(ast.slice2(1, len(ast)-1), env) ast = ast[-1] # Continue loop (TCO) elif u"if" == a0sym: a1, a2 = ast[1], ast[2] cond = EVAL(a1, env) if cond is nil or cond is false: if len(ast) > 3: ast = ast[3] # Continue loop (TCO) else: return nil else: ast = a2 # Continue loop (TCO) elif u"fn*" == a0sym: a1, a2 = ast[1], ast[2] return MalFunc(None, a2, env, a1, EVAL) else: el = eval_ast(ast, env) f = el.values[0] if isinstance(f, MalFunc): if f.ast: ast = f.ast env = f.gen_env(el.rest()) # Continue loop (TCO) else: return f.apply(el.rest()) else: raise Exception("%s is not callable" % f)
def qq_loop(elt, acc): if types._list_Q(elt) and len(elt) == 2: fst = elt[0] if isinstance(fst, MalSym) and fst.value == u"splice-unquote": return _list(_symbol(u"concat"), elt[1], acc) return _list(_symbol(u"cons"), quasiquote(elt), acc)
def EVAL(ast, env): while True: #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) if len(ast) == 0: return ast # apply list ast = macroexpand(ast, env) if not types._list_Q(ast): return eval_ast(ast, env) if len(ast) == 0: return ast a0 = ast[0] if isinstance(a0, MalSym): a0sym = a0.value else: a0sym = u"__<*fn*>__" if u"def!" == a0sym: a1, a2 = ast[1], ast[2] res = EVAL(a2, env) return env.set(a1, res) elif u"let*" == a0sym: a1, a2 = ast[1], ast[2] let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i + 1], let_env)) ast = a2 env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] elif u"quasiquote" == a0sym: ast = quasiquote(ast[1]) # Continue loop (TCO) elif u"defmacro!" == a0sym: func = EVAL(ast[2], env) func.ismacro = True return env.set(ast[1], func) elif u"macroexpand" == a0sym: return macroexpand(ast[1], env) elif u"try*" == a0sym: if len(ast) < 3: return EVAL(ast[1], env) a1, a2 = ast[1], ast[2] a20 = a2[0] if isinstance(a20, MalSym): if a20.value == u"catch*": try: return EVAL(a1, env) except types.MalException as exc: exc = exc.object catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) except Exception as exc: exc = MalStr(unicode("%s" % exc)) catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) return EVAL(a1, env) elif u"do" == a0sym: if len(ast) == 0: return nil elif len(ast) > 1: eval_ast(ast.slice2(1, len(ast) - 1), env) ast = ast[-1] # Continue loop (TCO) elif u"if" == a0sym: a1, a2 = ast[1], ast[2] cond = EVAL(a1, env) if cond is nil or cond is false: if len(ast) > 3: ast = ast[3] # Continue loop (TCO) else: return nil else: ast = a2 # Continue loop (TCO) elif u"fn*" == a0sym: a1, a2 = ast[1], ast[2] return MalFunc(None, a2, env, a1, EVAL) else: el = eval_ast(ast, env) f = el.values[0] if isinstance(f, MalFunc): if f.ast: ast = f.ast env = f.gen_env(el.rest()) # Continue loop (TCO) else: return f.apply(el.rest()) else: raise Exception("%s is not callable" % f)
def keys(hm): return types._list(*hm.keys()) def vals(hm): return types._list(*hm.values())
def vals(hm): return types._list(*hm.values()) # Sequence functions def coll_Q(coll): return sequential_Q(coll) or hash_map_Q(coll)
else: return f(*el[1:]) # print def PRINT(exp): return printer._pr_str(exp) # repl repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python for k, v in core.ns.items(): repl_env.set(k, v) repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) repl_env.set('*ARGV*', types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") 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)))))))") REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') sys.exit(0) # repl loop while True: try: line = mal_readline.readline("user> ")
def EVAL(ast, env): while True: #print("EVAL %s" % printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) elif types._vector_Q(ast): res = [] for a in ast.values: res.append(EVAL(a, env)) return MalVector(res) elif types._hash_map_Q(ast): new_dct = {} for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) elif not types._list_Q(ast): return ast # primitive value, return unchanged else: # apply list if len(ast) == 0: return ast a0 = ast[0] if isinstance(a0, MalSym): a0sym = a0.value else: a0sym = u"__<*fn*>__" if u"def!" == a0sym: a1, a2 = ast[1], ast[2] res = EVAL(a2, env) return env.set(a1, res) elif u"let*" == a0sym: a1, a2 = ast[1], ast[2] let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i + 1], let_env)) ast = a2 env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] elif u"quasiquote" == a0sym: ast = quasiquote(ast[1]) # Continue loop (TCO) elif u"defmacro!" == a0sym: func = EVAL(ast[2], env) func.ismacro = True return env.set(ast[1], func) elif u"try*" == a0sym: if len(ast) < 3: return EVAL(ast[1], env) a1, a2 = ast[1], ast[2] a20 = a2[0] if isinstance(a20, MalSym): if a20.value == u"catch*": try: return EVAL(a1, env) except types.MalException as exc: exc = exc.object catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) except Exception as exc: exc = MalStr(unicode("%s" % exc)) catch_env = Env(env, _list(a2[1]), _list(exc)) return EVAL(a2[2], catch_env) return EVAL(a1, env) elif u"do" == a0sym: if len(ast) == 0: return nil for i in range(1, len(ast) - 1): EVAL(ast[i], env) ast = ast[-1] # Continue loop (TCO) elif u"if" == a0sym: a1, a2 = ast[1], ast[2] cond = EVAL(a1, env) if cond is nil or cond is false: if len(ast) > 3: ast = ast[3] # Continue loop (TCO) else: return nil else: ast = a2 # Continue loop (TCO) elif u"fn*" == a0sym: a1, a2 = ast[1], ast[2] return MalFunc(None, a2, env, a1, EVAL) else: f = EVAL(a0, env) args = ast.rest() if f.ismacro: ast = f.apply(ast.rest()) # Continue loop (TCO) else: res = [] for a in args.values: res.append(EVAL(a, env)) el = MalList(res) if isinstance(f, MalFunc): if f.ast: ast = f.ast env = f.gen_env(el) # Continue loop (TCO) else: return f.apply(el) else: raise Exception("%s is not callable" % f)
def keys(hm): return types._list(*hm.keys())
else: return f(*el[1:]) # print def PRINT(exp): return printer._pr_str(exp) # repl repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) # core.py: defined using python for k, v in core.ns.items(): repl_env.set(types._symbol(k), v) repl_env.set(types._symbol('eval'), lambda ast: EVAL(ast, repl_env)) repl_env.set(types._symbol('*ARGV*'), types._list(*sys.argv[2:])) # core.mal: defined using the language itself REP("(def! *host-language* \"python\")") REP("(def! not (fn* (a) (if a false true)))") REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") 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)))))))") REP("(def! *gensym-counter* (atom 0))") REP("(def! gensym (fn* [] (symbol (str \"G__\" (swap! *gensym-counter* (fn* [x] (+ 1 x)))))))") REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') sys.exit(0) # repl loop
def qq_foldr(seq): acc = _list() for elt in reversed(seq): acc = qq_loop(elt, acc) return acc