Parsing 将括号解析为ANTLR

Parsing 将括号解析为ANTLR,parsing,antlr,grammar,expression,Parsing,Antlr,Grammar,Expression,我正在尝试匹配平衡括号,这样,如果匹配,就会创建一个PARAMS树,否则LPARAM和RPARAM标记将作为原子添加到树中 tokens { LIST; PARAMS; } start : list -> ^(LIST list); list : (expr|atom)+; expr : LPARAM list? RPARAM -> ^(PARAMS list?); atom : INT | LPARAM | RPARAM; INT :

我正在尝试匹配平衡括号,这样,如果匹配,就会创建一个
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
树,因为在规则表达式中,它总是将end
RPARAM
视为一个原子,而不是该规则的结束标记

因此目前,类似于
1235
的内容被添加到
列表
树中,作为令牌的平面列表,而不是所需的分组

我以前曾处理过将令牌作为原子添加到树中,但它们从未能够启动另一条规则,就像这里的
LPARAM
所做的那样


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

这里有一个简单的方法,有几个约束。我认为这些符合你在评论中提到的预期行为

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

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

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

atom    : INT
        ;
规则
root
匹配不匹配的
LPARAM
s和
RPARAM
s。规则
list
atom
只关心它们自己

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

测试用例1:没有列表 输入:
123

输出:

测试用例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    : ']';

LPARAM
RPARAM
实际上是原子还是表示列表开头和结尾的语法标记?有没有一种情况下,您希望将一个视为原子,而另一种情况下,它应该是标记?是的,LPARAM和RPARAM能够单独显示在树中。它们不仅仅是语法标记。我希望它们被视为标记,只有当它能够真正匹配括号中的表达式时。所以在“12(3)”中,(3)应该是一个带括号的表达式,结果树应该是^(列表12^(参数3))。而不成功的匹配,如1 2)3)应该变成^(列表1 2)3)。我明白了。语法应该如何处理“1(2)3”这样的输入?那应该是“1^(列表2)3”还是“1^(列表2”)3”?那么“1(2)3”呢?那是“
^(列表)(“2)3
”还是“
”(“1^(列表2)3
”?第二个。只要字符串的任何部分都能通过一个开放和闭括号成功地平衡,那么它就会被创建为PARAMS树,否则它失败,lpram/RPARAM应该只是作为原子添加。谢谢你花时间帮助我解决我的问题。我非常感谢你添加的所有额外测试用例。我只是changed expr+到expr*,以允许使用空括号。非常感谢:)您好,很抱歉再次询问,但我正在尝试将您的答案应用到我的语法中。现在,LPARAM和RPARAM由两个名为openOp和closeOp的规则表示。openOp对应于诸如“(”、“{”、“|”之类的起始运算符,closeOp对应于诸如“)、”之类的关闭运算符“,”|“。规则还返回一个名为“op”的字符串,其中包含匹配标记的文本。我是否可以使用语义谓词检查这两个运算符是否可以相互匹配?列表:l=openOp expr*r=closeOp{canBalance($l.op,$r.op)}?->^(列表expr*)@DavidJamesBall这是一个困难的问题。在我上面的语法中,
expr
表示可以包含在列表中的任何内容。它排除不匹配的位,因为它们由
root
管理。允许一种类型的不匹配成为有效匹配(例如
([1)
->
^(列表[1)
)使用这种方法并不容易,但我会仔细考虑一下。我已经隔离了这个问题。您的规则工作得很好。问题是我的规则…list:l=openOp expr*r=closeOp{canBalance($l.op,$r.op)}?->^(list expr*)。那么$l.op和$r.op总是空的。如果我传入$l.text,我可以看到openOp工作正常。如果我删除谓词,那么$l.op和$r.op就不再是空的。在规则末尾使用语义谓词有什么微妙之处,我不知道。它似乎是先执行的。我想这是因为backtraCKK:语义谓词依赖于规则中触发的动作,但这些动作不会被命中,因为解析器正在回溯。如果
openOp
closeOp
每个选项只引用一个令牌,则可以使用
$l.start
获取令牌。否则,使用
{$op=$WHATEVER}
在规则中强制执行操作,即使在回溯过程中也是如此。