仅在 ANTLR 语法的一部分显式处理换行符

Handle newlines explicitly only in part of an ANTLR grammar

我目前正在为必须转换为较新标准的旧专有标记语言开发解析器。为此,我正在使用 ANTLR 4。

该结构由由特定起始符及其相关终止符分隔的块组成(例如 { ... }< ... > , INPUT ... END).在每个块中,元素按行指定,以换行符分隔;实际上,只有在某些地方需要这些换行符才能理解代码的含义。

例如:

< ID
SOME_VAR "optional modifier string"
$anEnvironmentVariable
"a constant string"
"another constant" "with its optional modifier"
>

解析器规则如下

field
  : OPEN_ANGLED_BRACKET row_id
    ((ENVIRONMENT_VAR | DQUOTE_STR | VAR) DQUOTE_STR?)+
    CLOSED_ANGLED_BRACKET
  ;

// [...]

WHITESPACE
  : [ \t\r\n] -> skip
  ;

可以很容易地解析上面的例子,但是因为忽略了换行符,所以它实际上无法区分双引号字符串是常量(意味着它在行的开头)还是修饰符字符串(后面同一行中的前一个 variable/constant)。

我实际上可以像这样明确地处理换行符:

field
  : OPEN_ANGLED_BRACKET row_id NEWLINE
    ((ENVIRONMENT_VAR | DQUOTE_STR | VAR) DQUOTE_STR? NEWLINE)+
    CLOSED_ANGLED_BRACKET NEWLINE
  ;

// [...]

WHITESPACE
  : [ \t] -> skip
  ;

NEWLINE
  : '\r'? '\n'
  | '\r'
  ;

但是我必须明确地处理换行符在语法的其余部分中的任何地方,使它复杂化很多!

有什么方法可以将明确的换行符限制在有角度的括号内,在其他地方跳过它"automatically"?

你可以在这里使用lexical modes。您必须定义单独的词法分析器和解析器语法才能使用词法模式。

每当您在词法分析器中遇到 ENVIRONMENT_VARVARDQUOTE_STR(连续的第一个值)时,您就会更改词法模式。在这个新的词法模式中,你匹配 3 个东西:字符串、空格(你跳过)和换行(你也跳过,在这个标记之后,你改回默认模式)。这一切听起来可能有点含糊,所以这里有一个简短的演示:

文件:MarkupLexer.g4

lexer grammar MarkupLexer;

ENVIRONMENT_VAR       : '$' VAR    -> mode(MODIFIER_MODE);
VAR                   : [a-zA-Z_]+ -> mode(MODIFIER_MODE);
DQUOTE_STR            : STR        -> mode(MODIFIER_MODE);
OPEN_ANGLED_BRACKET   : '<';
CLOSED_ANGLED_BRACKET : '>';
SPACES                : [ \t\r\n] -> skip;

fragment STR : '"' ~["\r\n]* '"';

mode MODIFIER_MODE;

  MODIFIER_MODE_SPACES : [ \t] -> skip;
  MODIFIER_MODE_NL     : [\r\n]+ -> skip, mode(DEFAULT_MODE);
  MODIFIER_MODE_STRING : STR;

解析器将如下所示:

文件:MarkupParser.g4

parser grammar MarkupParser;

options {
  tokenVocab=MarkupLexer;
}

field
  : OPEN_ANGLED_BRACKET row_id row+ CLOSED_ANGLED_BRACKET
  ;

row
 : (ENVIRONMENT_VAR | DQUOTE_STR | VAR) MODIFIER_MODE_STRING?
 ;

row_id
 : VAR
 ;

当您解析输入时:

< ID
SOME_VAR "optional modifier string"
$anEnvironmentVariable
"a constant string"
"another constant" "with its optional modifier"
>

你将得到如下解析树:

我想提出一个不使用词法分析器模式的解决方案(因为我发现它们很难看),因此修改了 Bart 的语法:

grammar SOGrammar;

start:
    OPEN_ANGLED_BRACKET ROW_ID ROW* CLOSED_ANGLED_BRACKET
;

VAR:             [a-zA-Z_]+;
ENVIRONMENT_VAR: '$' VAR;
DQUOTE_STR:      '"' .*? '"';

OPEN_ANGLED_BRACKET: '<';
CLOSED_ANGLED_BRACKET: '>';

ROW_ID: VAR LINEBREAK;
ROW:
    (ENVIRONMENT_VAR | DQUOTE_STR | VAR) (SPACE* DQUOTE_STR)? LINEBREAK
;

SPACE: [ \t] -> skip;
LINEBREAK: [\r\n] -> skip;

这里的想法是,一行可以完全在我们可以控制空格的词法分析器中处理。

解析树是: