Antlr4 DM字符串词法分析器规则

时间:2018-12-05 19:58:48

标签: antlr antlr4 lexer

我正在尝试以词法分析器形式表示BYOND DM语言字符串(请参见http://byond.comhttp://byond.com/docs/ref)。这是字符串的规则:

  • 字符串以双引号开头和结尾。即"hello world"的值为hello world
  • 反斜杠用作转义符,可以转义结束引号。即"hello\"world"的值为hello"world
  • 可以通过用反斜杠结束该行来忽略字符串中的换行符。即"hello\ world"的值为helloworld
  • 如果字符串分别以序列{" / "}打开/关闭,则允许换行并输入到最终字符串中。序列\\\n仍然被忽略
  • 字符串可以包含大括号内的嵌入式表达式,这些表达式被格式化为结果。反斜杠可以逃避开括号。即"hello [ "world" ] \["在运行时求值为hello world [。任何表达式都可以放在花括号中(通话,数学等)
  • 如果起始引号/大括号前面带有'@'转义序列,并且字符串的嵌入式表达式被禁用。即@{"hello [worl\d"}@"hello [worl\d"均得出hello [worl\d

我正在尝试构建ANTLR4 .g4词法分析器规则来标记这些字符串。我认为我需要4种(或更多)令牌类型:

  • 普通字符串。即"hello world"@"hello world"@{"hello world"}{"hello world"}
  • 字符串在嵌入表达式之前开始。即"hello [{"hello [
  • 嵌入表达式之后的字符串结尾。即] world"] world"}
  • 两个嵌入式表达式之间的字符串。即] hello world [

这是我的尝试(不完整且不成功):

LSTRING: '"' ('\\[' | ~[[\r\n])* '[';
RSTRING: ']' ('\\"' | ~["\r\n])* '"'; 
CSTRING: ']' ('\\[' | ~[[\r\n])* '['; 
FSTRING: '"' ('\\"' | ~["\r\n])* '"';

如果这在词法分析器中无法解决,我可以自己编写带有标记@{""}[的解析器规则, ]\\"。但是,我想我会试一试,因为它会表现得更好。

1 个答案:

答案 0 :(得分:0)

我用下面的词法小知识解决了它。 Permalink

...
@lexer::members
{
ulong regularAccessLevel;
System.Collections.Generic.Stack<bool> multiString = new System.Collections.Generic.Stack<bool>();
}
...
VERBATIUM_STRING: '@"' (~["\r\n])* '"';
MULTILINE_VERBATIUM_STRING: '@{"' (~'"')* '"}';
MULTI_STRING_START: '{"' { multiString.Push(true); } -> pushMode(INTERPOLATION_STRING);
STRING_START: '"' { multiString.Push(false); } -> pushMode(INTERPOLATION_STRING);
...
LBRACE: '[' { ++regularAccessLevel; };
RBRACE: ']' { if(regularAccessLevel > 0) --regularAccessLevel; else if(multiString.Count > 0) { PopMode(); } };
...
mode INTERPOLATION_STRING;
CHAR_INSIDE: '\\\''
    | '\\"'
    | '\\['
    | '\\\\'
    | '\\0'
    | '\\a'
    | '\\b'
    | '\\f'
    | '\\n'
    | '\\r'
    | '\\t'
    | '\\v'
    ;

EMBED_START: '[' -> pushMode(DEFAULT_MODE);
MULTI_STRING_CLOSE: {multiString.Peek()}? '"}' { multiString.Pop(); PopMode(); };
STRING_CLOSE: {!multiString.Peek()}? '"' { multiString.Pop(); PopMode(); };
STRING_INSIDE: {!multiString.Peek()}? ~('[' | '\\' | '"' | '\r' | '\n')+;
MULTI_STRING_INSIDE: {multiString.Peek()}? ~('[' | '\\' | '"')+;

某些字符串可能会导致它依次发出多个STRING_INSIDE / MULTI_STRING_INSIDE令牌,但这是可以接受的,因为解析器仍然会吃掉它。

其中很多来自阅读antlr4示例permalink

中的C#内插字符串