我使用 ANTLR 来解析数学表达式字符串并使用 MathML 对其进行标记。
现在我有下面的语法。现在我有三个问题:
- 该语法允许使用完整的表达式,例如
2*(3+4)
。我想 它还允许不完整的表达式,例如2*(3+
。作为 ANTLR 的完全新手我不知道如何实现这一点。 请指出正确的文档或给出示例。 - 平方根规则
sqrt
在原子中的位置似乎 可以工作,但我很确定它应该位于指数中的某个位置 规则?还是应该这样做? - 如果我想扩展这个语法来实际执行 计算,我可以以某种方式重用它还是必须复制和粘贴?
对于我的语法的任何其他评论或建议也很感激,因为我对 ANTLR 的总体验现在大约是四个小时。
grammar Expr;
parse returns [String value]
: stat+ {$value = $stat.value;}
;
stat returns [String value]
: exponent NEWLINE {$value = "<math>" + $exponent.value + "</math>";}
| NEWLINE
;
exponent returns [String value]
: e=expr {$value = $e.value;}
( '^' e=expr {$value = "<msup><mrow>" + $value + "</mrow><mrow>" + $e.value + "</mrow></msup>";}
)*
;
expr returns [String value]
: e=multExpr {$value = $e.value;}
( '+' e=multExpr {$value += "<mo>+</mo>" + $e.value;}
| '-' e=multExpr {$value += "<mo>-</mo>" + $e.value;}
)*
;
multExpr returns [String value]
: e=atom {$value = $e.value;}
( '*' e=atom {$value += "<mo>*</mo>" + $e.value;}
| '/' e=atom {$value += "<mo>/</mo>" + $e.value;}
)*
;
atom returns [String value]
: INT {$value = "<mn>" + $INT.text + "</mn>";}
| '-' e=atom {$value = "<mo>-</mo>" + $e.value;}
| 'sqrt[' exponent ']' {$value = "<msqrt><mrow>" + $exponent.value + "</mrow></msqrt>";}
| '(' exponent ')' {$value = "<mo>(</mo>" + $exponent.value + "<mo>)</mo>";}
;
INT : '0'..'9'+ ;
NEWLINE:'\r'? '\n' ;
WS : (' '|'\t')+ {skip();} ;
最佳答案
首先对你的语法进行一些评论:
- 您应该为规则的左侧和右侧指定唯一的标签 (
e1=atom ('*' e2=atom ...
); - 您可能需要创建单独的
sqrt
和[
代币而不是 1 个sqrt[
,否则输入"sqrt [ 9 ]"
(sqrt
和[
之间的空格)将无法正确处理; - 一元减法的优先级通常低于求幂。
rickythefox wrote:
The location of the square root rule sqrt among the atomics seems to work but I'm pretty sure it should be somewhere in the exponent rule? Or should it?
不,那里没问题:它应该具有最高优先级。说到优先级,您的情况下通常的优先级表(从最低到最高)是:
- 加法和减法;
- 乘法和除法;
- 一元减号;
- 求幂;
- 带括号的表达式(包括函数调用,如
sqrt[...]
)。
rickythefox wrote:
The grammar allows for complete expressions like 2*(3+4). I want it to also allow incomplete expressions, e.g. 2*(3+. Being a complete newbie at ANTLR I have no idea how to accomplish this. Please point me to the right document or give an example.
这很棘手。
我真的只看到一种方法:在你的统计规则中,你首先强制解析器在 token 流中向前查看,以检查是否确实存在 expr
先。这可以使用 syntactic predicate 来完成。一旦解析器确定有 expr
,然后才解析所述表达式。如果没有expr
,尝试匹配 NEWLINE
,如果也没有NEWLINE
,只需消耗 NEWLINE
之外的单个 token (必须是不完整表达式的一部分!)。 (我将在下面发布一个小演示)
rickythefox wrote:
If I want to extend this grammar to also actually perform the calculation, can I somehow reuse it or do I have to copy and paste?
ANTLR 解析器规则可以返回多个对象。当然,事实并非如此,因为 Java 方法(解析器规则本质上就是这样)只能返回单个对象。解析器规则返回一个对象,该对象包含对多个对象的引用。所以你可以这样做:
stat returns [String str, double num]
: ...
;
演示
考虑到我的所有提示,一个小型的工作演示可能如下所示:
grammar Expr;
parse returns [String str, double num]
@init{$str = "";}
: (stat
{
$str += $stat.str;
$num = $stat.num;
if(!Double.isNaN($num)) {
System.out.println($stat.text.trim() + " = " + $num);
}
})+
;
stat returns [String str, double num]
: (expr)=> expr NEWLINE {$str = "<math>" + $expr.str + "</math>"; $num = $expr.num;}
| NEWLINE {$str = ""; $num = Double.NaN;}
| ~NEWLINE {$str = ""; $num = Double.NaN; System.err.println("Ignoring: " + $text);}
;
expr returns [String str, double num]
: e1=multExpr {$str = $e1.str; $num = $e1.num;}
( '+' e2=multExpr {$str += "<mo>+</mo>" + $e2.str; $num += $e2.num;}
| '-' e2=multExpr {$str += "<mo>-</mo>" + $e2.str; $num -= $e2.num;}
)*
;
multExpr returns [String str, double num]
: e1=unaryExpr {$str = $e1.str; $num = $e1.num;}
( '*' e2=unaryExpr {$str += "<mo>*</mo>" + $e2.str; $num *= $e2.num;}
| '/' e2=unaryExpr {$str += "<mo>/</mo>" + $e2.str; $num /= $e2.num;}
)*
;
unaryExpr returns [String str, double num]
: '-' e=expExpr {$str = "<mo>-</mo>" + $e.str; $num = -1 * $e.num;}
| e=expExpr {$str = $e.str; $num = $e.num;}
;
expExpr returns [String str, double num]
: e1=atom {$str = $e1.str; $num = $e1.num;}
( '^' e2=atom {$str = "<msup><mrow>" + $str + "</mrow><mrow>" + $e2.str + "</mrow></msup>"; $num = Math.pow($num, $e2.num);}
)*
;
atom returns [String str, double num]
: INT {$str = "<mn>" + $INT.text + "</mn>"; $num = Double.valueOf($INT.text);}
| 'sqrt' '[' expr ']' {$str = "<msqrt><mrow>" + $expr.str + "</mrow></msqrt>"; $num = Math.sqrt($expr.num);}
| '(' expr ')' {$str = "<mo>(</mo>" + $expr.str + "<mo>)</mo>"; $num = $expr.num;}
;
INT : '0'..'9'+;
NEWLINE : '\r'? '\n';
WS : (' '|'\t')+ {skip();};
(请注意 (...)=>
就是所谓的语法谓词)
您可以使用以下类测试从上面的语法生成的解析器:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String src =
"sqrt [ 9 ] \n" +
"1+2*3 \n" +
"2*(3+ \n" +
"2*(3+42)^2 \n";
ExprLexer lexer = new ExprLexer(new ANTLRStringStream(src));
ExprParser parser = new ExprParser(new CommonTokenStream(lexer));
ExprParser.parse_return returnValue = parser.parse();
String mathML = returnValue.str;
double eval = returnValue.num;
// ...
}
}
如果您现在运行上面的类,您将看到输入
sqrt [ 9 ]
1+2*3
2*(3+
2*(3+42)^2
将产生以下输出:
sqrt[9] = 3.0
1+2*3 = 7.0
Ignoring: 2
Ignoring: *
Ignoring: (
Ignoring: 3
Ignoring: +
2*(3+42)^2 = 4050.0
关于ANTLR - 允许不完整的语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8299220/