反语法:仅在模板字符串中允许空格匹配

时间:2019-04-17 11:55:03

标签: whitespace antlr antlr4 grammar template-strings

我想解析模板字符串:

`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-> skip,模板字符串不再有空格。将TemplateStringLiteral放在WS之前时,出现错误:

  

外部输入''期望{'`'}

如何允许仅在模板字符串内部对空格进行解析而不是跳过?

1 个答案:

答案 0 :(得分:2)

当前正在发生什么

当针对显示当前标记的当前语法测试示例时,词法分析器给出以下提示:

[@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]

这告诉您,您原本打算作为Some的{​​{1}}实际上被词汇化为TemplateStringLiteral*。为什么会这样?

this答案中所述,antlr使用可能的最长匹配来创建令牌。由于您的VAR规则仅匹配单个字符,但是您的TemplateStringLiteral规则无限匹配,因此词法分析器显然使用后者匹配VAR

您可以尝试的方法(破坏者:将无法使用)

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

Some

,以便捕获多个字符,因此将是首选。这有两个原因使其不起作用:

  1. 词法分析器如何将任何内容与TemplateStringLiteral: ('\\`' | ~'`')+ ; 规则匹配?

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

如何实现自己真正想要的

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

文件MartinCup.g4:

${

文件MartinCupLexer.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
    ;

此语法使用lexer modes区分花括号的内部和外部。 lexer grammar MartinCupLexer; BackTick : '`' ; TemplateStart : '${' -> pushMode(templateMode) ; TemplateStringLiteral : '\\`' | ~'`' ; mode templateMode; VAR : [$]?[a-zA-Z0-9_]+ | [$] ; OpenPar : '(' ; ClosedPar : ')' ; Comma : ',' ; Dot : '.' ; TemplateEnd : '}' -> popMode; 规则现在仅在遇到VAR之后才处于活动状态,并且仅在读取${之前保持活动状态。因此,它不会捕获诸如}之类的非模板文本。

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

关于空白

我假设您想在“普通文本”中保留空格,但不允许模板中包含空格。因此,我只是删除了Some规则。您随时可以根据需要重新添加它。

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

  

第1:1行多余输入'Some'期望{'$ {',TemplateStringLiteral}

其原因与上述相同,WS被归类为Some