【问题标题】:antlr grammar: Allow whitespace matching only in template stringantlr 语法:仅在模板字符串中允许空格匹配
【发布时间】:2019-04-19 03:41:30
【问题描述】:

我要解析模板字符串:

`Some text ${variable.name} and so on ... ${otherVariable.function(parameter)} ...`

这是我的语法:

varname: VAR ;
variable: varname funParameter? ('.' variable)* ;
templateString: '`' (TemplateStringLiteral* '${' variable '}' TemplateStringLiteral*)+ '`' ;
funParameter: '(' variable? (',' variable)*  ')' ;

WS      : [ \t\r\n\u000C]+ -> skip ;
TemplateStringLiteral: ('\\`' | ~'`') ;
VAR : [$]?[a-zA-Z0-9_]+|[$] ;

当语法的输入被解析时,模板字符串不再有空格,因为 WS -> 跳过。当我将 TemplateStringLiteral 放在 WS 之前时,出现错误:

外部输入“”需要 {'`'}

我怎样才能允许只在模板字符串中解析空格而不是跳过?

【问题讨论】:

  • Martin,我认为您的问题实际上与空格无关,因为您的解析问题与它无关(请参阅我的回答)。我认为您可以通过更多地关注症状来改进问题(即使规则乍一看似乎很好,也无法识别字符串)。如果这样说,它可以帮助未来有类似问题的访问者。如果您愿意,我可以尝试编辑您的问题。让我知道:)
  • 如果它解决了您的问题,您介意接受答案吗?或者如果没有提供更多详细信息?

标签: whitespace antlr antlr4 grammar template-strings


【解决方案1】:

目前正在发生的事情

当针对显示生成标记的当前语法测试您的示例时,词法分析器会给出:

[@0,0:0='`',<'`'>,1:0]
[@1,1:4='Some',<VAR>,1:1]
[@2,6:9='text',<VAR>,1:6]
[@3,11:12='${',<'${'>,1:11]
[@4,13:20='variable',<VAR>,1:13]
[@5,21:21='.',<'.'>,1:21]
[@6,22:25='name',<VAR>,1:22]
[@7,26:26='}',<'}'>,1:26]
... shortened ...
[@26,85:84='<EOF>',<EOF>,2:0]

这告诉你,你打算成为TemplateStringLiteral*Some 实际上被编成VAR。为什么会这样?

正如this 回答中提到的,antlr 使用尽可能长的匹配来创建令牌。由于您的 TemplateStringLiteral 规则仅匹配单个字符,但您的 VAR 规则匹配无限多个,词法分析器显然使用后者来匹配 Some

你可以尝试什么(剧透:行不通)

您可以尝试像这样修改规则:

TemplateStringLiteral: ('\\`' | ~'`')+ ;

这样它可以捕获多个字符,因此将是首选。这有两个原因导致它不起作用:

  1. 词法分析器如何将任何内容与VAR 规则匹配?

  2. TemplateStringLiteral 规则现在也匹配 ${,因此禁止正确识别模板块的开头。

如何实现你真正想要的

可能还有另一种解决方案,但这个可行:

文件 MartinCup.g4:

parser grammar MartinCup;

options { tokenVocab=MartinCupLexer; }

templateString
    : BackTick TemplateStringLiteral* (template TemplateStringLiteral*)+ BackTick
    ;

template
    : TemplateStart variable TemplateEnd
    ;

variable
    : varname funParameter? (Dot variable)*
    ;

varname
    : VAR
    ;

funParameter
    : OpenPar variable? (Comma variable)* ClosedPar
    ;

文件 MartinCupLexer.g4:

lexer grammar MartinCupLexer;

BackTick : '`' ;

TemplateStart
    : '${' -> pushMode(templateMode)
    ;

TemplateStringLiteral
    : '\\`'
    | ~'`'
    ;

mode templateMode;

VAR
    : [$]?[a-zA-Z0-9_]+
    | [$]
    ;

OpenPar : '(' ;
ClosedPar : ')' ;
Comma : ',' ;
Dot : '.' ;

TemplateEnd
    : '}' -> popMode;

此语法使用lexer modes 来区分花括号的内侧和外侧。 VAR 规则现在仅在遇到 ${ 后才处于活动状态,并且仅在读取 } 之前保持活动状态。因此它不会捕获像Some 这样的非模板文本。

请注意,词法分析器模式的使用需要拆分语法(解析器和词法分析器语法的单独文件)。由于解析器语法中不允许使用词法分析器规则,因此我不得不为括号、逗号、点和反引号引入标记。

关于空格

我假设您想在“普通文本”中保留空格,但不允许在模板中使用空格。因此我简单地删除了WS 规则。如果您愿意,可以随时重新添加。

我测试了您的替代语法,您将TemplateStringLiteral 放在WS 上方,但与您的观察相反,这给了我:

第 1:1 行无关输入 'Some' 期望 {'${', TemplateStringLiteral}

原因同上,Some 词法化为VAR

【讨论】:

  • 注意:对于在词法分析器语法中使用单个字符串文字定义的词法分析器规则,您仍然可以在解析器语法中使用字符串文字。也就是说,即使您有单独的词法分析器语法,您仍然可以在解析器语法中使用',' 而不是 COMMA。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-03
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多