以下は、論理式と算術式の組み合わせを評価する 2 つの方法を示す 2 つの単純な ANTLR 文法であり、その後にテスト クラスとその出力が続きます。最初の文法は、ANTLR を使用して式の型を決定し、式の値に互換性があることを確認して、要求した形式になっています。もう 1 つの形式は、文法内のアクションを使用して式の型を決定します。比較のためにここに載せておきます。
両方の文法で式の評価をハードコーディングして、例をより便利にし、期待どおりに機能することを示しました。これらの文法は、以下のテスト コードに示されている以外にはテストしていないことに注意してください。また、関心のある概念を説明するために必要な演算子のみを含めました。他の人を追加するのは簡単なはずです。
ANTLR に式の型を区別するように要求するには、演算子が複数の式の型に対して有効な場合に何をすべきかを ANTLR に指示する必要があります。たとえば、LogicRules
以下の文法では、lexer tokenLPAR
は、 rule で始まる論理式compExpr
または で始まる算術式の開始を示しprimaryExpr
ます。ANTLR は、既定では、入力"(3..."
が論理式のグループ化 ( "(3 + 3 == 6)"
) の開始をマークするのか、算術/数値式のグループ化 ( "(3 + 3) == 6"
) の開始をマークするのかを認識できません。ルールの構文述語は、compExpr
ANTLR が実際に待機して様子を見るように指示することで、これら 2 つの選択肢を区別できるようにするために使用されます。
LogicRules.g — ANTLR ルールにブール式と数値式を区別させる
grammar LogicRules;
statement returns [boolean result]
: logicalExpr {$result = $logicalExpr.result;} EOF
;
logicalExpr returns [boolean result]
@init { $result = true;}
: lhs=compExpr {$result = $lhs.result;}
(AND rhs=compExpr
{$result = $result && $rhs.result;}
)*
;
compExpr returns [boolean result]
@init { $result = true;}
: (eqExpr) => eqExpr {$result = $eqExpr.result;}
//^^ Needs a syntactic predicate to differentiate between logical grouping and arithmetic grouping
| LPAR logicalExpr {$result = $logicalExpr.result;} RPAR
//^^ Only group logical expressions at this level.
;
eqExpr returns [boolean result]
@init {$result = true;}
: lhs=arithmeticExpr
(EQ rhs=arithmeticExpr
{$result = $result && $lhs.result == $rhs.result;}
)+
//^^ use +: a logical expression of arithmetic expressions
// requires a logical operation to produce a boolean value
;
arithmeticExpr returns [int result]
@init {$result = 0;}
: lhs=primaryExpr {$result += $lhs.result;}
(PLUS rhs=primaryExpr
{$result += $rhs.result;}
)*
;
primaryExpr returns [int result]
@init {$result = 0;}
: INT {$result = $INT.int;}
| LPAR arithmeticExpr RPAR {$result = $arithmeticExpr.result;}
//^^ Only group other numeric/arithmetic expressions at this level.
;
INT : ('0'..'9')+;
AND : '&&';
EQ : '==';
LPAR : '(';
RPAR : ')';
PLUS : '+';
WS : (' '|'\t'|'\r'|'\n')+ {skip();};
ANTLR ルールではなくコードを使用して式の結果の型を評価すると、次のような文法になります。グループ化は 1 つのレベルでのみ行われ、構文述語は不要であることに注意してください。文法は全体的に比較的単純に保たれています。前の文法と同様に、この文法は無効な型変換に遭遇するとエラーを生成します。
LogicEval.g — コードでブール式と数値式を区別できるようにする
grammar LogicEval;
@parser::members {
private static boolean toBoolean(Object obj){
if (obj instanceof Boolean){
return (Boolean)obj;
} else {
throw new RuntimeException("Cannot convert " + obj + " to boolean");
}
}
private static int toInt(Object obj){
if (obj instanceof Integer){
return (Integer)obj;
} else {
throw new RuntimeException("Cannot convert " + obj + " to integer");
}
}
}
statement returns [Object result]
: expr {$result = $expr.result;} EOF
;
expr returns [Object result]
: lhs=compExpr {$result = $lhs.result;}
(AND rhs=compExpr
{$result = toBoolean($result) && toBoolean($rhs.result);}
)*
;
compExpr returns [Object result]
@init {Object lhsResult = null;}
: lhs=arithmeticExpr {$result = lhsResult = $lhs.result;}
(EQ rhs=arithmeticExpr
{$result = toInt(lhsResult) == toInt($rhs.result);}
)*
;
arithmeticExpr returns [Object result]
: lhs=primaryExpr {$result = $lhs.result;}
(PLUS rhs=primaryExpr
{$result = toInt($result) + toInt($rhs.result);}
)*
;
primaryExpr returns [Object result]
: INT {$result = $INT.int;}
| LPAR expr RPAR {$result = $expr.result;}
;
INT : ('0'..'9')+;
AND : '&&';
EQ : '==';
LPAR : '(';
RPAR : ')';
PLUS : '+';
WS : (' '|'\t'|'\r'|'\n')+ {skip();};
LogicTest.java — 2 つの文法のテスト コード
public class LogicTest {
public static void main(String[] args) {
test("1 + 2 == 3", Result.True);
test("1 + 2 == 4", Result.False);
test("1 + 2 == 3 && 1 + 2 == 4", Result.False);
test("1 + 2 == 3 && 4 + 5 == 9", Result.True);
test("(1 + 2) == 3 && (4 + 5 == 9)", Result.True);
test("1 + 2 == (3 && 4 + 5 == 9)", Result.Failure);
test("1 && 2", Result.Failure);
test("1 + 2", Result.Failure);
}
private static void test(String rawInput, Result expectedResult){
Result rulesResult = runRules(rawInput);
Result evalResult = runEval(rawInput);
System.out.println("---\n");
System.out.printf("**Input:** %s%n%n", rawInput);
System.out.printf("**Expected Result:** %s%n%n", expectedResult);
System.out.printf("**LogicRules Result:** %s%n%n", rulesResult);
System.out.printf("**LogicRules Passed?** %s%n%n", (rulesResult == expectedResult));
System.out.printf("**LogicEval Result:** %s%n%n", evalResult);
System.out.printf("**LogicEval Passed?** %s%n%n", (evalResult == expectedResult));
}
private static Result runRules(String rawInput){
CharStream input = new ANTLRStringStream(rawInput);
LogicRulesLexer lexer = new LogicRulesLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
LogicRulesParser parser = new LogicRulesParser(tokens);
boolean result;
try {
result = parser.statement();
} catch (Exception e) {
return Result.Failure;
}
if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
return Result.Failure;
}
return result ? Result.True : Result.False;
}
private static Result runEval(String rawInput){
CharStream input = new ANTLRStringStream(rawInput);
LogicEvalLexer lexer = new LogicEvalLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
LogicEvalParser parser = new LogicEvalParser(tokens);
Object result;
try {
result = parser.statement();
} catch (Exception e) {
return Result.Failure;
}
if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
return Result.Failure;
}
if (result instanceof Boolean){
return ((Boolean)result) ? Result.True : Result.False;
} else {
return Result.Failure; //Produced a valid result, but it wasn't a boolean.
}
}
private static enum Result {
True, False, Failure;
}
}
出力 — すべてのテストに合格
入力: 1 + 2 == 3
期待される結果:真
LogicRules 結果: True
LogicRules 合格? 真実
LogicEval 結果: True
LogicEval 合格? 真実
入力: 1 + 2 == 4
期待される結果: False
LogicRules 結果: False
LogicRules 合格? 真実
LogicEval 結果: False
LogicEval 合格? 真実
入力: 1 + 2 == 3 && 1 + 2 == 4
期待される結果: False
LogicRules 結果: False
LogicRules 合格? 真実
LogicEval 結果: False
LogicEval 合格? 真実
入力: 1 + 2 == 3 && 4 + 5 == 9
期待される結果:真
LogicRules 結果: True
LogicRules 合格? 真実
LogicEval 結果: True
LogicEval 合格? 真実
入力: (1 + 2) == 3 && (4 + 5 == 9)
期待される結果:真
LogicRules 結果: True
LogicRules 合格? 真実
LogicEval 結果: True
LogicEval 合格? 真実
入力: 1 + 2 == (3 && 4 + 5 == 9)
期待される結果:失敗
LogicRules 結果:失敗
LogicRules 合格? 真実
LogicEval 結果:失敗
LogicEval 合格? 真実
入力: 1 && 2
期待される結果:失敗
LogicRules 結果:失敗
LogicRules 合格? 真実
LogicEval 結果:失敗
LogicEval 合格? 真実
入力: 1 + 2
期待される結果:失敗
LogicRules 結果:失敗
LogicRules 合格? 真実
LogicEval 結果:失敗
LogicEval 合格? 真実