plyでMiniLispを実装
原点に戻る。plyでminilispを実装します。
紹介の意味も込めて某所にあったのをほぼそのまま載せます。
plyのシンプルさ(Lispのシンプルさ?)がわかってもらえるかと思います。
minilisp_lex.py
import ply.lex as lex tokens = ('QUOTE', 'SIMB', 'NUM', 'LPAREN', 'RPAREN', 'NIL', 'TRUE', 'FALSE', 'TEXT') reserved = { 'nil' : 'NIL', } t_LPAREN = r'\(' t_RPAREN = r'\)' t_QUOTE = r'\'' t_TRUE = r'\#t' t_FALSE = r'\#f' def t_NUM(t): r'\d+' try: t.value = int(t.value) except ValueError: print "Line %d: Number %s is too large!" % (t.lineno,t.value) t.value = 0 return t def t_SIMB(t): r'[a-zA-Z_+=\*\-][a-zA-Z0-9_+\*\-]*' t.type = reserved.get(t.value,'SIMB') return t def t_TEXT(t): r'\'[a-zA-Z0-9_+\*\- :,]*\'' t.type = reserved.get(t.value,'TEXT') return t def t_newline(t): r'\n+' t.lexer.lineno += len(t.value) t_ignore = ' \t' def t_error(t): print "Illegal character '%s'" % t.value[0] t.lexer.skip(1) lex.lex()
minilisp_yacc.py
import ply.yacc as yacc from minilisp_lex import tokens DEBUG = True name = {} def cons(l): return [l[0]] + l[1] name['cons'] = cons def concat(l): return l[0] + l[1] name['concat'] = concat def listar(l): return l name['list'] = listar def car(l): return l[0][0] name['car'] = car def cdr(l): return l[0][1:] name['cdr'] = cdr def eq(l): return l[0] == l[1] name['eq'] = eq name['='] = eq def _and(l): return not False in l name['and'] = _and def _or(l): return True in l name['or'] = _or def cond(l): if l[0]: return l[1] name['cond'] = cond def add(l): return sum(l) name['+'] = add def minus(l): '''Unary minus''' return -l[0] name['-'] = minus def _print(l): print lisp_str(l[0]) name['print'] = _print def lisp_eval(simb, items): if simb in name: return call(name[simb], eval_lists(items)) else: return [simb] + items def call(f, l): try: return f(eval_lists(l)) except TypeError: return f def eval_lists(l): r = [] for i in l: if is_list(i): if i: r.append(lisp_eval(i[0], i[1:])) else: r.append(i) else: r.append(i) return r def is_list(l): return type(l) == type([]) def lisp_str(l): if type(l) == type([]): if not l: return "()" r = "(" for i in l[:-1]: r += lisp_str(i) + " " r += lisp_str(l[-1]) + ")" return r elif l is True: return "#t" elif l is False: return "#f" elif l is None: return 'nil' else: return str(l) #BNF def p_exp_atom(p): 'exp : atom' p[0] = p[1] def p_exp_qlist(p): 'exp : quoted_list' p[0] = p[1] def p_exp_call(p): 'exp : call' p[0] = p[1] def p_quoted_list(p): 'quoted_list : QUOTE list' p[0] = p[2] def p_list(p): 'list : LPAREN items RPAREN' p[0] = p[2] def p_items(p): 'items : item items' p[0] = [p[1]] + p[2] def p_items_empty(p): 'items : empty' p[0] = [] def p_empty(p): 'empty :' def p_item_atom(p): 'item : atom' p[0] = p[1] def p_item_list(p): 'item : list' p[0] = p[1] def p_item_list(p): 'item : quoted_list' p[0] = p[1] def p_item_call(p): 'item : call' p[0] = p[1] def p_item_empty(p): 'item : empty' p[0] = p[1] def p_call(p): 'call : LPAREN SIMB items RPAREN' if DEBUG: print "Calling", p[2], "with", p[3] p[0] = lisp_eval(p[2], p[3]) def p_atom_simbol(p): 'atom : SIMB' p[0] = p[1] def p_atom_bool(p): 'atom : bool' p[0] = p[1] def p_atom_num(p): 'atom : NUM' p[0] = p[1] def p_atom_word(p): 'atom : TEXT' p[0] = p[1] def p_atom_empty(p): 'atom :' pass def p_true(p): 'bool : TRUE' p[0] = True def p_false(p): 'bool : FALSE' p[0] = False def p_nil(p): 'atom : NIL' p[0] = None def p_error(p): print "Syntax error!! ",p yacc.yacc()
minilisp.py
from minilisp_yacc import yacc, lisp_str import cmd class MiniLisp(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.prompt = "ml> " def do_exit(self, args): return -1 def do_EOF(self, args): print "Good bye!" return self.do_exit(args) def do_help(self, args): print self.__doc__ def emptyline(self): pass def default(self, line): result = yacc.parse(line) s = lisp_str(result) if s != 'nil': print s if __name__ == '__main__': ml = MiniLisp() ml.cmdloop()
実行すると入力待ちになります。
ml> (+ 11 12) Calling + with [11, 12] 23 ml>
わお!計算できたよー!
難しくはないけどなげーよ!って話はあるかもしれませんが。。。
「みなさんもplyで俺言語を作ってみてはいかがですか?」
とか言ってる人ってキモイよね!