antlr4多行字符串解析
如果我在antlr4 lexer中有一个单行_字符串片段规则,该规则在一行上标识一个简单的带引号的字符串,那么我如何在lexer中创建一个更通用的字符串规则,该规则将连接相邻的单行_字符串(即,仅由空格和/或注释分隔),只要它们从不同的行开始 即 将被解析为两个字符串标记,“foo”后跟“bar” 而:antlr4多行字符串解析,antlr,antlr4,Antlr,Antlr4,如果我在antlr4 lexer中有一个单行_字符串片段规则,该规则在一行上标识一个简单的带引号的字符串,那么我如何在lexer中创建一个更通用的字符串规则,该规则将连接相邻的单行_字符串(即,仅由空格和/或注释分隔),只要它们从不同的行开始 即 将被解析为两个字符串标记,“foo”后跟“bar” 而: "foo" "bar" 将被视为一个字符串标记:“foobar” 澄清一下:我的想法是,虽然我通常希望解析器能够将相邻字符串识别为单独的字符串,并且解析器可以忽略空格和注释,但我希望使用这样的
"foo"
"bar"
将被视为一个字符串标记:“foobar”
澄清一下:我的想法是,虽然我通常希望解析器能够将相邻字符串识别为单独的字符串,并且解析器可以忽略空格和注释,但我希望使用这样的想法,即如果行上最后一个非空格子标记是字符串,下一行的第一个子标记不是全部空白也是一个字符串,那么单独的字符串应该连接成一个长字符串,作为一种指定可能非常长的字符串的方法,而不必将整个字符串放在一行上。这是非常简单的,如果我想所有相邻的字符串子标记连接,因为他们在C。。。但出于我的目的,我只希望在字符串子标记从不同的行开始时发生连接。这种连接对于解析器中可能使用字符串的任何规则都不可见。这就是为什么我认为最好将规则放在lexer中而不是解析器中,但我并不完全反对在解析器中这样做,所有可能引用字符串标记的解析规则在需要字符串时都会引用解析器字符串规则
样本1:
"desc" "this sample will parse as two strings.
示例3(注意,'output'是语言中的一个关键字):
解析器应该接受这两个示例。前者是声明的一个例子,而后者是语言中命令式语句的一个例子
增编:
我原本认为这需要在lexer中完成,因为尽管与所有其他空格一样,语法分析器应该忽略换行符,但多行字符串实际上对换行符的存在很敏感,我不认为语法分析器可以察觉到这一点
然而,我一直在想,可能有一个ONELINE_字符串作为lexer规则,并有一个通用的“STRING”解析器规则来检测相邻的ONELINE_字符串,在字符串之间使用谓词来检测下一个ONELINE_字符串标记是否从与前一个不同的行开始,如果是这样,它应该以不可见的方式连接它们,以便其文本与在一行中指定的字符串无法区分。然而,我不确定这将如何实施
好的,我知道了
正如你们中的一些人所建议的,我需要在解析器中使用字符串识别器。诀窍是在lexer中使用lexer模式
multiString : singleString +;
singleString : ONELINE_STRING;
ONELINE_STRING: ...; // no fragment!
WS : ... -> skip;
Comment : ... -> skip;
所以在Lexer文件中我有这样一个:
BEGIN_STRING : '"' -> pushMode(StringMode);
mode StringMode;
END_STRING: '"'-> popMode;
STRING_LITERAL_TEXT : ~[\r\n%"];
STRING_LITERAL_ESCAPE_QUOTE : '%"' { setText("\""); };
STRING_LITERAL_ESCAPE_PERCENT: '%%' { setText("%"); };
STRING_LITERAL_ESCAPE_NEWLINE : '%n'{ setText("\n"); };
UNTERMINATED_STRING: { _input.LA(1) == '\n' || _input.LA(1) == '\r' || _input.LA(1) == EOF}? -> popMode;
string returns [String text] locals [int line] : a=stringLiteral { $line = $a.line; $text=$a.text;}
({_input.LT(1)!=null && _input.LT(1).getLine()>$line}?
a=stringLiteral { $line = $a.line; $text+=$a.text; })*
;
stringLiteral returns [int line, String text]: BEGIN_STRING {$text = "";}
(a=(STRING_LITERAL_TEXT
| STRING_LITERAL_ESCAPE_NEWLINE
| STRING_LITERAL_ESCAPE_QUOTE
| STRING_LITERAL_ESCAPE_PERCENT
) {$text+=$a.text;} )*
stringEnd { $line = $BEGIN_STRING.line; }
;
stringEnd: END_STRING #string_finish
| UNTERMINATED_STRING #string_hang
;
在解析器文件中,我有以下内容:
BEGIN_STRING : '"' -> pushMode(StringMode);
mode StringMode;
END_STRING: '"'-> popMode;
STRING_LITERAL_TEXT : ~[\r\n%"];
STRING_LITERAL_ESCAPE_QUOTE : '%"' { setText("\""); };
STRING_LITERAL_ESCAPE_PERCENT: '%%' { setText("%"); };
STRING_LITERAL_ESCAPE_NEWLINE : '%n'{ setText("\n"); };
UNTERMINATED_STRING: { _input.LA(1) == '\n' || _input.LA(1) == '\r' || _input.LA(1) == EOF}? -> popMode;
string returns [String text] locals [int line] : a=stringLiteral { $line = $a.line; $text=$a.text;}
({_input.LT(1)!=null && _input.LT(1).getLine()>$line}?
a=stringLiteral { $line = $a.line; $text+=$a.text; })*
;
stringLiteral returns [int line, String text]: BEGIN_STRING {$text = "";}
(a=(STRING_LITERAL_TEXT
| STRING_LITERAL_ESCAPE_NEWLINE
| STRING_LITERAL_ESCAPE_QUOTE
| STRING_LITERAL_ESCAPE_PERCENT
) {$text+=$a.text;} )*
stringEnd { $line = $BEGIN_STRING.line; }
;
stringEnd: END_STRING #string_finish
| UNTERMINATED_STRING #string_hang
;
因此,只要相邻字符串文字位于不同的行上,字符串规则就会将它们连接起来。stringEnd规则需要一个事件处理程序,用于处理字符串文字未正确终止的情况,以便解析器可以报告语法错误,但字符串会被视为已正确关闭。编辑:抱歉,尚未完全阅读您的要求。以下方法将匹配两个示例,而不仅仅是所需的示例。我必须考虑一下 最简单的方法是在解析器中执行此操作。我认为没有必要在lexer中这样做
multiString : singleString +;
singleString : ONELINE_STRING;
ONELINE_STRING: ...; // no fragment!
WS : ... -> skip;
Comment : ... -> skip;
编辑:抱歉,尚未完全阅读您的要求。以下方法将匹配两个示例,而不仅仅是所需的示例。我必须考虑一下 最简单的方法是在解析器中执行此操作。我认为没有必要在lexer中这样做
multiString : singleString +;
singleString : ONELINE_STRING;
ONELINE_STRING: ...; // no fragment!
WS : ... -> skip;
Comment : ... -> skip;
如前所述,(IMO)更好的方法是在解析器中处理这个问题。但这里有一种在lexer中处理它的方法:
STRING
: SINGLE_STRING ( LINE_CONTINUATION SINGLE_STRING )*
;
HIDDEN
: ( SPACE | LINE_BREAK | COMMENT ) -> channel(HIDDEN)
;
fragment SINGLE_STRING
: '"' ~'"'* '"'
;
fragment LINE_CONTINUATION
: ( SPACE | COMMENT )* LINE_BREAK ( SPACE | COMMENT )*
;
fragment SPACE
: [ \t]
;
fragment LINE_BREAK
: [\r\n]
| '\r\n'
;
fragment COMMENT
: '//' ~[\r\n]+
;
将输入标记化:
"a" "b"
"c"
"d"
"e"
"f"
然后,您需要在稍后阶段自己从令牌中删除此“//comment”
。lexer将无法将此子字符串放在其他通道上,或跳过它。如前所述,(IMO)更好的方法是在解析器内处理此问题。但这里有一种在lexer中处理它的方法:
STRING
: SINGLE_STRING ( LINE_CONTINUATION SINGLE_STRING )*
;
HIDDEN
: ( SPACE | LINE_BREAK | COMMENT ) -> channel(HIDDEN)
;
fragment SINGLE_STRING
: '"' ~'"'* '"'
;
fragment LINE_CONTINUATION
: ( SPACE | COMMENT )* LINE_BREAK ( SPACE | COMMENT )*
;
fragment SPACE
: [ \t]
;
fragment LINE_BREAK
: [\r\n]
| '\r\n'
;
fragment COMMENT
: '//' ~[\r\n]+
;
将输入标记化:
"a" "b"
"c"
"d"
"e"
"f"
然后,您需要在稍后阶段自己从令牌中删除此
“//comment”
。lexer将无法将此子字符串放在其他通道上,或跳过它。“…在解析器中执行此操作…”+1“…在解析器中执行此操作…”+1关于“foo”\n“bar”
?(请注意\n
前面的空格)也许您可以解释一下这种情况的必要性?这两种情况是以有效的方式出现的,还是其中一种无效?如果其中一个是语法错误,无论如何都要对其进行分析,稍后再进行检查!两者都是无效的。。。在同一行上,两个字符串应该分别进行解析,并且被解析器视为两个单独的字符串。但是我仍然需要一种方法来指定更长的字符串,而不必将它们全部放在一行上,因此我们的想法是在字符串位于单独的行上时隐式连接字符串,但它们之间只有被忽略的字符或文本,例如空格。这种连接应该对语法的主要部分是不可见的,就其文本属性而言,除了可能非常长的长度外,它应该与常规字符串无法区分。我的意思是,如果可能的话,两个单独的字符串可以紧接着出现?也就是说,“String”“String”是否可能合法出现,并且不应组合为单个字符串?关于“foo”\n“bar”
?(请注意\n
前面的空格)也许您可以解释一下这种情况的必要性?这两种情况是以有效的方式出现的,还是其中一种无效?如果他们中的一个是个笨蛋