From a34b02006527d28db5df5c6553be804770b81f79 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 9 Apr 2014 23:58:27 -0500 Subject: Fix metadata on functions. - Don't use metadata to store ast, env, params data. - In Clojure, store metadata on the :meta key of the real metadata. This also allows using any datatype as metadata. --- python/core.py | 5 ++++- python/mal_types.py | 15 ++++++++++----- python/step5_tco.py | 7 +++---- python/step6_file.py | 7 +++---- python/step7_quote.py | 7 +++---- python/step8_macros.py | 7 +++---- python/step9_interop.py | 7 +++---- python/stepA_more.py | 7 +++---- 8 files changed, 32 insertions(+), 30 deletions(-) (limited to 'python') diff --git a/python/core.py b/python/core.py index 28d9c5e..5e2ef75 100644 --- a/python/core.py +++ b/python/core.py @@ -85,7 +85,10 @@ def mapf(f, lst): return List(map(f, lst)) # Metadata functions def with_meta(obj, meta): - new_obj = copy.copy(obj) + if type(obj) == type(lambda x:x): + new_obj = obj.__copy__() + else: + new_obj = copy.copy(obj) new_obj.__meta__ = meta return new_obj diff --git a/python/mal_types.py b/python/mal_types.py index 15e4b6b..74409c9 100644 --- a/python/mal_types.py +++ b/python/mal_types.py @@ -38,11 +38,16 @@ def _symbol(str): return Symbol(str) def _symbol_Q(exp): return type(exp) == Symbol # Functions -def _function(Eval, Env, exp, env, params): - def f(*args): - return Eval(exp, Env(env, params, args)) - f.__meta__ = {"exp": exp, "env": env, "params": params} - return f +def _function(Eval, Env, ast, env, params): + def gen_fn(): + def fn(*args): + return Eval(ast, Env(env, params, args)) + fn.__meta__ = None + fn.__ast__ = ast + fn.__gen_env__ = lambda args: Env(env, params, args) + fn.__copy__ = gen_fn + return fn + return gen_fn() def _function_Q(f): return type(f) == type(function_Q) # lists diff --git a/python/step5_tco.py b/python/step5_tco.py index 4335a96..d9d34bc 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -65,10 +65,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) diff --git a/python/step6_file.py b/python/step6_file.py index 9e0b8cd..f3847d2 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -65,10 +65,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) diff --git a/python/step7_quote.py b/python/step7_quote.py index 3ed6965..f57dc0a 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -87,10 +87,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) diff --git a/python/step8_macros.py b/python/step8_macros.py index 6e2bd45..5d9151a 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -107,10 +107,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) diff --git a/python/step9_interop.py b/python/step9_interop.py index 66a8c50..521a3c2 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -116,10 +116,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) diff --git a/python/stepA_more.py b/python/stepA_more.py index 7c6a5da..21c1641 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -127,10 +127,9 @@ def EVAL(ast, env): else: el = eval_ast(ast, env) f = el[0] - if hasattr(f, '__meta__') and f.__meta__.has_key("exp"): - m = f.__meta__ - ast = m['exp'] - env = Env(m['env'], m['params'], el[1:]) + if hasattr(f, '__ast__'): + ast = f.__ast__ + env = f.__gen_env__(el[1:]) else: return f(*el[1:]) -- cgit v1.2.3 From 712af9efbe15a9a65c25ab92ee2a49c8e749ed3d Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 10 Apr 2014 19:27:42 -0500 Subject: Add stats-lisp target (only env, core, stepA) --- python/Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'python') diff --git a/python/Makefile b/python/Makefile index 3985d14..b461db3 100644 --- a/python/Makefile +++ b/python/Makefile @@ -2,8 +2,10 @@ TESTS = -SOURCES = mal_readline.py mal_types.py reader.py printer.py \ - env.py core.py stepA_more.py +SOURCES_BASE = mal_readline.py mal_types.py reader.py printer.py +SOURCES_LISP = env.py core.py stepA_more.py +SOURCES = $(SOURCES_BASE) $(SOURCES_LISP) + #all: mal.sh # @@ -20,6 +22,8 @@ SOURCES = mal_readline.py mal_types.py reader.py printer.py \ stats: $(SOURCES) @wc $^ +stats-lisp: $(SOURCES_LISP) + @wc $^ tests: $(TESTS) -- cgit v1.2.3 From 7e9a2883fe5c25a521b1dc37e4c549e1ed508ece Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 15 Apr 2014 01:24:43 -0500 Subject: All: fix get. All pass stepA tests. --- python/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python') diff --git a/python/core.py b/python/core.py index 5e2ef75..8b01b69 100644 --- a/python/core.py +++ b/python/core.py @@ -39,7 +39,7 @@ def dissoc(src_hm, *keys): return hm def get(hm, key): - if key in hm: + if hm and key in hm: return hm[key] else: return None -- cgit v1.2.3 From a05f7822b10ed4cdd61ed8384299a003baf1c1c6 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 16 Apr 2014 22:42:17 -0500 Subject: Python: metadata on builtin funcs. Support python3. --- python/core.py | 11 ++++------- python/mal_readline.py | 8 ++++++-- python/mal_types.py | 38 ++++++++++++++++++++++++++++---------- python/printer.py | 2 +- python/step1_read_print.py | 2 +- python/step2_eval.py | 4 ++-- python/step3_env.py | 4 ++-- python/step4_if_fn_do.py | 2 +- python/step5_tco.py | 2 +- python/step6_file.py | 2 +- python/step7_quote.py | 2 +- python/step8_macros.py | 2 +- python/step9_interop.py | 2 +- python/stepA_more.py | 9 ++++++--- 14 files changed, 56 insertions(+), 34 deletions(-) (limited to 'python') diff --git a/python/core.py b/python/core.py index 8b01b69..20ac793 100644 --- a/python/core.py +++ b/python/core.py @@ -17,12 +17,12 @@ def do_str(*args): return "".join(map(lambda exp: printer._pr_str(exp, False), args)) def prn(*args): - print " ".join(map(lambda exp: printer._pr_str(exp, True), args)) + print(" ".join(map(lambda exp: printer._pr_str(exp, True), args))) return None def println(*args): line = " ".join(map(lambda exp: printer._pr_str(exp, False), args)) - print line.replace('\\n', '\n') + print(line.replace('\\n', '\n')) return None @@ -85,10 +85,7 @@ def mapf(f, lst): return List(map(f, lst)) # Metadata functions def with_meta(obj, meta): - if type(obj) == type(lambda x:x): - new_obj = obj.__copy__() - else: - new_obj = copy.copy(obj) + new_obj = types._clone(obj) new_obj.__meta__ = meta return new_obj @@ -126,7 +123,7 @@ ns = { '+': lambda a,b: a+b, '-': lambda a,b: a-b, '*': lambda a,b: a*b, - '/': lambda a,b: a/b, + '/': lambda a,b: int(a/b), 'list': types._list, 'list?': types._list_Q, diff --git a/python/mal_readline.py b/python/mal_readline.py index e8cf957..2add6b5 100644 --- a/python/mal_readline.py +++ b/python/mal_readline.py @@ -1,7 +1,11 @@ -import os, readline as pyreadline +import os, sys, readline as pyreadline history_loaded = False histfile = os.path.expanduser("~/.mal-history") +if sys.version_info[0] >= 3: + rl = input +else: + rl = raw_input def readline(prompt="user> "): if not history_loaded: @@ -15,7 +19,7 @@ def readline(prompt="user> "): pass try: - line = raw_input(prompt) + line = rl(prompt) pyreadline.add_history(line) with open(histfile, "a") as hf: hf.write(line + "\n") diff --git a/python/mal_types.py b/python/mal_types.py index 74409c9..e6bd70d 100644 --- a/python/mal_types.py +++ b/python/mal_types.py @@ -1,3 +1,9 @@ +import sys, copy, types as pytypes +if sys.version_info[0] >= 3: + str_types = [str] +else: + str_types = [str, unicode] + # General functions def _equal_Q(a, b): @@ -26,11 +32,26 @@ def _equal_Q(a, b): def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq) +def _clone(obj): + #if type(obj) == type(lambda x:x): + if type(obj) == pytypes.FunctionType: + if obj.__code__: + return pytypes.FunctionType( + obj.__code__, obj.__globals__, name = obj.__name__, + argdefs = obj.__defaults__, closure = obj.__closure__) + else: + return pytypes.FunctionType( + obj.func_code, obj.func_globals, name = obj.func_name, + argdefs = obj.func_defaults, closure = obj.func_closure) + else: + return copy.copy(obj) + + # Scalars def _nil_Q(exp): return exp is None def _true_Q(exp): return exp is True def _false_Q(exp): return exp is False -def _string_Q(exp): return type(exp) in [str, unicode] +def _string_Q(exp): return type(exp) in str_types # Symbols class Symbol(str): pass @@ -39,15 +60,12 @@ def _symbol_Q(exp): return type(exp) == Symbol # Functions def _function(Eval, Env, ast, env, params): - def gen_fn(): - def fn(*args): - return Eval(ast, Env(env, params, args)) - fn.__meta__ = None - fn.__ast__ = ast - fn.__gen_env__ = lambda args: Env(env, params, args) - fn.__copy__ = gen_fn - return fn - return gen_fn() + def fn(*args): + return Eval(ast, Env(env, params, args)) + fn.__meta__ = None + fn.__ast__ = ast + fn.__gen_env__ = lambda args: Env(env, params, args) + return fn def _function_Q(f): return type(f) == type(function_Q) # lists diff --git a/python/printer.py b/python/printer.py index d501d60..65bf256 100644 --- a/python/printer.py +++ b/python/printer.py @@ -13,7 +13,7 @@ def _pr_str(obj, print_readably=True): return "{" + " ".join(ret) + "}" elif types._string_Q(obj): if print_readably: - return '"' + obj.encode('unicode_escape').replace('"', '\\"') + '"' + return '"' + obj.encode('unicode_escape').decode('latin1').replace('"', '\\"') + '"' else: return obj elif types._nil_Q(obj): diff --git a/python/step1_read_print.py b/python/step1_read_print.py index 0315cf0..7c5b7d8 100644 --- a/python/step1_read_print.py +++ b/python/step1_read_print.py @@ -27,4 +27,4 @@ while True: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step2_eval.py b/python/step2_eval.py index e50d231..a5061dc 100644 --- a/python/step2_eval.py +++ b/python/step2_eval.py @@ -48,7 +48,7 @@ def REP(str): repl_env['+'] = lambda a,b: a+b repl_env['-'] = lambda a,b: a-b repl_env['*'] = lambda a,b: a*b -repl_env['/'] = lambda a,b: a/b +repl_env['/'] = lambda a,b: int(a/b) while True: try: @@ -58,4 +58,4 @@ while True: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step3_env.py b/python/step3_env.py index 21879d6..f684274 100644 --- a/python/step3_env.py +++ b/python/step3_env.py @@ -62,7 +62,7 @@ def _ref(k,v): repl_env.set(k, v) _ref('+', lambda a,b: a+b) _ref('-', lambda a,b: a-b) _ref('*', lambda a,b: a*b) -_ref('/', lambda a,b: a/b) +_ref('/', lambda a,b: int(a/b)) while True: try: @@ -72,4 +72,4 @@ while True: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py index d0a7fc3..d9910f6 100644 --- a/python/step4_if_fn_do.py +++ b/python/step4_if_fn_do.py @@ -88,4 +88,4 @@ while True: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step5_tco.py b/python/step5_tco.py index d9d34bc..633cbd8 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -95,4 +95,4 @@ while True: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step6_file.py b/python/step6_file.py index f3847d2..1a992cc 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -103,4 +103,4 @@ else: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step7_quote.py b/python/step7_quote.py index f57dc0a..cae3c99 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -125,4 +125,4 @@ else: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step8_macros.py b/python/step8_macros.py index 5d9151a..adb4546 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -145,4 +145,4 @@ else: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step9_interop.py b/python/step9_interop.py index 521a3c2..1d8e55a 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -154,4 +154,4 @@ else: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/stepA_more.py b/python/stepA_more.py index 21c1641..dccce8e 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -89,7 +89,10 @@ def EVAL(ast, env): elif 'macroexpand' == a0: return macroexpand(ast[1], env) elif "py!*" == a0: - exec compile(ast[1], '', 'single') in globals() + if sys.version_info[0] >= 3: + exec(compile(ast[1], '', 'single'), globals()) + else: + exec(compile(ast[1], '', 'single') in globals()) return None elif "py*" == a0: return eval(ast[1]) @@ -103,7 +106,7 @@ def EVAL(ast, env): try: return EVAL(a1, env); except Exception as exc: - exc = exc.message + exc = exc.args[0] catch_env = Env(env, [a2[1]], [exc]) return EVAL(a2[2], catch_env) else: @@ -168,4 +171,4 @@ else: print(REP(line)) except reader.Blank: continue except Exception as e: - print "".join(traceback.format_exception(*sys.exc_info())) + print("".join(traceback.format_exception(*sys.exc_info()))) -- cgit v1.2.3 From 8cb5cda46cf3aef847ae3926dc53a5e5f87fe261 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 16 Apr 2014 23:57:50 -0500 Subject: All: move some fns to core. Major cleanup. - Don't import/require core until step4. - Define cond/or macros from step8 --- python/core.py | 6 ++++++ python/step3_env.py | 9 ++++----- python/step4_if_fn_do.py | 7 +++---- python/step5_tco.py | 7 +++---- python/step6_file.py | 12 ++++-------- python/step7_quote.py | 12 ++++-------- python/step8_macros.py | 14 ++++++-------- python/step9_interop.py | 14 ++++++-------- python/stepA_more.py | 15 +++++---------- 9 files changed, 41 insertions(+), 55 deletions(-) (limited to 'python') diff --git a/python/core.py b/python/core.py index 20ac793..54737aa 100644 --- a/python/core.py +++ b/python/core.py @@ -3,6 +3,8 @@ from itertools import chain import mal_types as types from mal_types import List, Vector +import mal_readline +import reader import printer # Errors/Exceptions @@ -112,10 +114,14 @@ ns = { 'false?': types._false_Q, 'symbol': types._symbol, 'symbol?': types._symbol_Q, + 'pr-str': pr_str, 'str': do_str, 'prn': prn, 'println': println, + 'readline': lambda prompt: mal_readline.readline(prompt), + 'read-string': reader.read_str, + 'slurp': lambda file: open(file).read(), '<': lambda a,b: a': lambda a,b: a>b, diff --git a/python/step3_env.py b/python/step3_env.py index f684274..372adc4 100644 --- a/python/step3_env.py +++ b/python/step3_env.py @@ -57,12 +57,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -_ref('+', lambda a,b: a+b) -_ref('-', lambda a,b: a-b) -_ref('*', lambda a,b: a*b) -_ref('/', lambda a,b: int(a/b)) +repl_env.set('+', lambda a,b: a+b) +repl_env.set('-', lambda a,b: a-b) +repl_env.set('*', lambda a,b: a*b) +repl_env.set('/', lambda a,b: int(a/b)) while True: try: diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py index d9910f6..a06d10b 100644 --- a/python/step4_if_fn_do.py +++ b/python/step4_if_fn_do.py @@ -72,12 +72,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") while True: diff --git a/python/step5_tco.py b/python/step5_tco.py index 633cbd8..7982073 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -79,12 +79,11 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# core.py: defined using python +for k, v in core.ns.items(): repl_env.set(k, v) -# Defined using the language itself +# core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") while True: diff --git a/python/step6_file.py b/python/step6_file.py index 1a992cc..ba2d355 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -79,16 +79,12 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# 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)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# 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) \")\")))))") diff --git a/python/step7_quote.py b/python/step7_quote.py index cae3c99..aefa421 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -101,16 +101,12 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# 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)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# 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) \")\")))))") diff --git a/python/step8_macros.py b/python/step8_macros.py index adb4546..90aedf3 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -121,18 +121,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# 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)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# 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] + '")') diff --git a/python/step9_interop.py b/python/step9_interop.py index 1d8e55a..eae7837 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -130,18 +130,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# 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)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# 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] + '")') diff --git a/python/stepA_more.py b/python/stepA_more.py index dccce8e..fa79ec3 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -144,21 +144,16 @@ def PRINT(exp): repl_env = Env() def REP(str): return PRINT(EVAL(READ(str), repl_env)) -def _ref(k,v): repl_env.set(k, v) -# Import types functions -for name, val in core.ns.items(): _ref(name, val) +# 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)) -_ref('readline', lambda prompt: mal_readline.readline(prompt)) -_ref('read-string', reader.read_str) -_ref('eval', lambda ast: EVAL(ast, repl_env)) -_ref('slurp', lambda file: open(file).read()) - -# Defined using the language itself +# 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))))))))") -REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -- cgit v1.2.3 From db4c329aff4621e05b92a55be4f18173f5a4f655 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 17 Apr 2014 21:49:07 -0500 Subject: All: perf test, Makefile refactor, add *host-language* Other: - bash,make,postscript: quasiquote of vectors - Fix Java slurp - add time function to core.mal - switches on *host-language* for make time-secs vs time-ms - Ignore */experiments directories --- python/core.py | 3 ++- python/stepA_more.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'python') diff --git a/python/core.py b/python/core.py index 54737aa..d10047d 100644 --- a/python/core.py +++ b/python/core.py @@ -1,4 +1,4 @@ -import copy +import copy, time from itertools import chain import mal_types as types @@ -130,6 +130,7 @@ ns = { '-': lambda a,b: a-b, '*': lambda a,b: a*b, '/': lambda a,b: int(a/b), + 'time-ms': lambda : int(time.time() * 1000), 'list': types._list, 'list?': types._list_Q, diff --git a/python/stepA_more.py b/python/stepA_more.py index fa79ec3..e4bc768 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -58,7 +58,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -150,6 +150,7 @@ for k, v in core.ns.items(): repl_env.set(k, v) repl_env.set('eval', lambda ast: EVAL(ast, repl_env)) # 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)))))))") -- cgit v1.2.3 From 718887c3019c49fc807bc18fbd5feb975ec03c85 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 17 Apr 2014 22:23:07 -0500 Subject: Ruby,Python,C#: readline history fixes. Ruby include path. --- python/mal_readline.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'python') diff --git a/python/mal_readline.py b/python/mal_readline.py index 2add6b5..fc22b58 100644 --- a/python/mal_readline.py +++ b/python/mal_readline.py @@ -8,7 +8,9 @@ else: rl = raw_input def readline(prompt="user> "): + global history_loaded if not history_loaded: + history_loaded = True try: with open(histfile, "r") as hf: for line in hf.readlines(): -- cgit v1.2.3 From 86b689f3d7111a9fa13da389a30f3dfdf877d1a4 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Sat, 19 Apr 2014 13:04:09 -0500 Subject: All: *ARGV* and *host-language*. Misc syncing/fixes. --- python/step0_repl.py | 3 ++- python/step1_read_print.py | 3 ++- python/step2_eval.py | 3 ++- python/step3_env.py | 3 ++- python/step4_if_fn_do.py | 3 ++- python/step5_tco.py | 3 ++- python/step6_file.py | 25 ++++++++++++++----------- python/step7_quote.py | 25 ++++++++++++++----------- python/step8_macros.py | 25 ++++++++++++++----------- python/step9_interop.py | 30 ++++++++++++++++++------------ python/stepA_more.py | 24 ++++++++++++++---------- 11 files changed, 86 insertions(+), 61 deletions(-) (limited to 'python') diff --git a/python/step0_repl.py b/python/step0_repl.py index 8d42c33..bb4d6bf 100644 --- a/python/step0_repl.py +++ b/python/step0_repl.py @@ -22,6 +22,7 @@ def PRINT(exp): def REP(str): return PRINT(EVAL(READ(str), {})) +# repl loop while True: try: line = mal_readline.readline("user> ") @@ -29,4 +30,4 @@ while True: if line == "": continue print(REP(line)) except Exception as e: - print "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step1_read_print.py b/python/step1_read_print.py index 7c5b7d8..3c2e4ac 100644 --- a/python/step1_read_print.py +++ b/python/step1_read_print.py @@ -9,7 +9,7 @@ def READ(str): # eval def EVAL(ast, env): - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) return ast def PRINT(exp): @@ -19,6 +19,7 @@ def PRINT(exp): def REP(str): return PRINT(EVAL(READ(str), {})) +# repl loop while True: try: line = mal_readline.readline("user> ") diff --git a/python/step2_eval.py b/python/step2_eval.py index a5061dc..8af09b1 100644 --- a/python/step2_eval.py +++ b/python/step2_eval.py @@ -28,7 +28,7 @@ def eval_ast(ast, env): return ast # primitive value, return unchanged def EVAL(ast, env): - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -50,6 +50,7 @@ repl_env['-'] = lambda a,b: a-b repl_env['*'] = lambda a,b: a*b repl_env['/'] = lambda a,b: int(a/b) +# repl loop while True: try: line = mal_readline.readline("user> ") diff --git a/python/step3_env.py b/python/step3_env.py index 372adc4..4565011 100644 --- a/python/step3_env.py +++ b/python/step3_env.py @@ -26,7 +26,7 @@ def eval_ast(ast, env): return ast # primitive value, return unchanged def EVAL(ast, env): - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -63,6 +63,7 @@ repl_env.set('-', lambda a,b: a-b) repl_env.set('*', lambda a,b: a*b) repl_env.set('/', lambda a,b: int(a/b)) +# repl loop while True: try: line = mal_readline.readline("user> ") diff --git a/python/step4_if_fn_do.py b/python/step4_if_fn_do.py index a06d10b..e99cfcf 100644 --- a/python/step4_if_fn_do.py +++ b/python/step4_if_fn_do.py @@ -27,7 +27,7 @@ def eval_ast(ast, env): return ast # primitive value, return unchanged def EVAL(ast, env): - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -79,6 +79,7 @@ for k, v in core.ns.items(): repl_env.set(k, v) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") +# repl loop while True: try: line = mal_readline.readline("user> ") diff --git a/python/step5_tco.py b/python/step5_tco.py index 7982073..72457e9 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -28,7 +28,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -86,6 +86,7 @@ for k, v in core.ns.items(): repl_env.set(k, v) # core.mal: defined using the language itself REP("(def! not (fn* (a) (if a false true)))") +# repl loop while True: try: line = mal_readline.readline("user> ") diff --git a/python/step6_file.py b/python/step6_file.py index ba2d355..764eb53 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -28,7 +28,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -83,6 +83,7 @@ def REP(str): # 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)))") @@ -90,13 +91,15 @@ REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")) if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -else: - while True: - try: - line = mal_readline.readline("user> ") - if line == None: break - if line == "": continue - print(REP(line)) - except reader.Blank: continue - except Exception as e: - print("".join(traceback.format_exception(*sys.exc_info()))) + sys.exit(0) + +# repl loop +while True: + try: + line = mal_readline.readline("user> ") + if line == None: break + if line == "": continue + print(REP(line)) + except reader.Blank: continue + except Exception as e: + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step7_quote.py b/python/step7_quote.py index aefa421..1002613 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -46,7 +46,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -105,6 +105,7 @@ def REP(str): # 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)))") @@ -112,13 +113,15 @@ REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")) if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -else: - while True: - try: - line = mal_readline.readline("user> ") - if line == None: break - if line == "": continue - print(REP(line)) - except reader.Blank: continue - except Exception as e: - print("".join(traceback.format_exception(*sys.exc_info()))) + sys.exit(0) + +# repl loop +while True: + try: + line = mal_readline.readline("user> ") + if line == None: break + if line == "": continue + print(REP(line)) + except reader.Blank: continue + except Exception as e: + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step8_macros.py b/python/step8_macros.py index 90aedf3..28b68fb 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -58,7 +58,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -125,6 +125,7 @@ def REP(str): # 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)))") @@ -134,13 +135,15 @@ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first x if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -else: - while True: - try: - line = mal_readline.readline("user> ") - if line == None: break - if line == "": continue - print(REP(line)) - except reader.Blank: continue - except Exception as e: - print("".join(traceback.format_exception(*sys.exc_info()))) + sys.exit(0) + +# repl loop +while True: + try: + line = mal_readline.readline("user> ") + if line == None: break + if line == "": continue + print(REP(line)) + except reader.Blank: continue + except Exception as e: + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/step9_interop.py b/python/step9_interop.py index eae7837..2f7c5e0 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -58,7 +58,7 @@ def eval_ast(ast, env): def EVAL(ast, env): while True: - #print("EVAL %s" % ast) + #print("EVAL %s" % printer._pr_str(ast)) if not types._list_Q(ast): return eval_ast(ast, env) @@ -89,7 +89,10 @@ def EVAL(ast, env): elif 'macroexpand' == a0: return macroexpand(ast[1], env) elif "py!*" == a0: - exec compile(ast[1], '', 'single') in globals() + if sys.version_info[0] >= 3: + exec(compile(ast[1], '', 'single'), globals()) + else: + exec(compile(ast[1], '', 'single') in globals()) return None elif "py*" == a0: return eval(ast[1]) @@ -134,6 +137,7 @@ def REP(str): # 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)))") @@ -143,13 +147,15 @@ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first x if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -else: - while True: - try: - line = mal_readline.readline("user> ") - if line == None: break - if line == "": continue - print(REP(line)) - except reader.Blank: continue - except Exception as e: - print("".join(traceback.format_exception(*sys.exc_info()))) + sys.exit(0) + +# repl loop +while True: + try: + line = mal_readline.readline("user> ") + if line == None: break + if line == "": continue + print(REP(line)) + except reader.Blank: continue + except Exception as e: + print("".join(traceback.format_exception(*sys.exc_info()))) diff --git a/python/stepA_more.py b/python/stepA_more.py index e4bc768..b9d8152 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -148,6 +148,7 @@ def REP(str): # 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! *host-language* \"python\")") @@ -158,13 +159,16 @@ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first x if len(sys.argv) >= 2: REP('(load-file "' + sys.argv[1] + '")') -else: - while True: - try: - line = mal_readline.readline("user> ") - if line == None: break - if line == "": continue - print(REP(line)) - except reader.Blank: continue - except Exception as e: - print("".join(traceback.format_exception(*sys.exc_info()))) + sys.exit(0) + +# repl loop +REP("(println (str \"Mal [\" *host-language* \"]\"))") +while True: + try: + line = mal_readline.readline("user> ") + if line == None: break + if line == "": continue + print(REP(line)) + except reader.Blank: continue + except Exception as e: + print("".join(traceback.format_exception(*sys.exc_info()))) -- cgit v1.2.3 From 89bd4de1e2704c1bc562788b2c5e4fc08b71a538 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 23 Apr 2014 21:46:57 -0500 Subject: Perl: add vector, hash-map, metadata, atom support. TCO let* - Changes all collections to be one level of inderection where the top level is always a hash containing 'meta' and 'val'. --- python/reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python') diff --git a/python/reader.py b/python/reader.py index 846e2a8..13b1f7b 100644 --- a/python/reader.py +++ b/python/reader.py @@ -100,5 +100,5 @@ def read_form(reader): def read_str(str): tokens = tokenize(str) - if len(tokens) == 0: raise Blank + if len(tokens) == 0: raise Blank("Blank Line") return read_form(Reader(tokens)) -- cgit v1.2.3 From 6301e0b6374cecc5599665be14d6ddc6a31ce1e8 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 23 Apr 2014 21:59:50 -0500 Subject: All: TCO let* and quasiquote. --- python/step5_tco.py | 4 +++- python/step6_file.py | 4 +++- python/step7_quote.py | 7 +++++-- python/step8_macros.py | 7 +++++-- python/step9_interop.py | 7 +++++-- python/stepA_more.py | 7 +++++-- 6 files changed, 26 insertions(+), 10 deletions(-) (limited to 'python') diff --git a/python/step5_tco.py b/python/step5_tco.py index 72457e9..cbb92c9 100644 --- a/python/step5_tco.py +++ b/python/step5_tco.py @@ -45,7 +45,9 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "do" == a0: eval_ast(ast[1:-1], env) ast = ast[-1] diff --git a/python/step6_file.py b/python/step6_file.py index 764eb53..9d84d1f 100644 --- a/python/step6_file.py +++ b/python/step6_file.py @@ -45,7 +45,9 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "do" == a0: eval_ast(ast[1:-1], env) ast = ast[-1] diff --git a/python/step7_quote.py b/python/step7_quote.py index 1002613..de19c6d 100644 --- a/python/step7_quote.py +++ b/python/step7_quote.py @@ -63,11 +63,14 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "quote" == a0: return ast[1] elif "quasiquote" == a0: - return EVAL(quasiquote(ast[1]), env) + ast = quasiquote(ast[1]); + # Continue loop (TCO) elif "do" == a0: eval_ast(ast[1:-1], env) ast = ast[-1] diff --git a/python/step8_macros.py b/python/step8_macros.py index 28b68fb..80ca239 100644 --- a/python/step8_macros.py +++ b/python/step8_macros.py @@ -77,11 +77,14 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "quote" == a0: return ast[1] elif "quasiquote" == a0: - return EVAL(quasiquote(ast[1]), env) + ast = quasiquote(ast[1]); + # Continue loop (TCO) elif 'defmacro!' == a0: func = EVAL(ast[2], env) func._ismacro_ = True diff --git a/python/step9_interop.py b/python/step9_interop.py index 2f7c5e0..7cacf1f 100644 --- a/python/step9_interop.py +++ b/python/step9_interop.py @@ -77,11 +77,14 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "quote" == a0: return ast[1] elif "quasiquote" == a0: - return EVAL(quasiquote(ast[1]), env) + ast = quasiquote(ast[1]); + # Continue loop (TCO) elif 'defmacro!' == a0: func = EVAL(ast[2], env) func._ismacro_ = True diff --git a/python/stepA_more.py b/python/stepA_more.py index b9d8152..723f0ed 100644 --- a/python/stepA_more.py +++ b/python/stepA_more.py @@ -77,11 +77,14 @@ def EVAL(ast, env): let_env = Env(env) for i in range(0, len(a1), 2): let_env.set(a1[i], EVAL(a1[i+1], let_env)) - return EVAL(a2, let_env) + ast = a2 + env = let_env + # Continue loop (TCO) elif "quote" == a0: return ast[1] elif "quasiquote" == a0: - return EVAL(quasiquote(ast[1]), env) + ast = quasiquote(ast[1]); + # Continue loop (TCO) elif 'defmacro!' == a0: func = EVAL(ast[2], env) func._ismacro_ = True -- cgit v1.2.3