antlr 语法:仅允许在模板字符串中匹配空格
antlr grammar: Allow whitespace matching only in template string
我想解析模板字符串:
`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 之前时,出现错误:
extraneous input ' ' expecting {'`'}
我怎样才能允许只在模板字符串中解析而不跳过空格?
当前情况
当根据显示生成的标记的当前语法测试您的示例时,词法分析器给出:
[@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
。为什么会这样?
正如 回答中提到的,antlr 使用尽可能长的匹配来创建令牌。由于您的 TemplateStringLiteral
规则仅匹配单个字符,但您的 VAR
规则匹配无限多,词法分析器显然使用后者来匹配 Some
.
你可以尝试什么(扰流板:行不通)
您可以尝试这样修改规则:
TemplateStringLiteral: ('\`' | ~'`')+ ;
因此它可以捕获多个字符,因此将是首选。这有两个不起作用的原因:
词法分析器如何匹配 VAR
规则?
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
.
这样的非模板文本
请注意,词法分析器模式的使用需要拆分语法(解析器和词法分析器语法的单独文件)。由于解析器语法中不允许使用词法分析器规则,因此我不得不为括号、逗号、点和反引号引入标记。
关于空格
我假设您想在 "normal text" 中保留空格,但不允许在模板中使用空格。因此我简单地删除了 WS
规则。如果您愿意,可以随时重新添加它。
我测试了你的替代语法,你把 TemplateStringLiteral
放在 WS
上面,但与你的观察相反,这给了我:
line 1:1 extraneous input 'Some' expecting {'${', TemplateStringLiteral}
原因同上,Some
被词法化为VAR
。
我想解析模板字符串:
`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 之前时,出现错误:
extraneous input ' ' expecting {'`'}
我怎样才能允许只在模板字符串中解析而不跳过空格?
当前情况
当根据显示生成的标记的当前语法测试您的示例时,词法分析器给出:
[@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
。为什么会这样?
正如 TemplateStringLiteral
规则仅匹配单个字符,但您的 VAR
规则匹配无限多,词法分析器显然使用后者来匹配 Some
.
你可以尝试什么(扰流板:行不通)
您可以尝试这样修改规则:
TemplateStringLiteral: ('\`' | ~'`')+ ;
因此它可以捕获多个字符,因此将是首选。这有两个不起作用的原因:
词法分析器如何匹配
VAR
规则?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
.
请注意,词法分析器模式的使用需要拆分语法(解析器和词法分析器语法的单独文件)。由于解析器语法中不允许使用词法分析器规则,因此我不得不为括号、逗号、点和反引号引入标记。
关于空格
我假设您想在 "normal text" 中保留空格,但不允许在模板中使用空格。因此我简单地删除了 WS
规则。如果您愿意,可以随时重新添加它。
我测试了你的替代语法,你把 TemplateStringLiteral
放在 WS
上面,但与你的观察相反,这给了我:
line 1:1 extraneous input 'Some' expecting {'${', TemplateStringLiteral}
原因同上,Some
被词法化为VAR
。