この記事は3年以上前に書かれた記事で内容が古い可能性があります
pythonで言語作ってみた(ply(Python Lex-Yacc)使ってみた)
言語作ってみたいけどc言語かけぬ、、、と思い検索すると、ply(Python Lex-Yacc)というものを発見。使ってみた。
https://github.com/dabeaz/ply
猿でもわかる解説
自分の理解では、3つのパートからできている
「ヘルプ」と打つと使い方を表示してくれる「HELP」という機能を作る例をみていく
(0)requirements
pythonバージョンは3
% python -V Python 3.5.0
plyというモジュールを入れておく
% pip install ply==3.10
(1)言葉を決める
トークンとやらをまず作る
トークンは文章を品詞分解したあとのパーツのようなもの、今回は「ヘルプ」と一言打つだけなので一個だけ定義する
tokens = ( 'HELP', )
HELPトークンに「ヘルプ」または「help」が入るとする
正規表現が使える
def t_HELP(t): r'ヘルプ|help' return t
(2)やることを決める
lexという、入力された情報を先ほど定義したトークンでどう理解するかを決めるモジュールを読み込む
import ply.lex as lex lexer = lex.lex()
トークン「HELP」、つまり、先ほど定義した「ヘルプ」か「help」が入力されたら、printで説明文を出すようにする
def p_help(t): 'expression : HELP' print( """ 「退出」と入力すれば終了します 「入力 <文字列>」と入力すると文字列を記憶します、10個まで記憶できます 「辞書」と入力すると記憶した文字列を表示します 「出力 <数字>」と入力すると記憶した文字列を表示します """ )
ちなみに
ちなみに、トークン「INPUT」と「VAR」を使って以下のように定義されていた場合、t[1]にINPUT、t[2]にVARの値が入る
def p_input(t): 'expression : INPUT VAR'
なので
def p_input(t): 'expression : INPUT VAR' print(t[2])
とすると、以下のように値を返すことができる
(INPUTにはinput、VARには文字列何でも入るとする)
何か入力して「Enter」を押してください→ input aa aa
(3)どう実行するかを決める
最後に、構文解析をするyaccというモジュールを読み込む
import ply.yacc as yacc parser = yacc.yacc()
入力とパースの制御について記述(雑)
while True: try: s = input('何か入力して「Enter」を押してください→ ') # Use raw_input on Python 2 except EOFError: break if not s: # ignore blank input continue parser.parse(s)
合体させると
以上合体させるとこんな感じに
# ============== # 言葉を決める(トークンを定義する) # ============== tokens = ( 'HELP', ) def t_HELP(t): r'ヘルプ|help' return t # ============== # やることを決める(ソースをトークンに分割) # ============== import ply.lex as lex lexer = lex.lex() def p_help(t): 'expression : HELP' print( """ 「退出」と入力すれば終了します 「入力 <文字列>」と入力すると文字列を記憶します、10個まで記憶できます 「辞書」と入力すると記憶した文字列を表示します 「出力 <数字>」と入力すると記憶した文字列を表示します """ ) # ============== # どう実行するかを決める(パーサーが構文解析をする) # ============== import ply.yacc as yacc parser = yacc.yacc() while True: try: s = input('何か入力して「Enter」を押してください→ ') # Use raw_input on Python 2 except EOFError: break if not s: # ignore blank input continue parser.parse(s)
これでヘルプだけ実行できるようになる
何か入力して「Enter」を押してください→ ヘルプ 「退出」と入力すれば終了します 「入力 <文字列>」と入力すると文字列を記憶します、10個まで記憶できます 「辞書」と入力すると記憶した文字列を表示します 「出力 <数字>」と入力すると記憶した文字列を表示します 何か入力して「Enter」を押してください→
コード
機能をちょっと強化すると以下のような感じに
最新版はこちら
tokens = ( 'EXIT', 'HELP', 'INPUT', 'OUTPUT', 'NUMBER', 'VAR', 'DICT', ) def t_NUMBER(t): r'\d+' try: t.value = int(t.value) except ValueError: print("Integer value too large %d", t.value) t.value = 0 return t t_VAR = r'[A-Za-z一-龥ぁ-んァ-ン_][A-Za-z一-龥ぁ-んァ-ン0-9_]*' t_ignore = " \t" t_ignore_COMMENT = r'\#.*' def t_EXIT(t): r'退出|exit' return t def t_HELP(t): r'ヘルプ|help' return t def t_INPUT(t): r'入力|input' return t def t_OUTPUT(t): r'出力|output' return t def t_DICT(t): r'メモリ|辞書|dict|mem' return t def t_newline(t): r'\n+' t.lexer.lineno += t.value.count("\n") def t_error(t): print("Illegal character '%s'" % t.value[0]) t.lexer.skip(1) import ply.lex as lex lexer = lex.lex() names = { } def p_help(t): 'expression : HELP' print( """ 「退出」と入力すれば終了します 「入力 <文字列>」と入力すると文字列を記憶します、10個まで記憶できます 「辞書」と入力すると記憶した文字列を表示します 「出力 <数字>」と入力すると記憶した文字列を表示します """ ) flag = 0 def p_input(t): 'expression : INPUT VAR' names.update({flag:t[2]}) global flag flag += 1 def p_output(t): 'expression : OUTPUT NUMBER' print(names[t[2]]) def p_dict(t): 'expression : DICT' print(names) import sys def p_exit(t): 'expression : EXIT' print('また来てください') sys.exit() def p_error(t): print("Syntax error at '%s'" % t.value) import ply.yacc as yacc parser = yacc.yacc() print("""========================== 「ヘルプ」と入力して「Enter」を押すと使い方が表示されます ==========================""") while True: try: s = input('何か入力して「Enter」を押してください→ ') # Use raw_input on Python 2 except EOFError: break if not s: # ignore blank input continue parser.parse(s)
実行
実行するとこんな感じ
# python ply_work.py ========================== 「ヘルプ」と入力して「Enter」を押すと使い方が表示されます ========================== 何か入力して「Enter」を押してください→ ヘルプ 「退出」と入力すれば終了します 「入力 <文字列>」と入力すると文字列を記憶します、10個まで記憶できます 「辞書」と入力すると記憶した文字列を表示します 「出力 <数字>」と入力すると記憶した文字列を表示します 何か入力して「Enter」を押してください→ 入力 りんご 何か入力して「Enter」を押してください→ 入力 ぶどう 何か入力して「Enter」を押してください→ 入力 柿 何か入力して「Enter」を押してください→ 辞書 {0: 'りんご', 1: 'ぶどう', 2: '柿'} 何か入力して「Enter」を押してください→ 出力 2 柿 何か入力して「Enter」を押してください→ 退出 また来てください #
色々ツッコミどころ満載ですが、一旦これで