ANTLR 中的匹配括号

Matching parentheses in ANTLR

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

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

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 匹配。

g运行 带有 -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',<W2>,1:5]
[@4,8:8='2',<W2>,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',<W2>,1:13]
[@10,15:15='P',<W1>,1:15]
[@11,17:17='2',<W2>,1:17]
[@12,19:19='3',<W2>,1:19]
[@13,20:20=')',<')'>,1:20]
[@14,21:21='.',<'.'>,1:21]
[@15,22:22='(',<'('>,1:22]
[@16,23:23='3',<W2>,1:23]
[@17,25:25='S',<W1>,1:25]
[@18,27:27='3',<W2>,1:27]
[@19,29:29='2',<W2>,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',<W2>,1:35]
[@26,37:37='T',<W1>,1:37]
[@27,39:39='2',<W2>,1:39]
[@28,41:41='4',<W2>,1:41]
[@29,42:42=')',<')'>,1:42]
[@30,43:42='<EOF>',<EOF>,1:43]

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

看来您的误解是递归下降解析从解析器规则开始,当它遇到 Lexer 规则时,尝试匹配它。

这不是 ANTLR 的工作方式。使用 ANTLR,您的输入首先是 运行 通过词法分析器(又名 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

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