Antlr4 DM 字符串词法分析器规则
Antlr4 DM string lexer rules
我正在尝试以词法分析器形式表示 BYOND DM 语言字符串(参见 http://byond.com and http://byond.com/docs/ref)。以下是字符串的规则:
- 字符串以双引号开头和结尾。即
"hello world"
计算为 hello world
- 反斜杠作为转义字符,可以转义结束引号。即
"hello\"world"
计算为 hello"world
- 可以通过以反斜杠结束该行来忽略字符串中的换行符。即
"hello\
world"
计算为 helloworld
- 如果字符串opens/closes的序列分别为
{"
/"}
,则允许换行并输入到最终字符串中。序列\\n
仍然被忽略
- 字符串可以包含大括号内的嵌入表达式,这些表达式被格式化为结果。反斜杠可以避开左大括号。即
"hello [ "world" ] \["
在 运行 时计算为 hello world [
。任何表达式都可以放在大括号中(调用、数学等...)
- 如果起始 quote/curly 大括号以“@”转义序列为前缀,并且禁用字符串的嵌入表达式。即
@{"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])* '"';
如果在词法分析器中无法解决这个问题,我可以用标记 @
、{"
、"}
、[=37= 自己编写解析器规则]、]
、\
和 "
。但是,我想我会试一试,因为它的性能会更好。
我用以下词法分析器花絮解决了这个问题。 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 示例中的 C# 内插字符串 permalink
我正在尝试以词法分析器形式表示 BYOND DM 语言字符串(参见 http://byond.com and http://byond.com/docs/ref)。以下是字符串的规则:
- 字符串以双引号开头和结尾。即
"hello world"
计算为hello world
- 反斜杠作为转义字符,可以转义结束引号。即
"hello\"world"
计算为hello"world
- 可以通过以反斜杠结束该行来忽略字符串中的换行符。即
"hello\ world"
计算为helloworld
- 如果字符串opens/closes的序列分别为
{"
/"}
,则允许换行并输入到最终字符串中。序列\\n
仍然被忽略 - 字符串可以包含大括号内的嵌入表达式,这些表达式被格式化为结果。反斜杠可以避开左大括号。即
"hello [ "world" ] \["
在 运行 时计算为hello world [
。任何表达式都可以放在大括号中(调用、数学等...) - 如果起始 quote/curly 大括号以“@”转义序列为前缀,并且禁用字符串的嵌入表达式。即
@{"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])* '"';
如果在词法分析器中无法解决这个问题,我可以用标记 @
、{"
、"}
、[=37= 自己编写解析器规则]、]
、\
和 "
。但是,我想我会试一试,因为它的性能会更好。
我用以下词法分析器花絮解决了这个问题。 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 示例中的 C# 内插字符串 permalink