【问题标题】:ANTLR - allowing for incomplete grammarANTLR - 允许不完整的语法
【发布时间】:2011-11-28 16:45:54
【问题描述】:

我正在使用 ANTLR 解析数学表达式字符串并使用 MathML 标记它们。

现在我有下面的语法。现在我有三个问题:

  1. 语法允许像2*(3+4) 这样的完整表达式。我想 它还允许不完整的表达式,例如2*(3+。成为一个 ANTLR 的完整新手我不知道如何做到这一点。 请指出正确的文件或举个例子。
  2. 平方根规则sqrt 在原子中的位置似乎 工作,但我很确定它应该在 exponent 的某个地方 规则?还是应该?
  3. 如果我想扩展此语法以实际执行 计算,我可以以某种方式重复使用它还是必须复制和粘贴?

对于我的语法的任何其他 cmets 或建议也很感激,因为我现在使用 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();} ;

【问题讨论】:

  • 定义“不完整”会很困难,尤其是当您允许缺少操作数、运算符或 ( ) 时。根据定义,解析器生成器接受您要解析的 语言的 BNF,而不是“不完整版本”。因此,您要么修改语法以包含您认为不完整的内容(我怀疑这将是失败,因为您的用户会认为您做错了),或者您找到了一种方法来扭曲解析器生成器(在这种情况下为 ANTLR ) 接受格式错误的输入。这可能很难做到。
  • @BartKiers 不完整的表达式总是最后一个。 MathML 允许不完整的表达式,因此结果将只是可以评估的部分。优先级不是故意的,谢谢您的注意。 :)

标签: antlr antlr3


【解决方案1】:

首先对你的语法说几句:

  • 您应该为规则提供左侧和右侧的唯一标签 (e1=atom ('*' e2=atom ...);
  • 您可能希望创建单独的 sqrt[ 令牌而不是 1 个单独的 sqrt[,否则像 "sqrt [ 9 ]" 这样的输入(sqrt[ 之间的空格)将无法正确处理;
  • 一元减号的优先级通常低于取幂。

rickythefox 写道:

原子中平方根规则 sqrt 的位置似乎有效,但我很确定它应该在指数规则中的某个地方?还是应该?

不,这很好:它应该具有最高优先级。谈到优先级,在您的情况下,通常的优先级表(从最低到最高)是:

  • 加法和减法;
  • 乘法和除法;
  • 一元减号;
  • 求幂;
  • 带括号的表达式(包括函数调用,如sqrt[...])。

rickythefox 写道:

语法允许完整的表达式,如 2*(3+4)。我希望它也允许不完整的表达式,例如2*(3+。作为 ANTLR 的新手,我不知道如何做到这一点。请指出正确的文档或举个例子。

这很棘手。

我真的只看到一种方法:在您的 stat 规则中,您首先强制解析器在令牌流中向前看,以检查前面是否真的有 expr。这可以使用syntactic predicate 来完成。一旦解析器确定存在expr,然后才解析所述表达式。如果没有expr,请尝试匹配NEWLINE,如果也没有NEWLINE,只需使用NEWLINE 以外的单个令牌(必须不完整的表达!)。 (我将在下面发布一个小演示)

rickythefox 写道:

如果我想扩展此语法以实际执行计算,我可以以某种方式重复使用它还是必须复制和粘贴?

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();};

(注意(...)=&gt; 就是这个所谓的句法谓词

您可以使用以下类测试从上述语法生成的解析器:

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

【讨论】:

  • 感谢您非常详尽的回答! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 2013-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多