解析带有嵌入式多行控制字符序列的字符串

Parsing strings with embedded multi line control character seuqences

我正在为实时编程语言 PEARL 编写编译器。 PEARL 支持带有嵌入式控制字符序列的字符串,例如

'some text'B 1B 1B\'some more text'.

控制字符序列以“\”为前缀,以“\”结尾。 控制序列里面是两位数字,用来指定控制字符。

在上面的示例中,结果字符串将是

'some textESCESCESCsome more text'

ESC 代表不可打印的 ASCII 转义字符。

此外,控制字符序列内部允许换行符来构建多行字符串,例如

'some text'B 
1B 
1B\'some more text'.

结果与上面的字符串相同。

grammar stringliteral;

tokens {
    CHAR,CHARS,CTRLCHARS,ESC,WHITESPACE,NEWLINE
}

stringLiteral:  '\'' CHARS? '\'' ;

fragment
CHARS: CHAR+ ;

fragment
CHAR: CTRLCHARS | ~['\n\r] ;

fragment
ESC: '\'\' ;

fragment
CTRLCHARS: ESC ~['] ESC;

WHITESPACE: (' ' | '\t')+ -> channel(HIDDEN);

NEWLINE: ( '\r' '\n'? | '\n' ) -> channel(HIDDEN);

上面的lexer/parser行为很奇怪,因为它只接受 'x' 形式的字符串并忽略多个字符和控制字符序列。

可能我正在监督一些显而易见的事情。欢迎任何提示或想法如何解决这个问题!

我现在已经根据 Mike 的提示更正了语法:

grammar stringliteral;

tokens {
     STRING
}

stringLiteral: STRING;
STRING: '\'' ( '\'' '\' | '\' '\'' | . )*? '\'';

控制字符序列结尾的识别还有问题:

输入 'A STRING'\CTRL\'' 产生错误

Line 1:10 token recognition error at: '\'
line 1:11 token recognition error at: 'C'
line 1:12 token recognition error at: 'T'
line 1:13 token recognition error at: 'R'
line 1:14 token recognition error at: 'L'
line 1:15 token recognition error at: '\'

有什么想法吗?顺便说一句:我们正在使用 antlr v 4.5。

此语法存在多个问题:

  1. 您不能在解析器规则中使用 片段 词法分析器规则。
  2. 您的字符串规则是一个解析器规则,因此它会自动删除您使用 WHITESPACENEWLINE 规则定义的空格。
  3. 您没有接受像 B 1B 1B.
  4. 这样的控制字符序列的规则

尤其是第三点是一个真正的问题,因为您不知道您的控制序列在哪里结束(除非这只是一个错字,而您实际上是指:B B B.

在任何情况下,不要在词法分析器中处理转义序列(使规则生效所需的最小处理除外,即处理 \' 序列。您的规则只需要解析整个文本,您可以在语义阶段找出转义序列:

STRING: '\' ('\' '\'' | . )*? '\''; 

注意 *? 是 non-greedy 运算符,在第一个结束引号字符处停止。否则,词法分析器将继续匹配同一字符串规则中的所有后续(转义和 non-escaped)引号字符(贪婪行为)。此外,字符串规则现在是词法分析器规则,不受空格跳过的影响。

我通过改编最新 java 语法示例中的适当规则解决了这个语法片段的问题:

StringLiteral
    :   '\'' StringCharacters? '\''
    ;

fragment
StringCharacters
    :   StringCharacter+
    ;

fragment
StringCharacter
    :   ~['\\r\n]
    |   EscapeSequence
    ;

fragment
EscapeSequence
    : '\'\' (HexEscape| ' ' | [\r\n])* '\\''
    ;

fragment
HexEscape
    :  B4Digit B4Digit
    ;

fragment
B4Digit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' |     'C' | 'D' | 'E' | 'F'
    ;