1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00

RPython: step4 basics.

This commit is contained in:
Joel Martin 2015-06-08 21:58:37 -05:00
parent e6cfacb4fa
commit b0a9121df8
7 changed files with 454 additions and 57 deletions

View File

@ -13,6 +13,7 @@ all: $(STEPS)
step1_read_print: mal_types.py reader.py printer.py
step2_eval: mal_types.py reader.py printer.py
step3_env: mal_types.py reader.py printer.py env.py
step4_if_fn_do: mal_types.py reader.py printer.py env.py core.py
clean:
rm -f $(STEPS) *.pyc

263
rpython/core.py Normal file
View File

@ -0,0 +1,263 @@
import copy, time
from itertools import chain
import mal_types as types
from mal_types import (MalType, nil, true, false,
MalInt, MalStr, MalList)
import mal_readline
import reader
import printer
# General functions
def do_equal(args):
if types._equal_Q(args[0], args[1]): return true
else: return false
## Errors/Exceptions
#def throw(exc): raise Exception(exc)
#
# String functions
def pr_str(args):
parts = []
for exp in args.values: parts.append(printer._pr_str(exp, True))
return MalStr(u" ".join(parts))
def do_str(args):
parts = []
for exp in args.values: parts.append(printer._pr_str(exp, False))
return MalStr(u"".join(parts))
def prn(args):
parts = []
for exp in args.values: parts.append(printer._pr_str(exp, True))
print(u" ".join(parts))
return nil
def println(args):
parts = []
for exp in args.values: parts.append(printer._pr_str(exp, False))
print(u" ".join(parts))
return nil
# Number functions
def lt(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
if a.value < b.value: return true
else: return false
def lte(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
if a.value <= b.value: return true
else: return false
def gt(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
if a.value > b.value: return true
else: return false
def gte(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
if a.value >= b.value: return true
else: return false
def plus(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
return MalInt(a.value+b.value)
def minus(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
return MalInt(a.value-b.value)
def multiply(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
return MalInt(a.value*b.value)
def divide(args):
a, b = args[0], args[1]
assert isinstance(a, MalInt)
assert isinstance(b, MalInt)
return MalInt(int(a.value/b.value))
## Hash map functions
#def assoc(src_hm, *key_vals):
# hm = copy.copy(src_hm)
# for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
# return hm
#
#def dissoc(src_hm, *keys):
# hm = copy.copy(src_hm)
# for key in keys:
# if key in hm: del hm[key]
# return hm
#
#def get(hm, key):
# if hm and key in hm:
# return hm[key]
# else:
# return None
#
#def contains_Q(hm, key): return key in hm
#
#def keys(hm): return types._list(*hm.keys())
#
#def vals(hm): return types._list(*hm.values())
#
# Sequence functions
def do_list(ml):
assert isinstance(ml, MalList)
return ml
def list_Q(args):
if isinstance(args[0], MalList): return true
else: return false
def empty_Q(args):
assert isinstance(args, MalType)
seq = args[0]
if isinstance(seq, MalList):
if len(seq) == 0: return true
else: return false
elif seq is nil:
return true
else:
raise Exception("empty? called on non-sequence")
def count(args):
assert isinstance(args, MalType)
seq = args[0]
if isinstance(seq, MalList):
return MalInt(len(seq))
elif seq is nil:
return MalInt(0)
else:
raise Exception("count called on non-sequence")
#def coll_Q(coll): return sequential_Q(coll) or hash_map_Q(coll)
#
#def cons(x, seq): return List([x]) + List(seq)
#
#def concat(*lsts): return List(chain(*lsts))
#
#def nth(lst, idx):
# if idx < len(lst): return lst[idx]
# else: throw("nth: index out of range")
#
#def first(lst): return lst[0]
#
#def rest(lst): return List(lst[1:])
#
#def empty_Q(lst): return len(lst) == 0
#
#def count(lst):
# if types._nil_Q(lst): return 0
# else: return len(lst)
#
## retains metadata
#def conj(lst, *args):
# if types._list_Q(lst):
# new_lst = List(list(reversed(list(args))) + lst)
# else:
# new_lst = Vector(lst + list(args))
# if hasattr(lst, "__meta__"):
# new_lst.__meta__ = lst.__meta__
# return new_lst
#
#def apply(f, *args): return f(*(list(args[0:-1])+args[-1]))
#
#def mapf(f, lst): return List(map(f, lst))
#
#
## Metadata functions
#def with_meta(obj, meta):
# new_obj = types._clone(obj)
# new_obj.__meta__ = meta
# return new_obj
#
#def meta(obj):
# if hasattr(obj, "__meta__"): return obj.__meta__
# else: return None
#
#
## Atoms functions
#def deref(atm): return atm.val
#def reset_BANG(atm,val):
# atm.val = val
# return atm.val
#def swap_BANG(atm,f,*args):
# atm.val = f(atm.val,*args)
# return atm.val
ns = {
'=': do_equal,
# 'throw': throw,
# 'nil?': types._nil_Q,
# 'true?': types._true_Q,
# 'false?': types._false_Q,
# 'symbol': types._symbol,
# 'symbol?': types._symbol_Q,
# 'keyword': types._keyword,
# 'keyword?': types._keyword_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(),
'<': lt,
'<=': lte,
'>': gt,
'>=': gte,
'+': plus,
'-': minus,
'*': multiply,
'/': divide,
# 'time-ms': lambda : int(time.time() * 1000),
#
'list': do_list,
'list?': list_Q,
# 'vector': types._vector,
# 'vector?': types._vector_Q,
# 'hash-map': types._hash_map,
# 'map?': types._hash_map_Q,
# 'assoc': assoc,
# 'dissoc': dissoc,
# 'get': get,
# 'contains?': contains_Q,
# 'keys': keys,
# 'vals': vals,
#
# 'sequential?': types._sequential_Q,
# 'cons': cons,
# 'concat': concat,
# 'nth': nth,
# 'first': first,
# 'rest': rest,
'empty?': empty_Q,
'count': count,
# 'conj': conj,
# 'apply': apply,
# 'map': mapf,
#
# 'with-meta': with_meta,
# 'meta': meta,
# 'atom': types._atom,
# 'atom?': types._atom_Q,
# 'deref': deref,
# 'reset!': reset_BANG,
# 'swap!': swap_BANG
}

View File

@ -1,4 +1,4 @@
from mal_types import MalType, MalSym
from mal_types import MalType, MalSym, MalList
# Environment
class Env():
@ -7,12 +7,17 @@ class Env():
self.outer = outer or None
if binds:
assert isinstance(binds, MalList) and isinstance(exprs, MalList)
for i in range(len(binds)):
if binds[i] == "&":
self.data[binds[i+1]] = exprs[i:]
bind = binds[i]
assert isinstance(bind, MalSym)
if bind.value == u"&":
bind = binds[i+1]
assert isinstance(bind, MalSym)
self.data[bind.value] = exprs.slice(i)
break
else:
self.data[binds[i]] = exprs[i]
self.data[bind.value] = exprs[i]
def find(self, key):
assert isinstance(key, MalSym)

View File

@ -1,33 +1,27 @@
import sys, copy, types as pytypes
### python 3.0 differences
##if sys.hexversion > 0x3000000:
## def u(x):
## return x
##else:
## import codecs
## def u(x):
## return codecs.unicode_escape_decode(x)[0]
##
##
##if sys.version_info[0] >= 3:
## str_types = [str]
##else:
## str_types = [str, unicode]
##
### General functions
##
##def _equal_Q(a, b):
## ota, otb = type(a), type(b)
## if not (ota == otb or (_sequential_Q(a) and _sequential_Q(b))):
## return False;
## if _symbol_Q(a):
## return a == b
# General functions
def _equal_Q(a, b):
assert isinstance(a, MalType) and isinstance(b, MalType)
ota, otb = a.__class__, b.__class__
if not (ota is otb or (_sequential_Q(a) and _sequential_Q(b))):
return False
if _symbol_Q(a):
assert isinstance(a, MalSym) and isinstance(b, MalSym)
return a.value == b.value
elif _string_Q(a):
assert isinstance(a, MalStr) and isinstance(b, MalStr)
return a.value == b.value
elif _int_Q(a):
assert isinstance(a, MalInt) and isinstance(b, MalInt)
return a.value == b.value
## elif _list_Q(a) or _vector_Q(a):
## if len(a) != len(b): return False
## for i in range(len(a)):
## if not _equal_Q(a[i], b[i]): return False
## return True
elif _list_Q(a):
if len(a) != len(b): return False
for i in range(len(a)):
if not _equal_Q(a[i], b[i]): return False
return True
## elif _hash_map_Q(a):
## akeys = a.keys()
## akeys.sort()
@ -38,11 +32,16 @@ import sys, copy, types as pytypes
## if akeys[i] != bkeys[i]: return False
## if not equal_Q(a[akeys[i]], b[bkeys[i]]): return False
## return True
## else:
## return a == b
##
elif a is b:
#elif ((a is nil and a is nil) or (a is true and b is true) or (a
# is false and b is false)):
return True
else:
raise Exception("no = op defined for %s" % a.__class__.__name__)
##def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)
##
def _sequential_Q(seq): return _list_Q(seq)
##def _clone(obj):
## #if type(obj) == type(lambda x:x):
## if type(obj) == pytypes.FunctionType:
@ -145,23 +144,6 @@ def _keywordu(strn):
def _keyword_Q(exp):
return _string_Q(exp) and exp.value[0] == u"\u029e"
# Functions
class MalFunc(MalType):
def __init__(self, fn):
self.fn = fn
def apply(self, args):
return self.fn(args)
##def _function(Eval, Env, ast, env, params):
## 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(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalFunc
# lists
class MalList(MalType):
def __init__(self, vals):
@ -169,21 +151,23 @@ class MalList(MalType):
self.values = vals
def append(self, val):
self.values.append(val)
def rest(self, val):
def rest(self):
return MalList(self.values[1:])
def __len__(self):
return len(self.values)
def __getitem__(self, i):
assert isinstance(i, int)
return self.values[i]
def slice(self, start, end=None):
if end is None: end = len(self.values)
return MalList(self.values[start:end])
## def __add__(self, rhs): return List(list.__add__(self, rhs))
## def __getitem__(self, i):
## if type(i) == slice: return List(list.__getitem__(self, i))
## elif i >= len(self): return None
## else: return list.__getitem__(self, i)
## def __getslice__(self, *a): return List(list.__getslice__(self, *a))
def _list(*vals): return MalList(list(vals))
def _listl(l): return MalList(l.values)
#def _list_Q(exp): return exp.__class__ == MalList
def _list_Q(exp):
assert isinstance(exp, MalType)
@ -208,6 +192,33 @@ def _list_Q(exp):
## for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
## return hm
##def _hash_map_Q(exp): return type(exp) == Hash_Map
# Functions
# env import must happen after MalSym and MalList definitions to allow
# circular dependency
from env import Env
class MalFunc(MalType):
def __init__(self, fn, ast=None, env=None, params=None,
EvalFunc=None):
if fn is None and EvalFunc is None:
raise Exception("MalFunc requires either fn or EvalFunc")
self.fn = fn
#assert isinstance(ast, MalType) or ast is None
self.ast = ast
self.env = env
self.params = params
self.EvalFunc = EvalFunc
def apply(self, args):
if self.EvalFunc:
return self.EvalFunc(self.ast, self.gen_env(args))
else:
return self.fn(args)
def gen_env(self, args):
return Env(self.env, self.params, args)
def _function_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalFunc
##
### atoms
##class Atom(object):

View File

@ -33,7 +33,7 @@ def _pr_str(obj, print_readably=True):
elif print_readably:
return u'"' + types._replace(u'\\n', u'\\n',
types._replace(u'\"', u'\\"',
types._replace(u'\\\\', u'\\\\', val))) + u'"'
types._replace(u'\\', u'\\\\', val))) + u'"'
else:
return val
elif types._nil_Q(obj):
@ -50,6 +50,8 @@ def _pr_str(obj, print_readably=True):
elif types._int_Q(obj):
assert isinstance(obj, MalInt)
return unicode(str(obj.value))
elif types._function_Q(obj):
return u"#<function>"
else:
return u"unknown"

View File

@ -95,7 +95,6 @@ repl_env.set(_symbol(u'*'), MalFunc(multiply))
repl_env.set(_symbol(u'/'), MalFunc(divide))
def entry_point(argv):
#mal_readline.init()
while True:
try:
line = mal_readline.readline("user> ")

116
rpython/step4_if_fn_do.py Normal file
View File

@ -0,0 +1,116 @@
import sys, traceback
import mal_readline
import mal_types as types
from mal_types import (MalSym, MalInt, MalStr, nil, true, false,
_symbol, _keywordu, MalList, _list, MalFunc)
import reader, printer
from env import Env
import core
# read
def READ(str):
return reader.read_str(str)
# eval
def eval_ast(ast, env):
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):
## 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, env):
#print("EVAL %s" % printer._pr_str(ast))
if not types._list_Q(ast):
return eval_ast(ast, env)
# 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))
return EVAL(a2, let_env)
elif u"do" == a0sym:
el = eval_ast(ast.rest(), env)
return el.values[-1]
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: return EVAL(ast[3], env)
else: return nil
else:
return EVAL(a2, env)
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):
return f.apply(el.rest())
else:
raise Exception("%s is not callable" % f)
# print
def PRINT(exp):
return printer._pr_str(exp)
# repl
def entry_point(argv):
repl_env = Env()
def REP(str, env):
return PRINT(EVAL(READ(str), env))
# core.py: defined using python
for k, v in core.ns.items():
repl_env.set(_symbol(unicode(k)), MalFunc(v))
# core.mal: defined using the language itself
REP("(def! not (fn* (a) (if a false true)))", repl_env)
while True:
try:
line = mal_readline.readline("user> ")
if line == "": continue
print(REP(line, repl_env))
except EOFError as e:
break
except Exception as e:
print(e)
#print("".join(traceback.format_exception(*sys.exc_info())))
return 0
# _____ Define and setup target ___
def target(*args):
return entry_point
# Just run entry_point if not RPython compilation
import sys
if not sys.argv[0].endswith('rpython'):
entry_point(sys.argv)