2

次の構文を許可する文法を指定する方法はありますか:

f(x)(g, (1-(-2))*3, 1+2*3)[0]

これは次のように変換されます (順序を示すために疑似 Lisp で):

(index 
  ((f x)
    g 
    (* (- 1 -2) 3)
    (+ (* 2 3) 1)
  ) 
  0
)

限られた演算子の優先順位などと一緒に。


次の文法は backtrack = true で機能しますが、それは避けたいと思います。

grammar T;

options {
 output=AST;
 backtrack=true;
 memoize=true;
}

tokens {
  CALL;
  INDEX;
  LOOKUP;
}

prog: (expr '\n')* ;

expr : boolExpr;

boolExpr
 : relExpr (boolop^ relExpr)?
 ;

relExpr
 : addExpr (relop^ addExpr)?
 | a=addExpr oa=relop b=addExpr ob=relop c=addExpr
   -> ^(LAND ^($oa $a $b) ^($ob $b $c))
 ;


addExpr
 : mulExpr (addop^ mulExpr)?
 ;

mulExpr
 : atomExpr (mulop^ atomExpr)?
 ;

atomExpr
 : INT
 | ID
 | OPAREN expr CPAREN -> expr
 | call
 ;

call
 : callable ( OPAREN (expr (COMMA expr)*)? CPAREN -> ^(CALL callable expr*)
            | OBRACK expr CBRACK -> ^(INDEX callable expr)
            | DOT ID -> ^(INDEX callable ID)
            )
 ;

fragment
callable
 : ID
 | OPAREN expr CPAREN
 ;

fragment
boolop
 : LAND | LOR
 ;

fragment
relop
 : (EQ|GT|LT|GTE|LTE)
 ;

fragment
addop
 : (PLUS|MINUS)
 ;

fragment
mulop
 : (TIMES|DIVIDE)
 ;

EQ : '==' ;
GT : '>' ;
LT : '<' ;
GTE : '>=' ;
LTE : '<=' ;

LAND : '&&' ;
LOR : '||' ;

PLUS : '+' ;
MINUS : '-' ;
TIMES : '*' ;
DIVIDE : '/' ;

ID : ('a'..'z')+ ;
INT : '0'..'9' ;

OPAREN : '(' ;
CPAREN : ')' ;
OBRACK : '[' ;
CBRACK : ']' ;
DOT : '.' ;

COMMA : ',' ;
4

1 に答える 1

2

あなたの文法にはいくつかの間違いがあります:

1

fragmentパーサー ルールではなく、レクサー ルールのみを s にすることができます。一部の ANTLR ターゲットは、パーサー ルールの前にある fragment キーワードを単純に無視しますが (Java ターゲットなど)、文法からそれらを削除することをお勧めします。別のターゲット言語用のパーサーを作成することにした場合、それ。

2

がないと、ツリー書き換え演算子 (および)書き換えルール ( ) をbacktrack=true混在させることはできません。これは、現在使用している 2 つの選択肢の代わりに、単一の選択肢を内部に作成する必要があるためです(これはあいまいさを排除するためです)。^!->relExpr

あなたの場合、^(単一の代替内で) だけで目的の AST を作成することはできないため、次のようにする必要があります。

relExpr
 : (a=addExpr -> $a) ( (oa=relOp b=addExpr    -> ^($oa $a $b))
                         ( ob=relOp c=addExpr -> ^(LAND ^($oa $a $b) ^($ob $b $c))
                         )?
                     )?
 ;

(はい、私は知っています、それは特にきれいではありませんが、それは仕方がないことです)

また、トークンがブロックLANDで定義されている場合にのみ、トークンを書き換えルールに入れることができます。tokens { ... }

tokens {
  // literal tokens
  LAND='&&';
  ...

  // imaginary tokens
  CALL;
  ...
}

それ以外の場合、トークン (およびその他のパーサー ルール) は、実際にパーサー ルール自体の内部で発生する場合にのみ、書き換えルールで使用できます。

3

文法で単項マイナスを考慮していませんでした。次のように実装してください。

mulExpr
 : unaryExpr ((TIMES | DIVIDE)^ unaryExpr)*
 ;

unaryExpr
 : MINUS atomExpr -> ^(UNARY_MINUS atomExpr)
 | atomExpr
 ;

ここで、 を必要としない文法を作成するには、ルールからandをbacktrack=true削除します。ID'(' expr ')'atomExpr

atomExpr
 : INT
 | call
 ;

ルール内で渡されたすべてをcallableオプションにします。call

call
 : (callable -> callable) ( OPAREN params CPAREN -> ^(CALL $call params)
                          | OBRACK expr CBRACK   -> ^(INDEX $call expr)
                          | DOT ID               -> ^(INDEX $call ID)
                          )*
 ;

そのように、IDand'(' expr ')'はすでに一致していますcall(そしてあいまいさはありません)。


上記の注意事項をすべて考慮すると、次の文法を取得できます。

grammar T;

options {
  output=AST;
}

tokens {
 // literal tokens
 EQ     = '==' ;
 GT     = '>' ;
 LT     = '<' ;
 GTE    = '>=' ;
 LTE    = '<=' ;
 LAND   = '&&' ;
 LOR    = '||' ;
 PLUS   = '+' ;
 MINUS  = '-' ;
 TIMES  = '*' ;
 DIVIDE = '/' ;
 OPAREN = '(' ;
 CPAREN = ')' ;
 OBRACK = '[' ;
 CBRACK = ']' ;
 DOT    = '.' ;
 COMMA  = ',' ;

 // imaginary tokens
 CALL;
 INDEX;
 LOOKUP;
 UNARY_MINUS;
 PARAMS;
}

prog
 : expr EOF -> expr
 ;

expr
 : boolExpr
 ;

boolExpr
 : relExpr ((LAND | LOR)^ relExpr)?
 ;

relExpr
 : (a=addExpr -> $a) ( (oa=relOp b=addExpr    -> ^($oa $a $b))
                         ( ob=relOp c=addExpr -> ^(LAND ^($oa $a $b) ^($ob $b $c))
                         )?
                     )?
 ;

addExpr
 : mulExpr ((PLUS | MINUS)^ mulExpr)*
 ;

mulExpr
 : unaryExpr ((TIMES | DIVIDE)^ unaryExpr)*
 ;

unaryExpr
 : MINUS atomExpr -> ^(UNARY_MINUS atomExpr)
 | atomExpr
 ;

atomExpr
 : INT
 | call
 ;

call
 : (callable -> callable) ( OPAREN params CPAREN -> ^(CALL $call params)
                          | OBRACK expr CBRACK   -> ^(INDEX $call expr)
                          | DOT ID               -> ^(INDEX $call ID)
                          )*
 ;

callable
 : ID
 | OPAREN expr CPAREN -> expr
 ;

params
 : (expr (COMMA expr)*)? -> ^(PARAMS expr*)
 ;

relOp
 : EQ | GT | LT | GTE | LTE
 ;

ID     : 'a'..'z'+ ;
INT    : '0'..'9'+ ;
SPACE  : (' ' | '\t') {skip();};

"a >= b < c"これは、入力を次の AST に解析します。

ここに画像の説明を入力

入力"f(x)(g, (1-(-2))*3, 1+2*3)[0]"は次のようになります。

ここに画像の説明を入力

于 2012-05-23T18:48:36.463 に答える