Skip to content

Instantly share code, notes, and snippets.

@yatsuta
Created December 29, 2013 11:17
Show Gist options
  • Save yatsuta/8169400 to your computer and use it in GitHub Desktop.
Save yatsuta/8169400 to your computer and use it in GitHub Desktop.
TinyR
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.')
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> ')
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)
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