1
1
mirror of https://github.com/kanaka/mal.git synced 2024-10-27 14:52:16 +03:00
mal/impls/python/step4_if_fn_do.py
Nicolas Boulenguez 37f62250a4 python: various simplifications
Use raw or multiline string litterals to make some hard-coded strings
more readable.

Replace some lambda constructs with more idiomatic generator
expressions, removing several temporary sequences (conversions,
contatenations, slices, arguments).

Replace definitions with assignments when possible.

Replace most python2/3 wrappers with code compatible with both
versions.  Use a non-printable ASCII characters instead of Unicode
characters (in order to avoid unicode in python2).

core:
Close file open by 'slurp' after reading.
coll_Q: remove as obsolete.
conj: do not preserve metadata.

env:
Replace find with an optional argument to get.
Search in outer environments with a loop instead of a recursion.
(python is not intended for large recursions).

readline:
Copy the example from the docs.
Write the history once.

mal_types:
clone: copy.copy() everything except callables.
functions: construct fn* functions in steps, where they are used
  (there was no encapsulation anyway)
Derive List and Vector from tuple, which is more efficient than list
for immutable structures.  Remove the need for __getitem__ stuff.
maps: provide an asPairs iterator.

printer:
Introduce a pr_list helper like many other implementations.

steps:
Remove obviously backported stuff from first steps.
Port forward the limitation of the exception backtrace size from step5
to later steps, so that REGRESS=1 does not crash stepA tests.
Fix the detection of special forms (strings were accepted as well as
symbols).
Let try* benefit from TCO.
2024-08-08 10:10:13 -05:00

94 lines
2.5 KiB
Python

import sys, traceback
import mal_readline
import mal_types as types
import reader, printer
from env import Env
import core
# read
READ = reader.read_str
# eval
def EVAL(ast, env):
dbgeval = env.get(types._symbol('DEBUG-EVAL'), return_nil=True)
if dbgeval is not None and dbgeval is not False:
print('EVAL: ' + printer._pr_str(ast))
if types._symbol_Q(ast):
return env.get(ast)
elif types._vector_Q(ast):
return types.Vector(EVAL(a, env) for a in ast)
elif types._hash_map_Q(ast):
return types.Hash_Map((k, EVAL(v, env)) for k, v in ast.items())
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 types._symbol_Q(a0):
if "def!" == a0:
a1, a2 = ast[1], ast[2]
res = EVAL(a2, env)
return env.set(a1, res)
elif "let*" == a0:
a1, a2 = ast[1], ast[2]
let_env = Env(env)
for k, v in types.asPairs(a1):
let_env.set(k, EVAL(v, let_env))
return EVAL(a2, let_env)
elif "do" == a0:
for i in range(1, len(ast)-1):
EVAL(ast[i], env)
return EVAL(ast[-1], env)
elif "if" == a0:
a1, a2 = ast[1], ast[2]
cond = EVAL(a1, env)
if cond is None or cond is False:
if len(ast) > 3:
return EVAL(ast[3], env)
else:
return None
else:
return EVAL(a2, env)
elif "fn*" == a0:
a1, a2 = ast[1], ast[2]
def fn(*args):
return EVAL(a2, Env(env, a1, args))
return fn
f = EVAL(a0, env)
if types._function_Q(f):
args = ast[1:]
return f(*(EVAL(a, env) for a in args))
else:
raise Exception('Can only apply functions')
# print
PRINT = printer._pr_str
# 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)
# 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> ")
print(REP(line))
except EOFError:
print()
break
except reader.Blank: continue
except Exception:
print("".join(traceback.format_exception(*sys.exc_info())))