構文エラー処理 (0)
少しずつ書いていきます
エラー処理の方針等を考えてみる。 - 設計と実装の狭間で。
- 実行VMのロケールを見て、日本語と英語でエラーメッセージを出す。
- エラーが発生したらエラーオブジェクトを溜めておいて、あとで表示する
- クォート途中で死んだら、その旨通知しる。
- (Lexer) 一文字目がクウォート開始文字の不明なトークンなら、適当なところまで読み飛ばしてみる (改行文字までとか)
- コメントの中で死んだら、その旨通知しる。
- (Parser) inComment時の例外は、コメントの終端まで読み飛ばしてみる
- どんなルールの処理中に死んだのか、通知しる。
- 気合でスタックトレースを作るorz (see Irenka Search Query)
パーサ上で発生した文法エラーは、どちらかの方法で回復させるのが普通です。
- エラーが発生したルールの終端までのトークンを読み飛ばして、そのルールをなかったことにする (token deletion)
- エラーが発生した地点にトークンを挿入して、そのルールを完成させる (token insertion)
LL(k)で1つ目の方法を選ぶ場合、リカバリポイントの設定が重要になります。リカバリポイントは終端のわかりやすいルールは何か、といった点を考えて設定します。
エラー時にトークンを読み飛ばす場合、どこまで読み飛ばすのがいいかを考えます。LL*1などではよく ';' や '}' までスキップするような方法を見かけます。
コレくらいの言語を元に、簡単なエラー復帰について紹介の予定。
grammar Simple; options { language = Java; output = AST; ASTLabelType = CommonTree; } tokens { NEGATE; } program : statement* ; statement : if_statement | print_statement ; if_statement : IF condition statement -> ^(IF condition statement) ; print_statement : PRINT expression SEMICOLON -> ^(PRINT expression) ; condition : LP expression RP -> expression ; expression : factor ( PLUS^ factor | MINUS^ factor )* ; factor : unary ( TIMES^ unary | DIVIDE^ unary | REMAINDER^ unary )* ; unary : h=MINUS term -> ^(NEGATE{h} term) | term -> term ; term : IDENTIFIER | NUMBER | LP expression RP -> expression ; IF : 'if'; PRINT : 'print'; SEMICOLON : ';'; LP : '('; RP : ')'; PLUS : '+'; MINUS : '-'; TIMES : '*'; DIVIDE : '/'; REMAINDER : '%'; IDENTIFIER : ( 'a'..'z' )+ ; NUMBER : ( '0'..'9' )+; WS : ( ' ' | '\t' | '\r' | '\n' ) { $channel = HIDDEN; } ;
*1:Light weight Languageのほう