如果第二个操作数存在,则用于制作布尔运算符的 Antlr 语法必须

问题描述

如果用户输入 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”,更不用说从 booleanAndExpressionfilter 开始的“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

  1. 您应该养成查看来自 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'; 永远不会匹配,因为词法分析器总是匹配第一条规则。由于规则ANDAND_MUST 匹配同一个字符串,并且AND 出现在AND_MUST 之前,所以它会一直匹配,而AND_MUST 永远不会匹配。您应该删除 AND_MUST 规则。 (a) Antlr 在创建令牌时尝试匹配尽可能多的输入字符; (b) 当两个或多个词法分析器规则可以匹配相同的输入时,第一个定义的获胜。

  2. Antlr 解析输入字符串,直到满足规则。如果部分字符串有效,它将这样做并且不会抱怨输入的其余部分。来自 booleanAndExpression 的解析(未用 EOF 扩充)将解析输入“A B”中的“A”并返回有效解析。为了防止这种行为,请始终使用 EOF 增强规则进行解析,例如 filter。原来的 filter 规则允许部分输入匹配,所以我用括号调整了规则(见 link)。

  3. 当我阅读 logicalExpressionbooleanAndExpression 的规则时,看起来您正在使用因式分解来设置 OR 和 AND 运算符的优先级。但是,稍后,您将 alt-rule precedence 用于 EQ、NE、GT、GE 和其他运算符。看起来您从两种不同的语法中复制了表达式规则,并尝试将它们组合起来以达到您的目的。您应该选择一种方式或另一种方式来为您的运营商设置优先级。因式分解更容易控制,但 alt 规则优先级会导致树更小,解析速度更快。

  4. 正如我之前提到的,规则 booleanAndExpression : simpleFilter ((AND)? simpleFilter )* ; 是错误的。如果您使用 AND?,那么句子形式 simpleFilter simpleFilter 是有效的,因此 A B 将是有效的。我在修改后的语法中对此进行了更改,但您可以通过修改我提供的更新语法 (AND)? 或仅 AND? 并重新运行来检查行为。

  5. 规则 simpleFilter 包含一个错误:它必须包含 STR 的基本情况。

  6. 您在整个语法中使用 Antlr 标签。我不建议你这样做。它使语法变得混乱并使其无法移植到其他解析生成器。要区分一个 alt 和另一个 alt,请使用该工具生成的 Antlr 访问器函数。例如,要区分第一条规则中的“(A)”与“A”,请检查“context.LPAREN()”是否不为空。

  7. 不要在词法分析器规则中使用“->跳过”,而是使用频道外的“->频道(OFF_CHANNEL)”。这允许错误消息避免“运行”,例如,它会显示为 line 1:2 no viable alternative at input 'AB' 而不是 line 1:2 no viable alternative at input 'A B'。但是,这需要您将组合语法拆分为单独的词法分析器和解析器语法,此时可能不需要。

  8. 在我的帖子之后发布了另一个“答案”,建议在语法中添加错误产生式。我不建议你这样做。相反,如果您想要更好的错误消息,您应该修改解析的错误报告器。你可以看看我提供的例子。 CFG 应该定义语言。它不应该因为实现细节而被歪曲。

问候, 肯

,
  1. 标题中缺少“投诉”,应重新措辞。

  2. 无法生成发布的语法:

    warning(184): Question_OP.g4:75:0: One of the token AND_MUST values unreachable. AND is always overlapped by token AND

  3. 注释掉 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

  1. 不要发布 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
  1. 现在让我们改变 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]

Parsing A and B A B

解析器没有理由抱怨,因为输入符合语法。作为语法设计者,您必须编写规则以拒绝无效输入,例如:

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 Bookdocumentationthe tutorial