ANTLR规则使用固定数量的字符

FGM*_*FGM 6 serialization parsing antlr

我正在尝试为PHP serialize()格式编写一个ANTLR语法,除了字符串之外,一切似乎都能正常工作.问题是序列化字符串的格式是:

s:6:"length";
Run Code Online (Sandbox Code Playgroud)

就正则表达而言,s:(\d+):".{\1}";如果在"匹配数"计数中仅允许反向引用(但它们不是),则会像描述此格式一样的规则.

但我无法找到一种方法来表达词法分析器或解析器语法:整个想法是使读取的字符数取决于描述要读取的字符数的反向引用,如Fortran Hollerith常量(即6HLength),而不是在字符串分隔符上.

这个来自FortranANTLR语法的例子似乎指明了方向,但我不知道如何.请注意,我的目标语言是Python,而大多数文档和示例都是针对Java的:

// numeral literal
ICON {int counter=0;} :
    /* other alternatives */
    // hollerith
    'h' ({counter>0}? NOTNL {counter--;})* {counter==0}?
      {
      $setType(HOLLERITH);
      String str = $getText;
      str = str.replaceFirst("([0-9])+h", "");
      $setText(str);
      }
    /* more alternatives */
    ;
Run Code Online (Sandbox Code Playgroud)

Bar*_*ers 5

由于输入 likes:3:"a"b";有效,您不能String在词法分析器中定义标记,除非第一个和最后一个双引号始终是字符串的开头和结尾。但我想情况并非如此。

所以,你需要一个像这样的词法分析器规则:

SString
  :  's:' Int ':"' ( . )* '";'
  ;
Run Code Online (Sandbox Code Playgroud)

换句话说:匹配 a s:,然后是一个integer值,:"然后是一个或多个可以是任何字符的字符,以";. 但是您需要告诉词法分析器在Int未达到该值时停止使用。您可以通过在语法中混合一些纯代码来做到这一点。您可以通过将普通代码包装在{和 中来嵌入普通代码}。因此,首先将令牌Int保存的值转换为一个名为 的整数变量chars

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( . )* '";'
  ;
Run Code Online (Sandbox Code Playgroud)

现在在( . )*循环中嵌入一​​些代码以在chars倒数到零时立即停止消耗:

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;
Run Code Online (Sandbox Code Playgroud)

就是这样。

一个小演示语法:

grammar Test;

options {
  language=Python;
}

parse
  :  (SString {print 'parsed: [\%s]' \% $SString.text})+ EOF
  ;

SString
  :  's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
  ;

Int
  :  '0'..'9'+
  ;
Run Code Online (Sandbox Code Playgroud)

(请注意,您需要对%语法内部进行转义!)

和一个测试脚本:

import antlr3
from TestLexer import TestLexer
from TestParser import TestParser

input = 's:6:"length";s:1:""";s:0:"";s:3:"end";'
char_stream = antlr3.ANTLRStringStream(input)
lexer = TestLexer(char_stream)
tokens = antlr3.CommonTokenStream(lexer)
parser = TestParser(tokens)
parser.parse()
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

parsed: [s:6:"length";]
parsed: [s:1:""";]
parsed: [s:0:"";]
parsed: [s:3:"end";]
Run Code Online (Sandbox Code Playgroud)