質問
私は拡張しようとしています 小さな言語の文法 割り当てを表現として扱う。したがって、書くことは有効です
a = b = 1; // -> a = (b = 1)
a = 2 * (b = 1); // contrived but valid
a = 1 = 2; // invalid
割り当ては、2つの側面で他のオペレーターとは異なります。それは正しい連想であり(大したことではありません)、その左側は変数でなければなりません。だから私はこのような文法を変えました
statement: assignmentExpr | functionCall ...;
assignmentExpr: Identifier indexes? '=' expression;
expression: assignmentExpr | condExpr;
非LL(*)決定が含まれているため、機能しません。私もこのバリアントを試しました:
assignmentExpr: Identifier indexes? '=' (expression | condExpr);
しかし、同じエラーがありました。私は、に興味を持っています
- この特定の質問
- 非ll(*)の決定を伴う文法を考えると、問題を引き起こす2つのパスを見つける方法
- それを修正する方法
解決
ここで重要なのは、表現の内部には、表現を満たす何かがあることをパーサーに「保証」する必要があることです。これは、構文的な述語を使用して実行できます( ( ... )=>
の部品 add
と mult
ルール)。
簡単なデモ:
grammar TL;
options {
output=AST;
}
tokens {
ROOT;
ASSIGN;
}
parse
: stat* EOF -> ^(ROOT stat+)
;
stat
: expr ';' -> expr
;
expr
: add
;
add
: mult ((('+' | '-') mult)=> ('+' | '-')^ mult)*
;
mult
: atom ((('*' | '/') atom)=> ('*' | '/')^ atom)*
;
atom
: (Id -> Id) ('=' expr -> ^(ASSIGN Id expr))?
| Num
| '(' expr ')' -> expr
;
Comment : '//' ~('\r' | '\n')* {skip();};
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
これは入力を解析します:
a = b = 1; // -> a = (b = 1)
a = 2 * (b = 1); // contrived but valid
次のASTに:
他のヒント
構文的な述語を使用せずに、このような文法を変更して同じことを達成できると思います。
statement: Expr ';' | functionCall ';'...;
Expr: Identifier indexes? '=' Expr | condExpr ;
condExpr: .... and so on;
このアイデアを念頭に置いて、バートの例を変更しました。
grammar TL;
options {
output=AST;
}
tokens {
ROOT;
}
parse
: stat+ EOF -> ^(ROOT stat+)
;
stat
: expr ';'
;
expr
: Id Assign expr -> ^(Assign Id expr)
| add
;
add
: mult (('+' | '-')^ mult)*
;
mult
: atom (('*' | '/')^ atom)*
;
atom
: Id
| Num
| '('! expr ')' !
;
Assign : '=' ;
Comment : '//' ~('\r' | '\n')* {skip();};
Id : 'a'..'z'+;
Num : '0'..'9'+;
Space : (' ' | '\t' | '\r' | '\n')+ {skip();};
そして入力のために:
a=b=4;
a = 2 * (b = 1);
あなたは解析ツリーをフォローします:
所属していません StackOverflow