在 lexer/parser 中删除所需的引号

Drop the required surrounding quotes in the lexer/parser

我有几个项目 运行 在我的语法中产生了类似的效果。

我需要解析类似 Key="Value"

的内容

所以我创建了一个语法(为了显示效果我能做的最简单的):

grammar test;

KEY   : [a-zA-Z0-9]+ ;
VALUE : DOUBLEQUOTE [ _a-zA-Z0-9.-]+ DOUBLEQUOTE ;

DOUBLEQUOTE     : '"'           ;
EQUALS          : '='           ;

entry    : key=KEY EQUALS value=VALUE;

我现在可以解析 thing="One Two Three" 并且在我的代码中收到

在我所有的项目中,我最终都需要一个额外的步骤来从值中删除那些 "

通常是这样的(我用Java)

String value = ctx.value.getText();
value = value.substring(1, value.length()-1);

在我的实际语法中,我发现很难将周围 " 的检查移动到解析器中。

是否有一种干净的方法可以通过在 lexer/parser 中做一些事情来删除 "

基本上我想要 ctx.value.getText() 到 return One Two Three 而不是 "One Two Three".


更新:

我一直在研究 Bart Kiers 提供的出色答案,发现这个变体完全符合我的要求。 通过将 DOUBLEQUOTE 放在隐藏的通道上,它们被词法分析器使用并从解析器中隐藏。

TestLexer.g4

lexer grammar TestLexer;

KEY         : [a-zA-Z0-9]+;
DOUBLEQUOTE : '"' -> channel(HIDDEN), pushMode(STRING_MODE);
EQUALS      : '=';

mode STRING_MODE;

  STRING_DOUBLEQUOTE
   : '"' -> channel(HIDDEN), type(DOUBLEQUOTE), popMode
   ;

  STRING
   : [ _a-zA-Z0-9.-]+
   ;

TestParser.g4

parser grammar TestParser;

options { tokenVocab=TestLexer; }

entry : key=KEY EQUALS value=STRING ;

试试这个:

VALUE
 : DOUBLEQUOTE [ _a-zA-Z0-9.-]+ DOUBLEQUOTE 
   {setText(getText().substring(1, getText().length()-1));}
 ;

不用说:这将您的语法与 Java 联系在一起,并且(取决于您拥有多少嵌入式 Java 代码)您的语法将很难移植到其他目标语言。

编辑

一旦创建了令牌,就没有 built-in 方法将其分开(除了在嵌入式操作中这样做,正如我所演示的那样)。您正在寻找的 可以 完成,但这意味着重写您的语法,以便字符串文字不会构造为单个标记。这可以通过使用 lexical modes 来完成,以便可以在解析器中构造字符串。

快速演示:

TestLexer.g4

lexer grammar TestLexer;

KEY         : [a-zA-Z0-9]+;
DOUBLEQUOTE : '"' -> pushMode(STRING_MODE);
EQUALS      : '=';

mode STRING_MODE;

  STRING_DOUBLEQUOTE
   : '"' -> type(DOUBLEQUOTE), popMode
   ;

  STRING_ATOM
   : [ _a-zA-Z0-9.-]
   ;

TestParser.g4

parser grammar TestParser;

options { tokenVocab=TestLexer; }

entry : key=KEY EQUALS value;

value : DOUBLEQUOTE string_atoms DOUBLEQUOTE;

string_atoms : STRING_ATOM*;

如果您现在 运行 Java 代码:

Lexer lexer = new TestLexer(CharStreams.fromString("Key=\"One Two Three\""));
TestParser parser = new TestParser(new CommonTokenStream(lexer));

TestParser.EntryContext entry = parser.entry();
System.out.println(entry.value().string_atoms().getText());

这将被打印:

One Two Three