mini-Pythonインタープリタその2

parserの概形は少しを残して書き終えた。
昨日の日記のコメントで「頭をつかう」と言ったのは、ほとんどは文法の定義通りに書き下せばいいのだがそれだと不都合が生じる部分があるということ。文法の定義が

while_stmt ::= 'while' expression ':' suite

だったらこんな感じに書けばできる*1

void Parser::parseWhileStatement(void)
{
  readToken(TOK_KW_WHILE);
  parseExpression();
  readToken(TOK_COLON);
  parseSuite();

  return;
}

で、頭を使うのが代入文(assignment statement)。ここで躓いてる。

assignment_stmt ::= target '=' expression

target ::= identifier | subscription

subscription ::= primary '[' expression_list_with_comma ']'

primary ::= atom ( '.' identifier | '[' expression_list_with_comma ']' 
           | '(' expression_list_with_comma ')' )*

atom ::= identifier | literal | list_display | dict_display 
           | '(' expression_list_with_comma ')'

atomはtokenで振り分けられる。というわけでガリガリ書けば頭を使わずにassignment_stmtのparserは書ける。
何が問題なのかというとparseSubscription(),parsePrimary(),parseAtom()といったメソッドを用意してあるのでできればこれを使いたいということだ。う〜ん

  1. tokenがidentifierじゃなかったらsubscriptionをparse。
  2. tokenがidentifierだったら次のtokenを見る
  3. 次のtokenがequalだったら続いてexpressionをparse
  4. じゃなかったらsubscriptionをparse

ここで問題発生。4のidentifierの時にsubscriptionをparseしようとしてもidentifierはその内容もろとも既に読み捨てられている*2
解決策としてはparsePrimary()にatom=identifierを伝えるフラグを渡してやることだろうか。


しかしここでまた問題発生だ。

statement ::= expression_stmt NEWLINE
	      | assignment_stmt NEWLINE
	      | pass_stmt NEWLINE
              | ……

expression_stmtも突き詰めていくと先頭がprimaryにたどり着くので、このexpression_stmtとassignment_stmtは同時に処理しなくてはならないということだ。
頭が悪いのでこの辺で思考停止。おやすみなさい。
また明日から自販機の中でジュースを落とす仕事が始まるお……

*1:返り血とか構文木の構築は省略

*2:TokenStreamに吐きだしていれば先読みも後戻りも楽だけど、リアルタイムでtokenを切っているので戻るのが難しい。