From 8deb5b65912f340e82ec7006c1ff0610a5ed3eb8 Mon Sep 17 00:00:00 2001 From: Nicolas Boulenguez Date: Sun, 13 Oct 2024 20:17:55 +0200 Subject: [PATCH] rpython: complete the eval_ast merge, add DEBUG-EVAL Commit 03389277 had only updated stepA. --- impls/rpython/env.py | 6 ++++ impls/rpython/step2_eval.py | 22 ++++++------ impls/rpython/step3_env.py | 24 ++++++------- impls/rpython/step4_if_fn_do.py | 24 ++++++------- impls/rpython/step5_tco.py | 26 +++++++------- impls/rpython/step6_file.py | 26 +++++++------- impls/rpython/step7_quote.py | 28 +++++++-------- impls/rpython/step8_macros.py | 60 +++++++++++---------------------- impls/rpython/step9_try.py | 60 +++++++++++---------------------- impls/rpython/stepA_mal.py | 33 ++++++++++-------- 10 files changed, 138 insertions(+), 171 deletions(-) diff --git a/impls/rpython/env.py b/impls/rpython/env.py index 258874623f..8eecce359a 100644 --- a/impls/rpython/env.py +++ b/impls/rpython/env.py @@ -38,3 +38,9 @@ def get(self, key): env = self.find(key) if not env: throw_str("'" + str(key.value) + "' not found") return env.data[key.value] + + def get_or_None(self, key): + assert isinstance(key, MalSym) + env = self.find(key) + if not env: return None + return env.data[key.value] diff --git a/impls/rpython/step2_eval.py b/impls/rpython/step2_eval.py index 82d71c882d..58398c60f8 100644 --- a/impls/rpython/step2_eval.py +++ b/impls/rpython/step2_eval.py @@ -12,17 +12,20 @@ def READ(str): # eval def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + # print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) if ast.value in env: return env[ast.value] else: raise Exception(u"'" + ast.value + u"' not found") - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -33,14 +36,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast el = eval_ast(ast, env) diff --git a/impls/rpython/step3_env.py b/impls/rpython/step3_env.py index f196dfcecc..525e6a7ff8 100644 --- a/impls/rpython/step3_env.py +++ b/impls/rpython/step3_env.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, _symbol, _keywordu, + nil, false, throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -13,14 +14,18 @@ def READ(str): # eval def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -31,14 +36,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast a0 = ast[0] diff --git a/impls/rpython/step4_if_fn_do.py b/impls/rpython/step4_if_fn_do.py index 1ce49692c9..e738c4a0f3 100644 --- a/impls/rpython/step4_if_fn_do.py +++ b/impls/rpython/step4_if_fn_do.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -14,14 +15,18 @@ def READ(str): # eval def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -32,14 +37,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast a0 = ast[0] diff --git a/impls/rpython/step5_tco.py b/impls/rpython/step5_tco.py index 8d24555c87..68c38382bc 100644 --- a/impls/rpython/step5_tco.py +++ b/impls/rpython/step5_tco.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -14,14 +15,19 @@ def READ(str): # eval def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + while True: + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -32,15 +38,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - while True: - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast a0 = ast[0] diff --git a/impls/rpython/step6_file.py b/impls/rpython/step6_file.py index 474392761f..9435a92eae 100644 --- a/impls/rpython/step6_file.py +++ b/impls/rpython/step6_file.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -14,14 +15,19 @@ def READ(str): # eval def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + while True: + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -32,15 +38,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - while True: - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast a0 = ast[0] diff --git a/impls/rpython/step7_quote.py b/impls/rpython/step7_quote.py index cb8c063ab7..87d9cbad0e 100644 --- a/impls/rpython/step7_quote.py +++ b/impls/rpython/step7_quote.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -41,14 +42,19 @@ def quasiquote(ast): return ast def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + while True: + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -59,15 +65,9 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -def EVAL(ast, env): - while True: - #print("EVAL %s" % printer._pr_str(ast)) - if not types._list_Q(ast): - return eval_ast(ast, env) - + else: # apply list if len(ast) == 0: return ast a0 = ast[0] @@ -89,8 +89,6 @@ def EVAL(ast, env): env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] - elif u"quasiquoteexpand" == a0sym: - return quasiquote(ast[1]) elif u"quasiquote" == a0sym: ast = quasiquote(ast[1]) # Continue loop (TCO) elif u"do" == a0sym: diff --git a/impls/rpython/step8_macros.py b/impls/rpython/step8_macros.py index 8aff32a945..eaecd35ec9 100644 --- a/impls/rpython/step8_macros.py +++ b/impls/rpython/step8_macros.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -40,30 +41,20 @@ def quasiquote(ast): else: return ast -def is_macro_call(ast, env): - if types._list_Q(ast): - a0 = ast[0] - if isinstance(a0, MalSym): - if not env.find(a0) is None: - return env.get(a0).ismacro - return False - -def macroexpand(ast, env): - while is_macro_call(ast, env): - assert isinstance(ast[0], MalSym) - mac = env.get(ast[0]) - ast = macroexpand(mac.apply(ast.rest()), env) - return ast - def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + while True: + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -74,20 +65,10 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -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 - + else: # 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): @@ -108,16 +89,12 @@ def EVAL(ast, env): env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] - elif u"quasiquoteexpand" == a0sym: - return quasiquote(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"do" == a0sym: if len(ast) == 0: return nil @@ -136,14 +113,17 @@ def EVAL(ast, env): a1, a2 = ast[1], ast[2] return MalFunc(None, a2, env, a1, EVAL) else: - el = eval_ast(ast, env) - f = el.values[0] + f = EVAL(a0, env) + if f.ismacro: + ast = f.apply(ast.rest()) # Continue loop (TCO) + continue + args = eval_ast(ast.rest(), env) if isinstance(f, MalFunc): if f.ast: ast = f.ast - env = f.gen_env(el.rest()) # Continue loop (TCO) + env = f.gen_env(args) # Continue loop (TCO) else: - return f.apply(el.rest()) + return f.apply(args) else: raise Exception("%s is not callable" % f) diff --git a/impls/rpython/step9_try.py b/impls/rpython/step9_try.py index 40989a7aab..f98074dfbb 100644 --- a/impls/rpython/step9_try.py +++ b/impls/rpython/step9_try.py @@ -3,6 +3,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -40,30 +41,20 @@ def quasiquote(ast): else: return ast -def is_macro_call(ast, env): - if types._list_Q(ast): - a0 = ast[0] - if isinstance(a0, MalSym): - if not env.find(a0) is None: - return env.get(a0).ismacro - return False - -def macroexpand(ast, env): - while is_macro_call(ast, env): - assert isinstance(ast[0], MalSym) - mac = env.get(ast[0]) - ast = macroexpand(mac.apply(ast.rest()), env) - return ast - def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + +def EVAL(ast, env): + while True: + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) - elif types._list_Q(ast): - res = [] - for a in ast.values: - res.append(EVAL(a, env)) - return MalList(res) elif types._vector_Q(ast): res = [] for a in ast.values: @@ -74,20 +65,10 @@ def eval_ast(ast, env): for k in ast.dct.keys(): new_dct[k] = EVAL(ast.dct[k], env) return MalHashMap(new_dct) - else: + elif not types._list_Q(ast): return ast # primitive value, return unchanged - -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 - + else: # 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): @@ -108,16 +89,12 @@ def EVAL(ast, env): env = let_env # Continue loop (TCO) elif u"quote" == a0sym: return ast[1] - elif u"quasiquoteexpand" == a0sym: - return quasiquote(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); @@ -154,14 +131,17 @@ def EVAL(ast, env): a1, a2 = ast[1], ast[2] return MalFunc(None, a2, env, a1, EVAL) else: - el = eval_ast(ast, env) - f = el.values[0] + f = EVAL(a0, env) + if f.ismacro: + ast = f.apply(ast.rest()) # Continue loop (TCO) + continue + args = eval_ast(ast.rest(), env) if isinstance(f, MalFunc): if f.ast: ast = f.ast - env = f.gen_env(el.rest()) # Continue loop (TCO) + env = f.gen_env(args) # Continue loop (TCO) else: - return f.apply(el.rest()) + return f.apply(args) else: raise Exception("%s is not callable" % f) diff --git a/impls/rpython/stepA_mal.py b/impls/rpython/stepA_mal.py index 9592851066..624a9b0e7b 100644 --- a/impls/rpython/stepA_mal.py +++ b/impls/rpython/stepA_mal.py @@ -12,6 +12,7 @@ import mal_types as types from mal_types import (MalSym, MalInt, MalStr, nil, true, false, _symbol, _keywordu, + throw_str, MalList, _list, MalVector, MalHashMap, MalFunc) import reader, printer from env import Env @@ -49,9 +50,17 @@ def quasiquote(ast): else: return ast +def eval_ast(ast, env): + assert isinstance(ast, MalList) + res = [] + for a in ast.values: + res.append(EVAL(a, env)) + return MalList(res) + def EVAL(ast, env): while True: - #print("EVAL %s" % printer._pr_str(ast)) + if env.get_or_None(MalSym(u"DEBUG-EVAL")) not in (None, nil, false): + print(u"EVAL: " + printer._pr_str(ast)) if types._symbol_Q(ast): assert isinstance(ast, MalSym) return env.get(ast) @@ -116,8 +125,8 @@ def EVAL(ast, env): elif u"do" == a0sym: if len(ast) == 0: return nil - for i in range(1, len(ast) - 1): - EVAL(ast[i], env) + 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] @@ -131,21 +140,17 @@ def EVAL(ast, env): 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) + f = EVAL(a0, env) + if f.ismacro: + ast = f.apply(ast.rest()) # Continue loop (TCO) + continue + args = eval_ast(ast.rest(), env) if isinstance(f, MalFunc): if f.ast: ast = f.ast - env = f.gen_env(el) # Continue loop (TCO) + env = f.gen_env(args) # Continue loop (TCO) else: - return f.apply(el) + return f.apply(args) else: raise Exception("%s is not callable" % f)