Java 在ANTLR4 lexer中将无效字符视为单个令牌
我正在使用来解析编辑器插件的JSON文件。它可以工作,但会逐个报告无效字符。以下代码段导致18个lexer错误:Java 在ANTLR4 lexer中将无效字符视为单个令牌,java,antlr4,Java,Antlr4,我正在使用来解析编辑器插件的JSON文件。它可以工作,但会逐个报告无效字符。以下代码段导致18个lexer错误: { sometext-without-quotes : 42 } 我想将其归结为1-2,将同一类型的连续、无效的单字符标记与一个更大的无效标记进行处理 对于类似的问题,有人建议使用自定义lexer将“未知”元素粘贴到较大的标记上: 我假设这绕过了通常的lexer错误报告,如果可能的话,我希望避免这种情况。对于这个相当简单的任务,难道没有合适的解决方案吗?在ANTLR3中,默认
{
sometext-without-quotes : 42
}
我想将其归结为1-2,将同一类型的连续、无效的单字符标记与一个更大的无效标记进行处理
对于类似的问题,有人建议使用自定义lexer将“未知”元素粘贴到较大的标记上:
我假设这绕过了通常的lexer错误报告,如果可能的话,我希望避免这种情况。对于这个相当简单的任务,难道没有合适的解决方案吗?在ANTLR3中,默认情况下它似乎起作用。答案在您提供的链接中。我不想完全照搬原来的答案,所以我会试着解释一下 添加未知的lexer,将匹配这些的倍数
unknowns : Unknown+ ;
...
Unknown : . ;
对这篇文章进行了编辑,以适应只使用lexer而不使用解析器的情况。如果使用解析器,则不需要重写nextToken
方法,因为可以以更干净的方式在解析器中处理错误,即未知
就词法分析器而言只是另一种令牌类型。lexer将这些信息传递给解析器,解析器随后可以处理错误。如果使用解析器,我通常会将所有令牌识别为单个令牌,然后在解析器中发出错误,即是否将它们分组。这样做的原因是所有的错误处理都是在一个地方完成的,即它不在lexer和解析器中。它还使lexer更易于编写和测试,即它必须识别所有文本,并且在任何utf8输入时都不会失败。有些人可能会采取不同的做法,但这对我使用C语言手工编写的词法分析器是有效的。解析器负责确定什么是真正有效的,以及如何在其上出错。另一个好处是lexer相当通用,可以重用
对于lexer唯一的解决方案
检查链接上的答案,并在答案中查找此评论
。。。但我只有一个lexer,没有解析器
答案说明您重写了nextToken
方法,并详细介绍了如何执行该操作
@Override
public Token nextToken() {
代码中最重要的部分是
Token next = super.nextToken();
if(next.getType() != Unknown) {
return next;
}
在此之后的代码处理您可以匹配坏令牌的情况。您可以使用lexer模式。为此,您必须将语法拆分为解析器语法和词法分析器语法。让我们从lexer语法开始: JSONLexer.g4
/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */
// Derived from http://json.org
lexer grammar JSONLexer;
STRING
: '"' (ESC | ~ ["\\])* '"'
;
fragment ESC
: '\\' (["\\/bfnrt] | UNICODE)
;
fragment UNICODE
: 'u' HEX HEX HEX HEX
;
fragment HEX
: [0-9a-fA-F]
;
NUMBER
: '-'? INT '.' [0-9] + EXP? | '-'? INT EXP | '-'? INT
;
fragment INT
: '0' | [1-9] [0-9]*
;
// no leading zeros
fragment EXP
: [Ee] [+\-]? INT
;
// \- since - means "range" inside [...]
TRUE : 'true';
FALSE : 'false';
NULL : 'null';
LCURL : '{';
RCURL : '}';
COL : ':';
COMA : ',';
LBRACK : '[';
RBRACK : ']';
WS
: [ \t\n\r] + -> skip
;
NON_VALID_STRING : . ->pushMode(MODE_ERR);
mode MODE_ERR;
WS1
: [ \t\n\r] + -> skip
;
COL1 : ':' ->popMode;
MY_ERR_TOKEN : ~[':']* ->type(NON_VALID_STRING);
基本上,我添加了一些用于解析器部分的标记(如LCURL
,COL
,COMA
等),并引入了无效字符串
标记,它基本上是第一个已经(应该)匹配的字符。一旦检测到这个令牌,我将lexer切换到模式\u ERR
模式。在这种模式下,一旦检测到:
,我就返回默认模式(这可以更改,也可以改进,但这里的目的是:),或者我说其他所有内容都是我分配给无效字符串的令牌类型。以下是ATNLRWorks在我使用您的输入运行Explorate lexer选项时对此的说明:
因此,s
是无效字符串
类型,在:
之前的所有内容也是如此。因此,相同类型但两个不同的标记。如果希望它们不是同一类型,只需省略lexer语法中的type
调用即可。
下面是语法分析器
JSONParser.g4
/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */
// Derived from http://json.org
parser grammar JSONParser;
options {
tokenVocab=JSONLexer;
}
json
: object
| array
;
object
: LCURL pair (COMA pair)* RCURL
| LCURL RCURL
;
pair
: STRING COL value
;
array
: LBRACK value (COMA value)* RBRACK
| LBRACK RBRACK
;
value
: STRING
| NUMBER
| object
| array
| TRUE
| FALSE
| NULL
;
如果您运行测试装备(我使用AntlWorks),您将得到一个错误(参见屏幕截图)
您还可以通过重写生成的lexer类来累积lexer错误,但我在问题中理解这不是所需的,或者我不理解这一部分:)是的,您是对的。谢谢你的详细解释。我希望有一个简单的解决方案(问题很简单),但看起来没有+1我希望有一个简单的解决方案(问题很简单),但似乎没有。谢谢你的确认。