我正在尝试为PHP serialize()格式编写一个ANTLR语法,除了字符串之外,一切似乎都能正常工作。问题是序列化字符串的格式是:
s:6:"length";
就正则表达而言,如果在“匹配数”计数中仅允许反向引用(但它们不是),则s:(\d+):".{\1}";
之类的规则将描述此格式。
但我无法找到一种方法来表达词法分析器或解析器语法:整个想法是使读取的字符数取决于描述要读取的字符数的反向引用,如Fortran Hollerith常量(即6HLength
),而不是字符串分隔符。
来自ANTLR grammar for Fortran的这个例子似乎指明了方向,但我不知道如何。请注意,我的目标语言是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 */
;
答案 0 :(得分:4)
由于s:3:"a"b";
之类的输入有效,因此您无法在词法分析器中定义String
令牌,除非第一个和最后一个双引号始终开头和结尾你的字符串。但我想情况并非如此。
所以,你需要像这样的词法分析器规则:
SString
: 's:' Int ':"' ( . )* '";'
;
换句话说:匹配s:
,然后是integer
值,后跟:"
,然后是一个或多个可以是任何内容的字符,以";
结尾。但是,当未达到值Int
时,您需要告诉词法分析器停止使用。你可以通过在语法中混合一些简单的代码来做到这一点。您可以通过将简单代码包含在{
和}
中来嵌入。因此,首先将令牌Int
保存的值转换为名为chars
的整数变量:
SString
: 's:' Int {chars = int($Int.text)} ':"' ( . )* '";'
;
现在在( . )*
循环中嵌入了一些代码,以便在chars
倒数到零时立即停止使用:
SString
: 's:' Int {chars = int($Int.text)} ':"' ( {if chars == 0: break} . {chars = chars-1} )* '";'
;
就是这样。
一个小的演示语法:
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'+
;
(请注意,您需要转义语法中的%
!)
测试脚本:
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()
产生以下输出:
parsed: [s:6:"length";]
parsed: [s:1:""";]
parsed: [s:0:"";]
parsed: [s:3:"end";]