首页 文章

antlr4多行字符串解析

提问于
浏览
1

如果我在antlr4词法分析器中有一个ONELINE_STRING片段规则,它在一行上标识一个简单的引用字符串,我怎样才能在词法分析器中创建一个更通用的STRING规则来连接相邻的ONELINE_STRING(即,仅用空格和/或注释分隔)只要他们每个人都在不同的路线上开始?

"foo" "bar"

将被解析为两个STRING标记,“foo”后跟“bar”

而:

"foo"
"bar"

将被视为一个STRING令牌:“foobar”

为了澄清:我的想法是,虽然我通常希望解析器能够将相邻的字符串识别为单独的,并且解析器会忽略空白和注释,但我想使用这个想法,如果最后一个非空白子令牌在一行上是一个字符串,并且下一行中不是所有空格的第一个子标记也是一个字符串,那么单独的字符串应该连接成一个长字符串,作为指定可能非常长的字符串的方法,而不必将整个事情放在一条线上 . 如果我想要连接所有相邻的字符串子标记,这是非常简单的,因为它们在C中...但是出于我的目的,我只想在字符串子标记在不同的行上开始时发生连接 . 对于可能使用字符串的解析器中的任何规则,此连接应该是不可见的 . 这就是为什么我认为将规则放在词法分析器而不是解析器中可能会更好,但我并不完全反对在解析器中执行此操作,并且所有可能引用了STRING令牌的解析规则都会而是在他们想要一个字符串时引用解析器字符串规则 .

样本1:

"desc" "this sample will parse as two strings.

Sample3(注意,'output'是该语言中的关键字):

output "this is a very long line that I've explicitly made so that it does not "
       "easily fit on just one line, so it gets split up into separate ones for "
       "ease of reading, but the  parser should see it all as one long string. "
       "This example will parse as if the output command had been followed by "
       "only a single string, even though it is composed of multiple string "
       "fragments, all of which should be invisible to the parser.%n";

解析器应该接受这两个示例都是有效的 . 前者是声明的一个例子,而后者是语言中命令性声明的一个例子 .

附录:

我最初一直认为这需要在词法分析器中完成,因为虽然解析器应该忽略换行符,就像所有其他空格一样,多行字符串实际上对换行符的存在很敏感我不认为解析器能够察觉到这一点 .

但是,我一直认为有可能将ONELINE_STRING作为词法分析器规则,并且有一个通用的“字符串”解析器规则来检测相邻的ONELINE_STRINGS,使用字符串之间的谓词来检测下一个ONELINE_STRING令牌是否正在启动与前一行不同的行,如果是这样,它应该无形地连接它们,以便它的文本与一行中指定的字符串无法区分 . 但是,我不确定如何实施这种方法 .

好的,我有 .

我需要在解析器中使用字符串识别器,正如你们有人建议的那样 . 诀窍是在词法分析器中使用词法分析器模式 .

所以在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
         ;

因此,字符串规则连接相邻的字符串文字,只要它们位于不同的行上即可 . stringEnd规则需要一个事件处理程序,用于何时未正确终止字符串文字,以便解析器可以报告语法错误,但该字符串将被视为已正确关闭 .

2 回答

  • 1

    编辑:对不起,没有完全阅读您的要求 . 以下方法将匹配两个示例,而不仅仅是期望的示例 . 不得不考虑一下......

    最简单的方法是在解析器中执行此操作 . 我认为没有必要在词法分析器中完成这一点 .

    multiString : singleString +;
    singleString : ONELINE_STRING; 
    
    
    ONELINE_STRING: ...; // no fragment!
    WS : ... -> skip;
    Comment : ... -> skip;
    
  • 1

    如前所述,(IMO)更好的方法是在解析器中处理这个问题 . 但这是一种在词法分析器中处理它的方法:

    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"
    

    将创建以下5个令牌:

    • "a"

    • "b"

    • "c"\n"d"

    • "e"

    • "f"

    但是,如果令牌包含注释:

    "c" // comment 
    "d"
    

    那么你需要在稍后阶段自己从令牌中删除这个 "// comment" . 词法分析器将无法将此子字符串放在不同的通道上,或 skip 它 .

相关问题