微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ANTLR 中的匹配括号

如何解决ANTLR 中的匹配括号

我是 Antlr 的新手,最近遇到了一个括号匹配问题。解析树中的每个节点都具有 (Node1,W1,W2,Node2) 形式,其中 Node1Node2 是两个节点,W1W2 是它们之间的两个权重。给定一个输入文件 (1,C,10,2).((2,P,2,3).(3,S,3,2))*.(2,T,4),解析树看起来是错误的,其中运算符不是这些节点的父节点,括号不匹配。

enter image description here

我写的解析文件是这样的:

grammar Semi;

prog
    : expr+
    ;
    
expr
    : expr '*'   
    | expr ('.'|'+') expr 
    | tuple   
    | '(' expr ')' 
    ;

tuple
    : LP NODE W1 W2 NODE RP
    ;

LP  : '(' ;
RP  : ')' ;
W1  : [PCST0];
W2  : [0-9]+;
NODE: [0-9]+;


WS  : [ \t\r\n]+ -> skip ;    // toss out whitespace
COMMA: ',' -> skip;

似乎 expr| '(' expr ')' 无法正常工作。那么我应该怎么做才能让这个解析器检测括号是否属于该节点? 更新: 命令有两个错误

line 1:1 no viable alternative at input '(1'
line 1:13 no viable alternative at input '(2'

所以看起来词法分析器没有检测到元组,但这是为什么呢?

解决方法

您的 W2 和 NODE 规则相同,因此您打算成为 NODE 的节点与 W2 匹配。

grun with -tokens 选项:(注意,没有 NODE 令牌)

[@0,0:0='(',<'('>,1:0]
[@1,1:1='1',<W2>,1:1]
[@2,3:3='C',<W1>,1:3]
[@3,5:6='10',1:5]
[@4,8:8='2',1:8]
[@5,9:9=')',<')'>,1:9]
[@6,10:10='.',<'.'>,1:10]
[@7,11:11='(',1:11]
[@8,12:12='(',1:12]
[@9,13:13='2',1:13]
[@10,15:15='P',1:15]
[@11,17:17='2',1:17]
[@12,19:19='3',1:19]
[@13,20:20=')',1:20]
[@14,21:21='.',1:21]
[@15,22:22='(',1:22]
[@16,23:23='3',1:23]
[@17,25:25='S',1:25]
[@18,27:27='3',1:27]
[@19,29:29='2',1:29]
[@20,30:30=')',1:30]
[@21,31:31=')',1:31]
[@22,32:32='*',<'*'>,1:32]
[@23,33:33='.',1:33]
[@24,34:34='(',1:34]
[@25,35:35='2',1:35]
[@26,37:37='T',1:37]
[@27,39:39='2',1:39]
[@28,41:41='4',1:41]
[@29,42:42=')',1:42]
[@30,43:42='<EOF>',<EOF>,1:43]

如果我用 W2s 替换解析规则中的节点(抱歉,我不知道这应该代表什么),我得到:

enter image description here

您的误解似乎是递归下降解析从解析器规则开始,当遇到词法分析器规则时,会尝试匹配它。

这不是 ANTLR 的工作方式。使用 ANTLR,您的输入首先通过 Lexer(又名 Tokenizer)运行以生成令牌流。这一步完全不了解您的解析器规则。 (这就是为什么使用 grun 转储令牌流通常很有用,这使您可以了解解析器规则正在执行的操作(您可以在示例中看到没有 NODE 令牌,因为它们都匹配 W2)。

另外,一个建议...看来逗号是正确输入的重要组成部分(除非 (1C102).((2P23).(3S32))*.(2T24) 被认为是有效的输入。根据这个假设,我删除了 -> skip 并将它们添加到您的解析器规则(这就是您在解析树中看到它们的原因)。我使用的结果语法是:

grammar Semi;

prog: expr+;

expr: expr '*' | expr ('.' | '+') expr | tuple | LP expr RP;

tuple: LP W2 COMMA W1 COMMA W2 COMMA W2 RP;

LP: '(';
RP: ')';
W1: [PCST0];
W2: [0-9]+;
NODE: [0-9]+;

WS: [ \t\r\n]+ -> skip; // toss out whitespace
COMMA: ',';

为了让您的语法更加自由,我建议您的 Lexer 规则应该关注原始类型。而且,您可以使用标签使代码中的各种元素或元组更容易访问。举个例子:

grammar Semi;

prog: expr+;

expr: expr '*' | expr ('.' | '+') expr | tuple | LP expr RP;

tuple: LP nodef=INT COMMA w1=PCST0 COMMA w2=INT COMMA nodet=INT RP;

LP: '(';
RP: ')';
PCST0: [PCST0];
INT: [0-9]+;

COMMA: ',';
WS: [ \t\r\n]+ -> skip; // toss out whitespace

通过此更改,您的元组 Context 类将具有 w1、w1 和节点的访问器。节点将是我在此处定义的 NBR 令牌数组。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。