在ANTLR4词法分析器中将无效字符视为单个标记

时间:2016-03-28 20:32:33

标签: java antlr4

我使用JSON grammar from the antlr4 grammar repository来解析编辑器插件的JSON文件。它有效,但逐个报告无效字符。以下代码段导致18个词法分析器错误:

{
   sometext-without-quotes : 42
}

我希望通过处理与一个较大的无效令牌相同类型的连续无效单字符令牌将其归结为1-2。

对于一个类似的问题,一个自定义词法分析器被建议粘合"未知"元素到更大的标记:In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?

我认为这会绕过通常的词法错误报告,如果可能的话我想避免这种报告。对于那个相当简单的任务,还没有合适的解决方案吗?它似乎默认在ANTLR3中有效。

2 个答案:

答案 0 :(得分:3)

答案在您提供的链接中。我不想完全复制原始答案,所以我会试着解释一下......

In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?

在词法分析器中添加未知数,以匹配这些的倍数...

unknowns : Unknown+ ; 
...
Unknown  : . ; 

对此帖进行了编辑,以满足您只使用词法分析器而不使用解析器的情况。如果使用解析器,则不需要覆盖nextToken方法,因为错误可以在解析器中以更清晰的方式处理,即unknowns只是词法分析器的另一种令牌类型。词法分析器将这些传递给解析器,然后解析器可以处理错误。如果使用解析器,我通常会将所有令牌识别为单独的令牌,然后在解析器中发出错误,即将它们分组或不分组。这样做的原因是所有错误处理都在一个地方完成,即它不在词法分析器和解析器中。它还使得词法分析器更易于编写和测试,即它必须识别所有文本,并且永远不会在任何utf8输入上失败。有些人可能会采用不同的方式,但这对我来说在C中用手写的词法分析器有效。解析器负责确定实际有效的内容以及如何对其进行错误。另一个好处是词法分析器非常通用,可以重复使用。

仅限lexer解决方案......

检查链接上的答案并在答案中查找此评论...

  

...但我只有一个词法分析器,没有解析器......

答案说明你覆盖了nextToken方法,并详细介绍了如何做到这一点

@Override
public Token nextToken() {    

代码中的重要部分是......

Token next = super.nextToken();

if(next.getType() != Unknown) {
  return next;
}

此后的代码处理您可以匹配坏令牌的情况。

答案 1 :(得分:2)

你可以做的是使用词法模式。为此你必须将语法分解为解析器和词法分析器语法。让我们从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);

基本上我添加了解析器部分中使用的一些标记(如LCURLCOLCOMA等)并引入了NON_VALID_STRING标记,这基本上是第一个字符那些已经(应该)匹配的东西都没有。检测到此令牌后,我将词法分析器切换到MODE_ERR模式。在这种模式下,一旦检测到:,我就会回到默认模式(这可以改变,也许可以改进,但服务器的目的就在这里:))或者我说其他一切都是MY_ERR_TOKEN我指定的NON_VALID_STRING令牌类型。当我用你的输入运行解释lexer选项时,ATNLRWorks对此说了些什么: ANTLRWorks lexer interpret

所以sNON_VALID_STRING类型,所有其他内容都是:。所以,相同类型但两个不同的令牌。如果您希望它们不是同一类型,只需省略词法分析器语法中的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
   ;

如果您运行测试装备(我使用ANTLRworks),您将收到一个错误(参见屏幕截图)antlworks parser


你也可以通过覆盖生成的lexer类来累积词法错误,但我在问题中理解这不是所希望的,或者我不理解那部分:)