这个 ANTLR 语法有什么问题?
What's wrong with this ANTLR grammar?
我想解析如下所示的查询表达式:
Person Name=%John%
(Person Name=John% and Address=%Ontario%)
Person Fullname_3="John C. Smith"
但我对 Antlr4 完全陌生,甚至不知道如何解析一个 TABLE FIELD=QUERY 子句。当我 运行 Go 中下面的语法作为目标时,我得到
line 1:7 mismatched input 'Name' expecting {'not', '(', FIELDNAME}
对于像
这样的简单查询
Person Name=John
为什么 Grammar 不能通过解析 fieldsearch->field EQ searchterm->FIELDNAME 来解析 FIELDNAME?
我想我误解了一些关于 Antlr 语法如何工作的非常基本的东西,但是什么?
/* ANTLR Grammar for Minidb Query Language */
grammar Mdb;
start : searchclause EOF ;
searchclause
: table expr
;
expr
: fieldsearch
| unop fieldsearch
| LPAREN expr relop expr RPAREN
;
unop
: NOT
;
relop
: AND
| OR
;
fieldsearch
: field EQ searchterm
;
field
: FIELDNAME
;
table
: TABLENAME
;
searchterm
: STRING
;
AND
: 'and'
;
OR
: 'or'
;
NOT
: 'not'
;
EQ
: '='
;
LPAREN
: '('
;
RPAREN
: ')'
;
fragment VALID_ID_START
: ('a' .. 'z') | ('A' .. 'Z') | '_'
;
fragment VALID_ID_CHAR
: VALID_ID_START | ('0' .. '9')
;
TABLENAME
: VALID_ID_START VALID_ID_CHAR*
;
FIELDNAME
: VALID_ID_START VALID_ID_CHAR*
;
STRING: '"' ~('\n'|'"')* ('"' | { panic("syntax-error - unterminated string literal") } ) ;
WS
: [ \r\n\t] + -> skip
;
尝试使用 grun Mdb tokens -tokens
查看为该输入生成的标记。它会告诉您输入由两个 table 名称、一个等号和另一个 table 名称组成。为了匹配您的语法,它需要是一个 table 名称、一个 字段名称 、一个等号和一个 字符串 .
第一个问题是 TABLENAME
和 FIELDNAME
具有完全相同的定义。如果两个词法分析器规则会在当前输入上产生相同长度的匹配项,ANTLR 会优先选择语法中排在第一位的规则。所以它永远不会产生 FIELDNAME
令牌。要解决这个问题,只需将这两个规则替换为一个 ID
规则即可。如果你愿意,你可以引入解析器规则 tableName : ID ;
和 fieldName : ID ;
如果你想保留名称。
另一个问题更直接:John
根本不符合您的字符串规则,因为它不在引号中。如果您确实希望允许 John
作为有效搜索词,您可能希望将其定义为 searchterm : STRING | ID ;
而不是仅允许 STRING
s.
我想解析如下所示的查询表达式:
Person Name=%John%
(Person Name=John% and Address=%Ontario%)
Person Fullname_3="John C. Smith"
但我对 Antlr4 完全陌生,甚至不知道如何解析一个 TABLE FIELD=QUERY 子句。当我 运行 Go 中下面的语法作为目标时,我得到
line 1:7 mismatched input 'Name' expecting {'not', '(', FIELDNAME}
对于像
这样的简单查询Person Name=John
为什么 Grammar 不能通过解析 fieldsearch->field EQ searchterm->FIELDNAME 来解析 FIELDNAME?
我想我误解了一些关于 Antlr 语法如何工作的非常基本的东西,但是什么?
/* ANTLR Grammar for Minidb Query Language */
grammar Mdb;
start : searchclause EOF ;
searchclause
: table expr
;
expr
: fieldsearch
| unop fieldsearch
| LPAREN expr relop expr RPAREN
;
unop
: NOT
;
relop
: AND
| OR
;
fieldsearch
: field EQ searchterm
;
field
: FIELDNAME
;
table
: TABLENAME
;
searchterm
: STRING
;
AND
: 'and'
;
OR
: 'or'
;
NOT
: 'not'
;
EQ
: '='
;
LPAREN
: '('
;
RPAREN
: ')'
;
fragment VALID_ID_START
: ('a' .. 'z') | ('A' .. 'Z') | '_'
;
fragment VALID_ID_CHAR
: VALID_ID_START | ('0' .. '9')
;
TABLENAME
: VALID_ID_START VALID_ID_CHAR*
;
FIELDNAME
: VALID_ID_START VALID_ID_CHAR*
;
STRING: '"' ~('\n'|'"')* ('"' | { panic("syntax-error - unterminated string literal") } ) ;
WS
: [ \r\n\t] + -> skip
;
尝试使用 grun Mdb tokens -tokens
查看为该输入生成的标记。它会告诉您输入由两个 table 名称、一个等号和另一个 table 名称组成。为了匹配您的语法,它需要是一个 table 名称、一个 字段名称 、一个等号和一个 字符串 .
第一个问题是 TABLENAME
和 FIELDNAME
具有完全相同的定义。如果两个词法分析器规则会在当前输入上产生相同长度的匹配项,ANTLR 会优先选择语法中排在第一位的规则。所以它永远不会产生 FIELDNAME
令牌。要解决这个问题,只需将这两个规则替换为一个 ID
规则即可。如果你愿意,你可以引入解析器规则 tableName : ID ;
和 fieldName : ID ;
如果你想保留名称。
另一个问题更直接:John
根本不符合您的字符串规则,因为它不在引号中。如果您确实希望允许 John
作为有效搜索词,您可能希望将其定义为 searchterm : STRING | ID ;
而不是仅允许 STRING
s.