0

私は COOL (教室のオブジェクト指向言語) のパーサー/レクサーを書いています。次のリンクで文法を確認できます: (マニュアルの最後のページ)

http://theory.stanford.edu/~aiken/software/cool/cool-manual.pdf

ANTLR を使用してこのプログラムを作成しています。次の入力を使用して、次の出力が必要です。

入力:

class Main inherits IO {
  main(): Object {{
    x <- 2 + 3 *4;
  }};
};

出力:

1
2
3
11
6
16
27
18
27
27

しかし、私が得ている出力は次のとおりです。

1
2
3
11
6
27
16
27
18
27

ここに私のパーサー/レクサーコードがあります:

// parser
grammar CA2;

program : {System.out.println("1");} (classdef';')+ ;
classdef : {System.out.println("2");} CLASS ID (INHERITS ID)? '{' (feature';')* '}' ;
feature : {System.out.println("3");} ID OPENP (formal (','formal)*)? CLOSEP ':' ID '{' expr '}' 
        | {System.out.println("4");} ID ':' ID ( POINTTOLEFT expr )? ;
formal : {System.out.println("5");} ID ':' ID ;
expr : {System.out.println("6");} ID POINTTOLEFT expr exprprime
     | {System.out.println("8");} ID OPENP ( expr (','expr)* )? CLOSEP exprprime
     | {System.out.println("9");} IF expr THEN expr ELSE expr FI exprprime
     | {System.out.println("10");} WHILE expr LOOP expr POOL exprprime 
     | {System.out.println("11");} '{' (expr';')+ '}' exprprime 
     | {System.out.println("12");} LET ID ':' ID (POINTTOLEFT expr)? (','ID ':' ID (POINTTOLEFT expr)?)* IN expr exprprime
     | {System.out.println("13");} CASE expr OF (ID POINTTORIGHT expr ';')+ ESAC exprprime
     | {System.out.println("14");} NEW ID exprprime
     | {System.out.println("15");} ISVOID expr exprprime
     /*| {System.out.println("16");} expr ADD expr
     | {System.out.println("17");} expr SUB expr
     | {System.out.println("18");} expr MULTIPLY expr
     | {System.out.println("19");} expr DIV expr
     | {System.out.println("20");} TILDA expr
     | {System.out.println("21");} expr LARGERTHAN expr
     | {System.out.println("22");} expr LARGEREQ expr
     | {System.out.println("23");} expr EQUALS expr
     | {System.out.println("24");} NOT expr
     | {System.out.println("25");} OPENP expr CLOSEP
     | {System.out.println("26");} ID
     | {System.out.println("27");} INTEGER*/
     | {System.out.println("28");} STRING exprprime | mathex exprprime ;
     /*| {System.out.println("29");} TRUE
     | {System.out.println("30");} FALSE ;*/
exprprime : {System.out.println("7");} (('@'ID)?)'.'ID OPENP (expr (','expr)*)? CLOSEP exprprime | ;
mathex : b ;
b : {System.out.println("24");} NOT b | c ;
cprime : {System.out.println("21");} LARGERTHAN d cprime 
       | {System.out.println("22");} LARGEREQ d cprime 
       | {System.out.println("23");} EQUALS d cprime | ;
c : d cprime ;
dprime : {System.out.println("16");} ADD e dprime 
       | {System.out.println("17");} SUB e dprime | ;
d : e dprime ;
eprime : {System.out.println("18");} MULTIPLY f eprime 
       | {System.out.println("19");} DIV f eprime | ;
e : f eprime ;
f : {System.out.println("20");} TILDA f | g ;
g : {System.out.println("25");} OPENP mathex CLOSEP 
  | {System.out.println("26");} ID 
  | {System.out.println("27");} INTEGER 
  | {System.out.println("29");} TRUE 
  | {System.out.println("30");} FALSE ;

//lexer
TRUE : 'true' ;
FALSE : 'false' ;
INHERITS : 'inherits' ;
CLASS : 'class' ;
IF : 'if' ;
THEN : 'then' ;
ELSE : 'else' ;
FI : 'fi' ;
WHILE : 'while' ;
LOOP : 'loop' ;
POOL : 'pool' ;
LET : 'let' ;
IN : 'in' ;
CASE : 'case' ;
OF : 'of' ;
ESAC : 'esac' ;
NEW : 'new' ;
ISVOID : 'isvoid' ;
NOT : 'not' ;
TILDA : '~' ;
WHITESPACE : [ ' '|'\r'|'\n'|'\t']+ ->skip ;
INTEGER : [0-9]+ ;
ID : ['_'a-zA-Z][a-zA-Z0-9'_']* ;
ADD : '+' ;
MULTIPLY : '*' ;
SUB : '-' ;
DIV : '/' ;
OPENP : '(' ;
CLOSEP : ')' ;
EQUALS : '=' ;
LARGERTHAN : '<' ;
LARGEREQ : '<=' ;
POINTTOLEFT : '<-' ;
POINTTORIGHT : '=>' ;
STRING : '"'(~[\r\n])*'"' ;

これは、ANTLR の COOL 文法のコード バージョンです。メインコードでコメントされている部分は曖昧さが解消され (意味はあいまいさが取り除かれます!)、2 番目の部分で左再帰が解放されます (数学規則)。

誰かがこれがどこで間違っているのか、そしてなぜ私は望ましい出力を得られないのか指摘できますか?

前もって感謝します!

4

1 に答える 1

1

のアクションを除いてprogram、各println呼び出しは文法内のトークン参照の直前に表示されます。これは、トークンがファイルに表示されるのと同じ順序で実行されることを意味することは明らかです。

期待される出力と実際の出力との間の最初の不一致は、行16との反転です27+入力内のトークンが入力内のトークンの前に出現した場合にのみ、期待される出力が発生し2ますが、明らかにそうではないことがわかります。2 番目の不一致は同じ理由で発生します。具体的には、予想される出力では、*トークンが文法内でトークンよりも前に現れると想定されていたことが原因3です。


あなたは最初に左再帰exprルールを作成し、その中にアクションを組み込んでいたことに気付きました。次の情報は、特定の質問の解決には関係ありませんが、そのコードのコメントを外して の左再帰形式を使用する場合に備えて理解しておくことが重要ですexpr

次の左再帰ルールを考慮して、2 つの組み込みアクションを追加して、識別子を簡単に追加できるようにします。

expr
  : {a();} ID
  | {b();} expr '+' ID
  ;

お気づきかもしれませんが、この構文は ANTLR ではコンパイルされません。{b();}ここで示した場所で式を評価すると、生成されたコードのパフォーマンスに多大な (マイナスの) 影響が生じることがわかったため、許可しないことにしました。出力は式のポーランド語接頭辞形式でしたが、パーサーは実際にはinfix notationを使用して入力を操作しようとしました。解決策は、代わりに中置記法を発行することです。

expr
  : {a();} ID
  | expr {b();} '+' ID
  ;

aおよびへの呼び出しの結果を収集することにより、結果をb書き込む前に、結果を任意の表記に変換できます。もう 1 つのオプションは、埋め込まれたアクションを、解析が完了した後に実行されるビジターに移動することです。ここでは、任意の順序でアクションを実行するのは簡単です。

さらに読む:インフィックス、ポストフィックス、およびプレフィックス

于 2014-10-30T12:41:58.870 に答える