Java 在ANTLR4 lexer中将无效字符视为单个令牌

Java 在ANTLR4 lexer中将无效字符视为单个令牌,java,antlr4,Java,Antlr4,我正在使用来解析编辑器插件的JSON文件。它可以工作,但会逐个报告无效字符。以下代码段导致18个lexer错误: { sometext-without-quotes : 42 } 我想将其归结为1-2,将同一类型的连续、无效的单字符标记与一个更大的无效标记进行处理 对于类似的问题,有人建议使用自定义lexer将“未知”元素粘贴到较大的标记上: 我假设这绕过了通常的lexer错误报告,如果可能的话,我希望避免这种情况。对于这个相当简单的任务,难道没有合适的解决方案吗?在ANTLR3中,默认

我正在使用来解析编辑器插件的JSON文件。它可以工作,但会逐个报告无效字符。以下代码段导致18个lexer错误:

{
   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我希望有一个简单的解决方案(问题很简单),但似乎没有。谢谢你的确认。