【问题标题】:Parse Parenthesis as atoms ANTLR将括号解析为原子 ANTLR
【发布时间】:2012-12-20 21:10:27
【问题描述】:

我正在尝试匹配平衡括号,这样,如果进行匹配,则会创建一个 PARAMS 树,否则 LPARAM 和 RPARAM 令牌将作为原子添加到树中...

tokens
{
    LIST;    
    PARAMS;
}

start   : list -> ^(LIST list);

list    : (expr|atom)+;

expr : LPARAM list? RPARAM -> ^(PARAMS list?);

atom :  INT | LPARAM | RPARAM;

INT :   '0'..'9'+;
LPARAM  :   '(';
RPARAM  :   ')';

目前,它永远不会创建 PARAMS 树,因为在规则 expr 中,它总是将结尾 RPARAM 视为原子,而不是该规则的结束标记。

因此,目前,1 2 3 (4) 5 之类的内容作为标记的平面列表添加到 LIST 树中,而不是所需的分组。

我之前处理过将标记作为原子添加到树中,但它们永远无法启动另一个规则,就像 LPARAM 在这里所做的那样。

我需要某种语法/语义谓词吗?

【问题讨论】:

  • LPARAMRPARAM 实际上是原子还是它们是表示列表开头和结尾的句法标记?是否有一种情况是您希望将一个视为原子,而另一种情况应将其视为标记?
  • 是的,LPARAM 和 RPARAM 可以单独出现在树中。它们不仅仅是句法标记。我希望它们被视为标记,只有当它实际上可以匹配带括号的表达式时。所以在“1 2 (3)”中,(3) 应该是带括号的表达式,结果树应该是 ^(LIST 1 2 ^(PARAMS 3))。而不成功的匹配,例如 1 2 )3) 应该变成 ^(LIST 1 2 ) 3 ))。
  • 我明白了。语法应该如何处理像“1(2)3)”这样的输入?应该是“1 ^(LIST 2) 3 ')'”还是“1 ^(LIST 2 ')' 3 )”?
  • “(1(2)3”呢?是“^(LIST '(' 2) 3”还是“'(' 1 ^(LIST 2) 3”?
  • 第二个。只要字符串的任何部分都可以通过左括号和右括号成功平衡,则将其创建为 PARAMS 树,否则将失败,LPARAM/RPARAM 应作为原子添加。

标签: parsing antlr grammar expression


【解决方案1】:

这是一个带有几个约束的简单方法。我认为这些符合您在 cmets 中提到的预期行为。

  • 不匹配的LPARAM 永远不会出现在子列表中
  • 不匹配的RPARAM 永远不会出现在子列表中

语法:

start   : root+ EOF -> ^(LIST root+ );

root    : expr
        | LPARAM
        | RPARAM
        ;
        
expr    : list
        | atom
        ;           
        
list    : LPARAM expr+ RPARAM -> ^(LIST expr+)
        ;

atom    : INT
        ;

规则 root 匹配不匹配的 LPARAMs 和 RPARAMs。规则listatom 只关心自己。

此解决方案相对脆弱,因为规则 root 要求将 expr 列在 LPARAMRPARAM 之前。即便如此,也许这足以解决您的问题。

测试用例 1:没有列表

输入:1 2 3

输出:

测试用例 2:一个列表

输入:1 (2) 3

输出:

测试用例 3:两个列表

输入:(1) 2 (3)

输出:

测试用例 4:没有列表,左不匹配

输入:((1 2 3

输出:

测试用例 5:两个列表,左不匹配

输入:((1 (2) (3)

输出:

测试用例 6:没有列表,权限不匹配

输入:1 2 3))

输出:

测试用例 7:两个列表,权限不匹配

输入:(1) (2) 3))

输出:

测试用例 8:两个列表,混合不匹配的左侧

输入:((1 (2) ( (3)

输出:

测试用例 9:两个列表,混合不匹配的权限

输入:(1) ) (2) 3))

输出:


这里有一个稍微复杂一点的语法,适用于 []() 对。我认为当你添加对时,解决方案会变得更糟,但是,嘿,这很有趣!您可能还遇到了语法驱动的 AST 构建的限制。

start   : root+ EOF -> ^(LIST root+ )
        ;
        
root    : expr
        | LPARAM
        | RPARAM
        | LSQB
        | RSQB
        ;       
expr    : plist
        | slist
        | atom
        ;           
        
plist   : LPARAM pexpr* RPARAM -> ^(LIST pexpr*)
        ;
        
pexpr   : slist
        | atom
        | LSQB
        | RSQB
        ;       
        
slist   : LSQB sexpr* RSQB -> ^(LIST sexpr*)
        ;
        
sexpr   : plist
        | atom
        | LPARAM
        | RPARAM
        ;               
        
atom    : INT;

INT     : ('0'..'9')+;
LPARAM  : '(';
RPARAM  : ')';
LSQB    : '[';
RSQB    : ']';

【讨论】:

  • 感谢您花时间帮助我解决问题。我非常感谢您添加的所有其他测试用例。我只是将 expr+ 更改为 expr*,以允许空括号。非常感谢:)
  • 您好,很抱歉再次提问,但我正在尝试将您的答案应用到我的语法中。现在 LPARAM 和 RPARAM 由 2 个规则表示,称为 openOp 和 closeOp。 openOp 对应于 '(', '{', '|' 等起始运算符,closeOp 对应于')', '}', '|' 等结束运算符。这些规则还返回一个名为“op”的字符串,其中包含匹配标记的文本。我可以使用语义谓词来检查两个运算符是否可以相互匹配?列表:l=openOp expr* r=closeOp {canBalance($l.op,$r.op)}?-> ^(LIST expr*).
  • @DavidJamesBall 这很难。在我上面的语法中,expr 表示可以包含在列表中的任何内容。这不包括不匹配的位,因为它们由root 管理。允许一种类型的不匹配进入有效匹配(例如([1) -> ^(LIST [ 1))不会轻易使用这种方法,但我会多考虑一下。
  • 我已经隔离了这个问题。你的规则工作得很好。问题在于我的规则...列表:l=openOp expr* r=closeOp {canBalance($l.op,$r.op)}?-> ^(LIST expr*)。然后 $l.op 和 $r.op 总是空的。如果我传入 $l.text,我可以看到 openOp 工作正常。如果我删除谓词,则 $l.op 和 $r.op 不再为空。在我不知道的规则末尾使用语义谓词是否有一些微妙之处。它似乎先执行它或什么的。
  • 我认为这是因为回溯:语义谓词取决于规则中触发的动作,但由于解析器正在回溯,这些动作不会被命中。如果openOpcloseOp 每个备选方案只引用一个令牌,您可以使用$l.start 来获取令牌。否则,请在规则中使用 {{$op = $WHATEVER}} 强制执行操作,即使在回溯期间也是如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 2011-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多