修复 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 个标记:
DEFINE
标记,带有文本 #define_
(注意 _
是 define
之后的 space!)
VARNAME
令牌,带有文本 foobar
如果你标记 #definefoobar
,你还会得到 2 个标记:
DEFINE
令牌,带有文本 #definef
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。
所以, 我正在将一个使用词法分析器读取 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 个标记:
DEFINE
标记,带有文本#define_
(注意_
是define
之后的 space!)VARNAME
令牌,带有文本foobar
如果你标记 #definefoobar
,你还会得到 2 个标记:
DEFINE
令牌,带有文本#definef
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。