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:
parent
e6cfacb4fa
commit
b0a9121df8
@ -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
263
rpython/core.py
Normal 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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
116
rpython/step4_if_fn_do.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user