構文エラー処理 (0)

少しずつ書いていきます

  • 実行VMロケールを見て、日本語と英語でエラーメッセージを出す。
  • クォート途中で死んだら、その旨通知しる。
  • コメントの中で死んだら、その旨通知しる。
  • どんなルールの処理中に死んだのか、通知しる。
エラー処理の方針等を考えてみる。 - 設計と実装の狭間で。
  • 実行VMロケールを見て、日本語と英語でエラーメッセージを出す。
    • エラーが発生したらエラーオブジェクトを溜めておいて、あとで表示する
  • クォート途中で死んだら、その旨通知しる。
    • (Lexer) 一文字目がクウォート開始文字の不明なトークンなら、適当なところまで読み飛ばしてみる (改行文字までとか)
  • コメントの中で死んだら、その旨通知しる。
    • (Parser) inComment時の例外は、コメントの終端まで読み飛ばしてみる
  • どんなルールの処理中に死んだのか、通知しる。

パーサ上で発生した文法エラーは、どちらかの方法で回復させるのが普通です。

  1. エラーが発生したルールの終端までのトークンを読み飛ばして、そのルールをなかったことにする (token deletion)
  2. エラーが発生した地点にトークンを挿入して、そのルールを完成させる (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のほう