Created
December 29, 2013 11:17
-
-
Save yatsuta/8169400 to your computer and use it in GitHub Desktop.
TinyR
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from util import * | |
#---------------------------------------------------------------------- | |
# typeof | |
#---------------------------------------------------------------------- | |
def typeof(obj): | |
if (is_call(obj)): return('call') | |
elif (type(obj) is dict): return(obj['type']) | |
else: | |
raise RmodokiTypeError('%s is not a object.' % obj) | |
#---------------------------------------------------------------------- | |
# call | |
#---------------------------------------------------------------------- | |
def is_call(obj): | |
return(type(obj) is list) | |
#---------------------------------------------------------------------- | |
# singleton objects (null, empty_env, unbound_val) | |
#---------------------------------------------------------------------- | |
null = {'type':'null'} | |
def is_null(obj): return(typeof(obj) == 'null') | |
empty_env = {'type':'empty_env'} | |
def is_empty_env(obj): return(typeof(obj) == 'empty_env') | |
unbound_val = {'type':'unbound_val'} | |
def is_unbound_val(obj): return(typeof(obj) == 'unbound_val') | |
#---------------------------------------------------------------------- | |
# symbol | |
#---------------------------------------------------------------------- | |
def new_symbol(val): return({'type':'symbol', 'val':val}) | |
def symbol_val(symbol): return(symbol['val']) | |
def is_symbol(obj): return(typeof(obj) == 'symbol') | |
#---------------------------------------------------------------------- | |
# num | |
#---------------------------------------------------------------------- | |
def new_num(val): return({'type':'num', 'val':val}) | |
def num_val(num): return(num['val']) | |
def is_num(obj): return(typeof(obj) == 'num') | |
#---------------------------------------------------------------------- | |
# string | |
#---------------------------------------------------------------------- | |
def new_string(val): return({'type':'string', 'val':val}) | |
def string_val(string): return(string['val']) | |
def is_string(obj): return(typeof(obj) == 'string') | |
#---------------------------------------------------------------------- | |
# closure | |
#---------------------------------------------------------------------- | |
def new_closure(formals, body, env): | |
return({'type':'closure', | |
'formals':formals, | |
'body':body, | |
'env':env}) | |
def closure_formals(closure): return(closure['formals']) | |
def closure_body(closure): return(closure['body']) | |
def closure_env(closure): return(closure['env']) | |
def is_closure(obj): return(typeof(obj) == 'closure') | |
#---------------------------------------------------------------------- | |
# dclosure | |
#---------------------------------------------------------------------- | |
def new_dclosure(formals, body, env): | |
return({'type':'dclosure', | |
'formals':formals, | |
'body':body, | |
'env':env}) | |
def dclosure_formals(dclosure): return(dclosure['formals']) | |
def dclosure_body(dclosure): return(dclosure['body']) | |
def dclosure_env(dclosure): return(dclosure['env']) | |
def is_dclosure(obj): return(typeof(obj) == 'dclosure') | |
#---------------------------------------------------------------------- | |
# env | |
#---------------------------------------------------------------------- | |
def new_env(frame, parent): | |
return({'type':'env', 'frame':frame, 'parent':parent}) | |
def env_frame(env): return(env['frame']) | |
def env_parent(env): return(env['parent']) | |
def is_env(obj): return(typeof(obj) == 'env') | |
#---------------------------------------------------------------------- | |
# promise | |
#---------------------------------------------------------------------- | |
def new_promise(exp, env): | |
return({'type':'promise','exp':exp, 'val':unbound_val, 'env':env}) | |
def promise_exp(promise): return(promise['exp']) | |
def promise_val(promise): return(promise['val']) | |
def promise_env(promise): return(promise['env']) | |
def is_promise(obj): return(typeof(obj) == 'promise') | |
def set_promise_val(promise, val): promise['val'] = val | |
#---------------------------------------------------------------------- | |
# builtin | |
#---------------------------------------------------------------------- | |
def new_builtin(index): return({'type':'builtin', 'index':index}) | |
def builtin_index(builtin): return(builtin['index']) | |
def is_builtin(obj): return(typeof(obj) == 'builtin') | |
#---------------------------------------------------------------------- | |
# special | |
#---------------------------------------------------------------------- | |
def new_special(index): return({'type':'special', 'index':index}) | |
def special_index(special): return(special['index']) | |
def is_special(obj): return(typeof(obj) == 'special') | |
if __name__ == '__main__': | |
print('This file is not executable.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pyparsing import * | |
from obj import * | |
#---------------------------------------------------------------------- | |
# Error | |
#---------------------------------------------------------------------- | |
ParseError = ParseException | |
#---------------------------------------------------------------------- | |
# Keyword ('function', 'dfunction') | |
#---------------------------------------------------------------------- | |
function = ( | |
Keyword('function') | |
).setParseAction(lambda t: new_symbol('function')) | |
dfunction = ( | |
Keyword('dfunction') | |
).setParseAction(lambda t: new_symbol('dfunction')) | |
#---------------------------------------------------------------------- | |
# ident, string | |
#---------------------------------------------------------------------- | |
string_elem = Word( | |
' \t' + | |
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + | |
'!#$%&()*+,-./:;<=>?@[\\]^_{|}~' | |
) | |
ident = ( | |
Suppress('`') + string_elem + Suppress('`') | |
| | |
Word('_' + alphas, '._' + alphanums) | |
).setParseAction(lambda t: new_symbol(t[0])) | |
string = ( | |
Suppress("'") + string_elem + Suppress("'") | |
| | |
Suppress('"') + string_elem + Suppress('"') | |
).setParseAction(lambda t: new_string(t[0])) | |
#---------------------------------------------------------------------- | |
# num | |
#---------------------------------------------------------------------- | |
num = Combine( | |
Word(nums) + | |
Optional('.' + Word(nums)) | |
).setParseAction(lambda t: new_num(float(t[0]))) | |
#---------------------------------------------------------------------- | |
# expr (placeholder) | |
#---------------------------------------------------------------------- | |
expr = Forward() | |
#---------------------------------------------------------------------- | |
# assign | |
#---------------------------------------------------------------------- | |
assign = ( | |
ident + | |
(Literal('<-') | Literal('<<-')) + | |
expr | |
).setParseAction(lambda t: | |
[[new_symbol(t[1]), t[0], t[2]]]) | |
#---------------------------------------------------------------------- | |
# block | |
#---------------------------------------------------------------------- | |
exprs_delim = (LineEnd() | ';').setWhitespaceChars(' \t\r') | |
block_start = ( | |
Literal('{') | |
).setParseAction(lambda t: new_symbol('{')) | |
block = ( | |
block_start + | |
(delimitedList(expr, exprs_delim) + | |
Optional(Suppress(exprs_delim)) | | |
Empty()) + | |
Suppress('}') | |
).setParseAction(lambda t: [t.asList()]) | |
#---------------------------------------------------------------------- | |
# func_call, func_def | |
#---------------------------------------------------------------------- | |
func_call_post = ( | |
Suppress('(') + | |
(delimitedList(expr, ',') | Empty()) + | |
Suppress(')') | |
).setParseAction(lambda t: [t.asList()]) | |
formals = ( | |
Suppress('(') + | |
(delimitedList(ident, ',') | Empty()) + | |
Suppress(')') | |
).setParseAction(lambda t: [t.asList()]) | |
func_def = ( | |
(function | dfunction) + | |
formals + | |
expr | |
).setParseAction(lambda t: [t.asList()]) | |
#---------------------------------------------------------------------- | |
# operators and operands | |
#---------------------------------------------------------------------- | |
operand = assign | func_def | block | ident | string | num | |
minusop = Suppress('-') | |
multop = ( | |
oneOf('* /') | |
).setParseAction(lambda t: new_symbol(t[0])) | |
plusop = ( | |
oneOf('+ -') | |
).setParseAction(lambda t: new_symbol(t[0])) | |
#---------------------------------------------------------------------- | |
# folding func utilities | |
#---------------------------------------------------------------------- | |
def after_calc(t): | |
def take2(i): | |
while True: | |
try: yield (next(i), next(i)) | |
except StopIteration: break | |
def expr_fold(l): | |
i = iter(l) | |
acc = next(i) | |
for op, arg in take2(i): | |
acc = [op, acc, arg] | |
return acc | |
return [expr_fold(t[0])] | |
def after_func_call(t): | |
def expr_fold(l): | |
i = iter(l) | |
acc = [next(i)] + next(i) | |
for arg in i: | |
acc = [acc] + arg | |
return acc | |
return [expr_fold(t[0])] | |
#---------------------------------------------------------------------- | |
# expr (infixNotation) | |
#---------------------------------------------------------------------- | |
expr << infixNotation( | |
operand, | |
[(func_call_post, 1, opAssoc.LEFT, after_func_call), | |
(minusop, 1, opAssoc.RIGHT, lambda t: [[new_symbol('*'), | |
new_num(-1), | |
t[0][0]]]), | |
(multop, 2, opAssoc.LEFT, after_calc), | |
(plusop, 2, opAssoc.LEFT, after_calc) | |
] | |
) | |
#---------------------------------------------------------------------- | |
# parse (read) | |
#---------------------------------------------------------------------- | |
def parse(s): | |
return expr.parseString(s, parseAll=True)[0] | |
read = parse | |
#---------------------------------------------------------------------- | |
# rpl | |
#---------------------------------------------------------------------- | |
def rpl(prompt): | |
while True: | |
try: | |
exp = read(input(prompt)) | |
except ParseError as e: | |
print('ParseError: ' + str(e)) | |
continue | |
print(exp) | |
if __name__ == '__main__': | |
rpl('parser> ') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import division | |
from parser import read, ParseError | |
from util import * | |
from obj import * | |
def eval(exp, env): | |
global visible | |
visible = True | |
if (is_call(exp)): return(eval_call(exp, env)) | |
elif (is_num(exp) or is_string(exp)): return(exp) | |
elif (is_symbol(exp)): | |
val = lookup(exp, env) | |
if (is_promise(val)): | |
val = force(val) | |
return(val) | |
else: raise EvalError('%s is not a right expression.' % to_str(exp)) | |
def force(promise): | |
val = promise_val(promise) | |
if (is_unbound_val(val)): | |
val = eval(promise_exp(promise), promise_env(promise)) | |
set_promise_val(promise, val) | |
return(val) | |
def eval_call(exp, env): | |
op = car(exp) | |
args = cdr(exp) | |
op = eval(op, env) | |
if (is_closure(op)): | |
args = eval_args(args, env) | |
return(apply_closure(op, args)) | |
elif (is_dclosure(op)): | |
args = promise_args(args, env) | |
return(apply_dclosure(op, args)) | |
elif (is_builtin(op)): | |
args = eval_args(args, env) | |
return(apply_builtin(op, args, env)) | |
elif (is_special(op)): | |
return(apply_special(op, args, env)) | |
else: | |
raise EvalError('%s is not callable.' % to_str(op)) | |
def eval_args(args, env): | |
l = length(args) | |
evaled_args = [None] * l | |
for i in seq_len(l): | |
evaled_args[i] = eval(args[i], env) | |
return(evaled_args) | |
def promise_args(args, env): | |
l = length(args) | |
promised_args = [None] * l | |
for i in seq_len(l): | |
promised_args[i] = new_promise(args[i], env) | |
return(promised_args) | |
def lookup(symbol, env): | |
key = symbol_val(symbol) | |
while (not is_empty_env(env)): | |
frame = env_frame(env) | |
if (has_key(frame, key)): return(frame[key]) | |
env = env_parent(env) | |
raise EvalError('%s is undefined.' % key) | |
def store(symbol, val, env): | |
key = symbol_val(symbol) | |
frame = env_frame(env) | |
frame[key] = val | |
def apply_closure(closure, args): | |
formals = closure_formals(closure) | |
body = closure_body(closure) | |
parent_env = closure_env(closure) | |
extended_env = extend_env(formals, | |
args, | |
parent_env) | |
return(eval(body, extended_env)) | |
def apply_dclosure(dclosure, args): | |
formals = dclosure_formals(dclosure) | |
body = dclosure_body(dclosure) | |
parent_env = dclosure_env(dclosure) | |
extended_env = extend_env(formals, | |
args, | |
parent_env) | |
return(eval(body, extended_env)) | |
def extend_env(formals, args, parent_env): | |
l = length(formals) | |
if (l != length(args)): | |
raise EvalError('length of formals and args are different.') | |
frame = dict() | |
for i in seq_len(l): | |
key = symbol_val(formals[i]) | |
frame[key] = args[i] | |
return(new_env(frame, parent_env)) | |
def apply_builtin(builtin, args, env): | |
global visible | |
primitive = primitives[builtin_index(builtin)] | |
op = primitive_op(primitive) | |
func = primitive_code(primitive) | |
val = func(op, args, env) | |
p_visible = primitive_visible(primitive) | |
if (not is_na(p_visible)): visible = p_visible | |
return(val) | |
def apply_special(special, args, env): | |
global visible | |
primitive = primitives[special_index(special)] | |
op = primitive_op(primitive) | |
func = primitive_code(primitive) | |
val = func(op, args, env) | |
p_visible = primitive_visible(primitive) | |
if (not is_na(p_visible)): visible = p_visible | |
return(val) | |
def print_(obj): print(to_str(obj)) | |
def to_str(obj): | |
type = typeof(obj) | |
func = to_str_funcs[type] | |
return(func(obj)) | |
def to_str_list(l): | |
if (length(l) == 0): return('[]') | |
head = car(l) | |
tail = cdr(l) | |
result_str = '[' + to_str(head) | |
for obj in tail: | |
result_str = result_str + ', ' + to_str(obj) | |
result_str = result_str + ']' | |
return(result_str) | |
to_str_funcs = { | |
'num':lambda n: ('%s' % num_val(n)), | |
'string':lambda s: ("'" + string_val(s) + "'"), | |
'null':lambda n: ('NULL'), | |
'closure':lambda c: ('<closure %s>' % id(c)), | |
'builtin':lambda b: | |
('<builtin `%s`>' % primitive_op(primitives[builtin_index(b)])), | |
'special':lambda s: | |
('<special `%s`>' % primitive_op(primitives[special_index(s)])), | |
'symbol':lambda s: ('`%s`' % symbol_val(s)), | |
'call':to_str_list | |
} | |
to_str_funcs['dclosure'] = lambda s: ('<dclosure %s>' % id(d)) | |
def do_assign(op, args, env): | |
var = args[0] | |
unevaled_val = args[1] | |
if (not is_symbol(var)): | |
raise EvalError('%s is not a symbol.' % to_str(var)) | |
val = eval(unevaled_val, env) | |
store(var, val, env) | |
return(val) | |
def do_function(op, args, env): | |
formals = args[0] | |
body = args[1] | |
return(new_closure(formals, body, env)) | |
def do_block(op, args, env): | |
l = length(args) | |
if (l == 0): return(null) | |
for i in seq_len(l-1): | |
eval(args[i], env) | |
return(eval(args[l-1], env)) | |
def do_calc(op, args, env): | |
arg1 = args[0] | |
if (not is_num(arg1)): | |
raise EvalError('%s is not a num object.' % to_str(arg1)) | |
arg2 = args[1] | |
if (not is_num(arg2)): | |
raise EvalError('%s is not a num object.' % to_str(arg2)) | |
val1 = num_val(arg1) | |
val2 = num_val(arg2) | |
if (op == '+'): return(new_num(val1 + val2)) | |
elif (op == '-'): return(new_num(val1 - val2)) | |
elif (op == '*'): return(new_num(val1 * val2)) | |
elif (op == '/'): return(new_num(val1 / val2)) | |
else: | |
raise EvalError('%s is not a mathematical operator.' % op) | |
def do_ls(op, args, env): | |
l = length(args) | |
if (l > 1): raise EvalError('ls: too many args.') | |
elif (l == 1): | |
arg = args[0] | |
if (is_num(arg)): i = num_val(arg) | |
else: raise EvalError('ls: %s is not a num object' % to_str(arg)) | |
else: i = 0 | |
if (i < 0): raise EvalError('ls: negative arg.') | |
while (i > 0): | |
env = env_parent(env) | |
if (is_empty_env(env)): | |
raise EvalError('ls: reached to empty_env.') | |
i = i - 1 | |
print(keys(env_frame(env))) | |
return(null) | |
def do_quote(op, args, env): | |
if (length(args) != 1): | |
raise EvalError('quote: only 1 arg is required.') | |
return(args[0]) | |
def do_print(op, args, env): | |
if (length(args) != 1): | |
raise EvalError('print: only 1 arg is required.') | |
arg = args[0] | |
print_(arg) | |
return(arg) | |
def do_if_zero(op, args, env): | |
if (length(args) != 3): | |
raise EvalError('if_zero: 3 args are required.') | |
cond = args[0] | |
then_clause = args[1] | |
else_clause = args[2] | |
cond = eval(cond, env) | |
if (not is_num(cond)): | |
raise EvalError('if_zero: %s is not a number.' % to_str(cond)) | |
if (num_val(cond) == 0): | |
return(eval(then_clause, env)) | |
else: | |
return(eval(else_clause, env)) | |
def do_dfunction(op, args, env): | |
formals = args[0] | |
body = args[1] | |
return(new_dclosure(formals, body, env)) | |
def is_global_env(env): | |
if (is_empty_env(env)): return(False) | |
elif (is_empty_env(env_parent(env))): return(False) | |
elif (is_empty_env(env_parent( | |
env_parent(env)))): return(True) | |
else: return(False) | |
def do_deepassign(op, args, env): | |
var = args[0] | |
unevaled_val = args[1] | |
if (not is_symbol(var)): | |
raise EvalError('%s is not a symbol.' % to_str(var)) | |
key = symbol_val(var) | |
val = eval(unevaled_val, env) | |
while (not is_global_env(env)): | |
frame = env_frame(env) | |
if (has_key(frame, key)): break | |
env = env_parent(env) | |
store(var, val, env) | |
return(val) | |
primitives = [ | |
{'ptype':'special', 'op':'{', 'python_code':do_block, 'visible':True}, | |
{'ptype':'special', 'op':'function', 'python_code':do_function, 'visible':True}, | |
{'ptype':'special', 'op':'<-', 'python_code':do_assign, 'visible':False}, | |
{'ptype':'builtin', 'op':'+', 'python_code':do_calc, 'visible':True}, | |
{'ptype':'builtin', 'op':'-', 'python_code':do_calc, 'visible':True}, | |
{'ptype':'builtin', 'op':'*', 'python_code':do_calc, 'visible':True}, | |
{'ptype':'builtin', 'op':'/', 'python_code':do_calc, 'visible':True}, | |
{'ptype':'builtin', 'op':'ls', 'python_code':do_ls, 'visible':False}, | |
{'ptype':'special', 'op':'quote', 'python_code':do_quote, 'visible':True}, | |
{'ptype':'builtin', 'op':'print', 'python_code':do_print, 'visible':False} | |
] | |
def primitive_type(primitive): return(primitive['ptype']) | |
def primitive_op(primitive): return(primitive['op']) | |
def primitive_code(primitive): return(primitive['python_code']) | |
def primitive_visible(primitive): return(primitive['visible']) | |
def install_primitive(index, env): | |
primitive = primitives[index] | |
type = primitive_type(primitive) | |
op = primitive_op(primitive) | |
var = new_symbol(op) | |
if (type == 'builtin'): | |
store(var, new_builtin(index), env) | |
elif (type == 'special'): | |
store(var, new_special(index), env) | |
primitives = ( | |
primitives + | |
[{'ptype':'special', 'op':'if_zero', 'python_code':do_if_zero, 'visible':NA}] | |
) | |
primitives[0]['visible'] = NA | |
primitives = ( | |
primitives + | |
[{'ptype':'special', 'op':'dfunction', 'python_code':do_dfunction, 'visible':True}] | |
) | |
primitives = ( | |
primitives + | |
[{'ptype':'special', 'op':'<<-', 'python_code':do_deepassign, 'visible':False}] | |
) | |
visible = True | |
def repl(prompt): | |
base_env = new_env(dict(), empty_env) | |
global_env = new_env(dict(), base_env) | |
for i in seq_len(length(primitives)): | |
install_primitive(i, base_env) | |
store(new_symbol('pi'), new_num(3.14159265), base_env) | |
while True: | |
try: | |
exp = read(input(prompt)) | |
except ParseError as e: | |
print('ParseError: ' + str(e)) | |
continue | |
try: | |
val = eval(exp, global_env) | |
except EvalError as e: | |
print('EvalError: ' + str(e)) | |
continue | |
if (visible): print_(val) | |
if __name__ == '__main__': | |
import os.path as op | |
import sys | |
prompt = op.splitext(op.basename(sys.argv[0]))[0] + '> ' | |
repl(prompt) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
#---------------------------------------------------------------------- | |
# Error | |
#---------------------------------------------------------------------- | |
class EvalError(Exception): pass | |
class RmodokiTypeError(Exception): pass | |
#---------------------------------------------------------------------- | |
# Python to Rmodoki utils | |
#---------------------------------------------------------------------- | |
major = sys.version_info.major | |
minor = sys.version_info.minor | |
if (major == 2 and minor >= 6): | |
input = raw_input | |
elif (major < 2 or (major == 2 and minor < 6)): | |
sys.stderr.write('version 2.6 or later needed.\n') | |
sys.exit(1) | |
length = len | |
seq_len = range | |
NA = None | |
def is_na(obj): return(obj is None) | |
def car(l): return(l[0]) | |
def cdr(l): return(l[1:]) | |
def keys(dic): return(dic.keys()) | |
def has_key(dic, key): return(key in dic) | |
if __name__ == '__main__': | |
print('This file is not executable.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment