修复 Antlr4 中 Antlr3 语法的“不允许多字符文字”错误

Fixing “multi-character literals are not allowed” error from Antlr3 grammar in Antlr4

所以, 我正在将一个使用词法分析器读取 C++ 头文件的 Antlr3 项目转换为 Antlr4。 我在将特定行转换为新的 Antlr4 语法时遇到问题。

Antlr3 语法的原始行是:

DEFINE                  :   '#define' ~(' WM_NEWUSER (WM_USER + 2)');

使用它会产生

multi-character literals are not allowed in lexer sets: 'WM_NEWUSER' error in the antlr4 lexer.

我尝试按照 中所述包装多字符文字 但它没有用,或者我做错了什么。

编辑:原始语法

grammar Grammar;

@lexer::header {package main;}

WS                      :   (' '|'\t')*;
NEW_LINE                :   '\r'? '\n';
IGNORE_LINES            :   '//@MySQL:IGNORE LINES:' ('0'..'9')+ (' '|'\t')* '\r'? '\n';
IGNORE_LINES2           :   '/*@MySQL:IGNORE LINES:' ('0'..'9')+ (' '|'\t'|'\r'|'\n')* '*/';
IGNORE_KEY              :   '//@MySQL:IGNORE KEY' (' '|'\t')* '\r'? '\n';
IGNORE_KEY2             :   '/*@MySQL:IGNORE KEY' (' '|'\t'|'\r'|'\n')* '*/';
IGNORE_STRUCT           :   '//@MySQL:IGNORE STRUCT' (' '|'\t')* '\r'? '\n';
IGNORE_STRUCT2          :   '/*@MySQL:IGNORE STRUCT' (' '|'\t'|'\r'|'\n')* '*/';
PRIMARY                 :   '//@MySQL:PRIMARY' (' '|'\t')* '\r'? '\n';
PRIMARY2                :   '/*@MySQL:PRIMARY' (' '|'\t'|'\r'|'\n')* '*/';
OPKZ_ZUORD              :   '//@MySQL:OPKZ:' (VARNAME ';')+ (' '|'\t')* '\r'? '\n';
OPKZ_ZUORD2             :   '/*@MySQL:OPKZ:' (VARNAME ';')+ (' '|'\t'|'\r'|'\n')* '*/';
DESC_KEY                :   '//@MySQL:DESC' (' '|'\t')* '\r'? '\n';
DESC_KEY2               :   '/*@MySQL:DESC' (' '|'\t'|'\r'|'\n')* '*/';
MYSQL_KEY_INFO_MULTI    :   '//@MySQL:MULTIKEY:' .* '\n';
MYSQL_KEY_INFO_MULTI2   :   '/*@MySQL:MULTIKEY:' .* '*/';
MYSQL_KEY_INFO_SPECIAL  :   '//@MySQL:SPECIALKEY:' .* '\n';
MYSQL_KEY_INFO_SPECIAL2 :   '/*@MySQL:SPECIALKEY:' .* '*/';
MYSQL_KEY_INSENSITIVE   :   '//@MySQL:INSENSITIVEKEY:' .* '\n';
COMMENT                 :   '/*' .* '*/';
LINE_COMMENT            :   '//' ~('\n'|'\r')* '\r'? '\n';
SIGNED_UNSIGNED         :   'signed'|'unsigned';
TYPE                    :   ('char'|'short'|'int'|'long'|'__int8'|'__int16'|'__int32'|'__int64'|'bool'|'float'|'double');
RESERVE                 :   'reserve'|'szReserve';
TYPEDEF                 :   'typedef';
ENUM                    :   'enum';
STRUCT                  :   'struct';
UNION                   :   'union';
CONST                   :   'const';
DEFINE                  :   '#define' ~(' WM_NEWUSER (WM_USER + 2)');
WM_USER_PLUS_2          :   '#define WM_NEWUSER (WM_USER + 2)' (' '|'\t')* '\r'? '\n';
BRACKET_OPEN            :   '(';
BRACKET_CLOSE           :   ')';
CURLY_BRACE_OPEN        :   '{';
CURLY_BRACE_CLOSE       :   '}';
SQUARE_BRACKET_OPEN     :   '[';
SQUARE_BRACKET_CLOSE    :   ']';
SEMI                    :   ';';
PLUS                    :   '+';
MINUS                   :   '-';
EQUALS                  :   '=';
MAL                     :   '*';
BACKSLASH               :   '\';
KOMMA                   :   ',';
NUMBER                  :   (('0'..'9')+)|(('0x') (('0'..'9')|('a'..'f')|('A'..'F'))+)|('\'' '\'? ('a'..'z'|'A'..'Z') '\'');
VARNAME                 :       ('a'..'z' | 'A'..'Z' | '_' ) ('0'..'9' | 'a'..'z' | 'A'..'Z' | '_' )*;
VERODERT                :   '(' (' '|'\t')* VARNAME (' '|'\t')* ('|' (' '|'\t')* VARNAME (' '|'\t')* )* ')';
PRAGMA_ONCE             :   '#pragma' (' '|'\t')+ 'once';
IF_NOT_DEFINED1         :   '#if' (' '|'\t')+ '!' (' '|'\t')* 'defined' (' '|'\t')* VARNAME (' '|'\t')* '\r'? '\n';
IF_DEFINED1             :   '#if' (' '|'\t')+ 'defined' (' '|'\t')* VARNAME (' '|'\t')* '\r'? '\n';
IF_NOT_DEFINED2         :   '#if' (' '|'\t')+ '!' 'defined' (' '|'\t')* '(' (' '|'\t')* VARNAME (' '|'\t')* ')' '\r'? '\n';
IF_DEFINED2             :   '#if' (' '|'\t')+ 'defined' (' '|'\t')* '(' (' '|'\t')* VARNAME (' '|'\t')* ')' '\r'? '\n';
ENDIF                   :   '#endif';

有什么提示可以解决我的问题吗?

' WM_NEWUSER (WM_USER + 2)' 的否定在 ANTLR 3 中或多或少有未定义的行为。

在词法分析器规则中,~ 否定字符 类。并且总是匹配单个字符。它不能否定整个字符串 ' WM_NEWUSER (WM_USER + 2)'.

自己用输入进行测试:#define foobar。将有 2 个标记:

  1. DEFINE 标记,带有文本 #define_(注意 _define 之后的 space!)
  2. VARNAME 令牌,带有文本 foobar

如果你标记 #definefoobar,你还会得到 2 个标记:

  1. DEFINE 令牌,带有文本 #definef
  2. VARNAME 令牌,带有文本 oobar

如您所见,'#define' 之后的否定部分将始终匹配单个字符。

因为被否定的不是正确的字符集,你不妨这样写规则:

DEFINE : '#define' .;

是的,这将与以下行为相同:

DEFINE : '#define' ~(' WM_NEWUSER (WM_USER + 2)');

其他一些观察结果:

  • 如果 ANTLR3 的词法分析器无法在 "late stage" 处创建令牌,则它在回溯方面表现不佳。尝试标记化 #define WM_NEWUSER (WM_USER + 23)。当它偶然发现 3 时,它将不得不放弃规则 WM_USER_PLUS_2,但是它无法为它已经使用的字符找到另一个词法分析器规则,并且会产生错误。
  • 我在你的词法分析器中看到很多 .* '\n':这是一个坏习惯,尽可能避免 .*。使用 ~('\n')* '\n' 代替
  • WS 规则匹配一个空字符串,这是词法分析器规则的禁忌(空字符串的数量是无限的,可能会导致你的词法分析器在运行时停止运行)
  • 你的 LINE_COMMENT 强制在末尾有一个换行符。当您输入的末尾有行注释(末尾没有换行符)时,这将失败

我的建议:放弃 v3 语法,要么从头开始,要么尝试找到适合您需要的open source grammar