问题描述
如果用户输入 A 和 B,则解析正确进行。但是如果用户输入 A B(没有逻辑运算符),那么解析器不会抱怨缺少“AND”标记。 我尝试将其用于解析器规则: 布尔与表达式 : simpleFilter ((AND)+ simpleFilter )* #SimF ;
但是解析器停止考虑第二个操作数。表示如果用户输入“A B”,则仅对第一个操作数“A”生成 AST,不解析 B。
如果用户输入“A B”而不是“A AND B”,我希望解析器抱怨缺少布尔运算符 任何帮助都将不胜感激,因为我一直在尝试解决此问题,但徒劳无功
语法文件在下面提到
grammar Filter;
/* Parser rules */
filter
: LPAREN logicalExpression RPAREN #ParenthesizedFilter
| logicalExpression EOF #CmopositeFilterHash;
logicalExpression
: booleanAndExpression ( OR booleanAndExpression )* #LogExp
;
booleanAndExpression
: simpleFilter ((AND) ? simpleFilter )* #SimF
;
simpleFilter
:NOT simpleFilter #NegateFilter
| key=STR op=EQ value=(STR | DECIMAL) #EqualsFilter
| key=STR op=NE value=(STR | DECIMAL) #NotEqualsFilter
| key=STR op=GT value=(STR | DECIMAL) #GreaterThanFilter
| key=STR op=GE value=(STR | DECIMAL) #GreaterThanEqualsFilter
| key=STR op=LT value=(STR | DECIMAL) #LessthanFilter
| key=STR op=LE value=(STR | DECIMAL) #LessthanEqualsFilter
| key=STR op=IN referenceValue #InReferenceFilter
| key=STR op=NOT op=IN referenceValue #notinReferenceFilter
| key=STR op=BETWEEN fromValue=(STR | DECIMAL) AND tovalue=(STR | DECIMAL) #RangeFilter
| key=STR NOT BETWEEN fromValue=(STR | DECIMAL) AND tovalue=(STR | DECIMAL) #OutsideRangeFilter
| key=STR op=IN array #InArrayFilter
| key=STR NOT IN array #notinArrayFilter
| key=STR op=CONTAINS value=(STR | DECIMAL) #ContainsFilter
| key=STR NOT CONTAINS value=(STR | DECIMAL) #NotContainsFilter
| DOLLAR LCURLY FILTER SEP id=(DECIMAL | STR) SEP name=STR RCURLY #ReferenceFilter;
array
: LBRACKET (arrayItem SEP*)+ RBRACKET ;
arrayItem
: value=(STR | DECIMAL)
| referenceValue ;
referenceValue
: DOLLAR LCURLY type=LIST SEP id=(STR | DECIMAL) SEP name=(STR | DECIMAL) RCURLY ;
/* Lexer Rules*/
SEP:
',' | ';';
LIST
: 'List' ;
FILTER: 'Filter';
DOLLAR: '$';
LCURLY: '{';
RCURLY: '}';
LPAREN: '(';
RPAREN: ')';
LBRACKET: '[';
RBRACKET: ']';
GT: '>';
GE: '>=';
LT: '<';
LE: '<=';
EQ: '=';
NE: '!=';
NOT: '!' | 'not' | 'NOT';
AND: 'AND' | 'and';
OR: 'OR' | 'or';
IN: 'in' | 'IN';
BETWEEN: 'between' | 'BETWEEN';
CONTAINS: 'contains' | 'CONTAINS';
AND_MUST: 'AND';
DECIMAL: '-'? DIGIT+ ( '.' DIGIT+ )? ;
fragment DIGIT: [0-9] ;
STR: ALPHANUMERIC+ | '"' (ALPHANUMERIC | [ ] | '""')+ '"';
fragment ALPHANUMERIC: [a-z0-9A-Z!.:@#$%&^*'+/?_`~-];
WS : [ \t\r\n\f]+ -> skip ;
解决方法
此语法不解析“A 和 B”,更不用说从 booleanAndExpression
或 filter
开始的“A B”。我怀疑你的驱动程序代码——你没有提供——没有检查解析和 lex 中的错误条件。我无法告诉您正在使用的目标,因此很难知道您的构建环境是什么,以及它是否有效。但是,我也会调查一下。
这是一个完整的 C# 应用程序,其中包含您的语法 (link)。驱动程序代码由我的 Antlr4BuildTasks.Templates 生成,并使用 Antlr v4.9 和 Antlr4BuildTasks,我也编写了它们。驱动程序检查解析错误,打印出两个示例的标记和树。在“A and B”和“A B”中,解析失败。这是对这些输入运行语法的输出。
A and B
line 1:2 no viable alternative at input 'Aand'
line 1:2 no viable alternative at input 'Aand'
line 1:7 no viable alternative at input 'B'
line 1:7 no viable alternative at input 'B'
error in parse.
[@0,0:0='A',<25>,1:0]
[@1,2:4='and',<18>,1:2]
[@2,6:6='B',1:6]
[@3,7:6='<EOF>',<-1>,1:7]
( booleanAndExpression
( simpleFilter
)
( simpleFilter
( STR i=0 txt=A tt=25 DEFAULT_TOKEN_CHANNEL
) )
( AND i=1 txt=and tt=18 DEFAULT_TOKEN_CHANNEL
)
( simpleFilter
)
( simpleFilter
( STR i=2 txt=B tt=25 DEFAULT_TOKEN_CHANNEL
) ) )
A B
line 1:2 no viable alternative at input 'AB'
line 1:2 no viable alternative at input 'AB'
error in parse.
[@0,2:2='B',3:2='<EOF>',1:3]
( booleanAndExpression
( simpleFilter
)
( simpleFilter
( STR i=0 txt=A tt=25 DEFAULT_TOKEN_CHANNEL
) )
( simpleFilter
( STR i=1 txt=B tt=25 DEFAULT_TOKEN_CHANNEL
) ) )
这里有一些修正语法的建议。我进行了一些更改here。
-
您应该养成查看来自 Antlr 工具的警告消息并解决它们的习惯。 Antlr Java tool 警告
...Filter.g4:76:0: One of the token AND_MUST values unreachable. AND is always overlapped by token AND
。规则AND_MUST: 'AND';
永远不会匹配,因为词法分析器总是匹配第一条规则。由于规则AND
和AND_MUST
匹配同一个字符串,并且AND
出现在AND_MUST
之前,所以它会一直匹配,而AND_MUST
永远不会匹配。您应该删除AND_MUST
规则。 (a) Antlr 在创建令牌时尝试匹配尽可能多的输入字符; (b) 当两个或多个词法分析器规则可以匹配相同的输入时,第一个定义的获胜。 -
Antlr 解析输入字符串,直到满足规则。如果部分字符串有效,它将这样做并且不会抱怨输入的其余部分。来自
booleanAndExpression
的解析(未用EOF
扩充)将解析输入“A B”中的“A”并返回有效解析。为了防止这种行为,请始终使用 EOF 增强规则进行解析,例如filter
。原来的filter
规则允许部分输入匹配,所以我用括号调整了规则(见 link)。 -
当我阅读
logicalExpression
和booleanAndExpression
的规则时,看起来您正在使用因式分解来设置 OR 和 AND 运算符的优先级。但是,稍后,您将 alt-rule precedence 用于 EQ、NE、GT、GE 和其他运算符。看起来您从两种不同的语法中复制了表达式规则,并尝试将它们组合起来以达到您的目的。您应该选择一种方式或另一种方式来为您的运营商设置优先级。因式分解更容易控制,但 alt 规则优先级会导致树更小,解析速度更快。 -
正如我之前提到的,规则
booleanAndExpression : simpleFilter ((AND)? simpleFilter )* ;
是错误的。如果您使用AND?
,那么句子形式simpleFilter simpleFilter
是有效的,因此A B
将是有效的。我在修改后的语法中对此进行了更改,但您可以通过修改我提供的更新语法(AND)?
或仅AND?
并重新运行来检查行为。 -
规则
simpleFilter
包含一个错误:它必须包含STR
的基本情况。 -
您在整个语法中使用 Antlr 标签。我不建议你这样做。它使语法变得混乱并使其无法移植到其他解析生成器。要区分一个 alt 和另一个 alt,请使用该工具生成的 Antlr 访问器函数。例如,要区分第一条规则中的“(A)”与“A”,请检查“context.LPAREN()”是否不为空。
-
不要在词法分析器规则中使用“->跳过”,而是使用频道外的“->频道(OFF_CHANNEL)”。这允许错误消息避免“运行”,例如,它会显示为
line 1:2 no viable alternative at input 'AB'
而不是line 1:2 no viable alternative at input 'A B'
。但是,这需要您将组合语法拆分为单独的词法分析器和解析器语法,此时可能不需要。 -
在我的帖子之后发布了另一个“答案”,建议在语法中添加错误产生式。我不建议你这样做。相反,如果您想要更好的错误消息,您应该修改解析的错误报告器。你可以看看我提供的例子。 CFG 应该定义语言。它不应该因为实现细节而被歪曲。
问候, 肯
,-
标题中缺少“投诉”,应重新措辞。
-
无法生成发布的语法:
warning(184): Question_OP.g4:75:0: One of the token AND_MUST values unreachable. AND is always overlapped by token AND
-
注释掉
AND_MUST
后,给定输入A and B
:
$ grun Question_OP filter -tokens input1.txt
[@0,<STR>,1:0]
[@1,<AND>,1:2]
[@2,1:6]
[@3,8:7='<EOF>',<EOF>,2:0]
line 1:2 no viable alternative at input 'Aand'
line 2:0 no viable alternative at input 'B'
这是正常的:解析器指向令牌 A
并搜索可以匹配此令牌的规则:filter -> logicalExpression -> booleanAndExpression -> simpleFilter :
simpleFilter
: NOT simpleFilter
| key=STR op=EQ value=(STR | DECIMAL)
...
| DOLLAR LCURLY FILTER SEP id=(DECIMAL | STR) SEP name=STR RCURLY
;
simpleFilter
无法匹配 A
,因为没有替代匹配单个令牌 STR
。
- 不要发布 70% 与问题无关的庞大语法,而是将其缩小到研究问题所需的严格最低限度,例如
grammar Filter;
filter
: LPAREN logicalExpression RPAREN #ParenthesizedFilter
| logicalExpression EOF #CompositeFilterHash;
logicalExpression
: booleanAndExpression ( OR booleanAndExpression )* #LogExp
;
booleanAndExpression
: simpleFilter ((AND) ? simpleFilter )* #SimF
;
simpleFilter
: NOT simpleFilter #NegateFilter
| key=STR op=EQ value=(STR | DECIMAL) #EqualsFilter
;
LPAREN : '(' ;
RPAREN : ')' ;
EQ : '=' ;
NOT : '!' | 'not' | 'NOT' ;
AND : 'AND' | 'and' ;
OR : 'OR' | 'or' ;
DECIMAL: '-'? DIGIT+ ( '.' DIGIT+ )? ;
STR : ALPHANUMERIC+ | '"' (ALPHANUMERIC | [ ] | '""')+ '"' ;
WS : [ \t\r\n\f]+ -> skip ;
fragment ALPHANUMERIC: [a-z0-9A-Z!.:@#$%&^*'+/?_`~-];
fragment DIGIT: [0-9] ;
输出:
$ grun Filter filter -tokens input1.txt
[@0,2:0]
line 1:2 mismatched input 'and' expecting '='
line 2:0 mismatched input '<EOF>' expecting '='
$ cat input1.txt
A and B
- 现在让我们改变 simpleFilter 以接受字母:
simpleFilter
: STR
;
在文件 input.txt
中输入以下内容:
A and B
A B
现在可以正确解析:
$ grun Filter filter -tokens -gui input.txt
[@0,8:8='A',2:0]
[@4,10:10='B',2:2]
[@5,12:11='<EOF>',3:0]
解析器没有理由抱怨,因为输入符合语法。作为语法设计者,您必须编写规则以拒绝无效输入,例如:
grammar Question;
prog
: ( line NL )+ EOF
;
line
: expr {System.out.println("Found expression `" + $expr.text + "`");}
| a=ID b=ID {System.out.println("Hey user,you forgot the `and` between " + $a.text + " and " + $b.text + " !");}
;
expr
: NOT expr # ExprNot
| expr AND expr # ExprAnd
| expr OR expr # ExprOr
| simpleFilter # SimF
| LPAREN expr RPAREN # ParenthesizedExpression
;
simpleFilter
: ID
| STRING
;
LPAREN : '(' ;
RPAREN : ')' ;
NOT : '!' | 'not' | 'NOT' ;
AND : 'AND' | 'and' ;
OR : 'OR' | 'or' ;
ID : [a-zA-Z_]+ ;
STRING : '"' .*? '"' ;
WS : [ \t]+ -> channel(HIDDEN) ;
NL : [\r\n\f]+ ;
使用文件 input.txt
:
A and B
A B
C or "xyz"
执行:
$ export CLASSPATH=".:/usr/local/lib/antlr-4.9-complete.jar"
$ alias a4='java -jar /usr/local/lib/antlr-4.9-complete.jar'
$ alias grun='java org.antlr.v4.gui.TestRig'
$ a4 Question.g4
$ javac Question*.java
$ grun Question prog -tokens input.txt
[@0,<ID>,1:1=' ',<WS>,channel=1,1:1]
[@2,1:2]
[@3,5:5=' ',1:5]
[@4,1:6]
[@5,7:7='\n',<NL>,1:7]
[@6,2:0]
[@7,9:9=' ',2:1]
[@8,2:2]
[@9,11:11='\n',2:3]
[@10,12:12='C',3:0]
[@11,13:13=' ',3:1]
[@12,14:15='or',<OR>,3:2]
[@13,16:16=' ',3:4]
[@14,17:21='"xyz"',<STRING>,3:5]
[@15,22:22='\n',3:10]
[@16,23:22='<EOF>',4:0]
Found expression `A and B`
Hey user,you forgot the `and` between A and B !
Found expression `C or "xyz"`
你已经定义的事实
AND_MUST: 'AND';
让我相信您不会像解析器那样进行推理,而是希望解析器的行为就像您认为它应该做的那样!
请耐心等待,ANTLR 并不容易。 This answer 花了我大约一个星期,我不得不做很多研究!阅读文档,The Book、documentation、the tutorial。