【发布时间】:2010-04-09 18:53:26
【问题描述】:
这是一个从未编写过解析器/词法分析器的人提出的菜鸟问题。
我正在为 PHP 中的 CSS 编写标记器/解析器(请不要重复“OMG,为什么在 PHP 中?”)。语法由 W3C 整齐地写下来here (CSS2.1) 和here (CSS3, draft)。
这是一个包含 21 个可能标记的列表,所有(除了两个)都不能表示为静态字符串。
我目前的方法是一遍又一遍地遍历包含 21 个模式的数组,执行if (preg_match()) 并通过匹配减少源字符串匹配。原则上,这真的很好。然而,对于一个 1000 行的 CSS 字符串,这需要 2 到 8 秒,这对我的项目来说太长了。
现在我正在研究其他解析器如何在几分之一秒内标记 和 解析 CSS。好的,C 总是比 PHP 快,但是,有没有明显的 D'Oh! 让我陷入困境?
我做了一些优化,比如检查 '@'、'#' 或 '"' 作为剩余字符串的第一个字符,然后只应用相关的正则表达式,但这并没有带来任何显着的性能提升。
到目前为止我的代码(sn-p):
$TOKENS = array(
'IDENT' => '...regexp...',
'ATKEYWORD' => '@...regexp...',
'String' => '"...regexp..."|\'...regexp...\'',
//...
);
$string = '...CSS source string...';
$stream = array();
// we reduce $string token by token
while ($string != '') {
$string = ltrim($string, " \t\r\n\f"); // unconsumed whitespace at the
// start is insignificant but doing a trim reduces exec time by 25%
$matches = array();
// loop through all possible tokens
foreach ($TOKENS as $t => $p) {
// The '&' is used as delimiter, because it isn't used anywhere in
// the token regexps
if (preg_match('&^'.$p.'&Su', $string, $matches)) {
$stream[] = array($t, $matches[0]);
$string = substr($string, strlen($matches[0]));
// Yay! We found one that matches!
continue 2;
}
}
// if we come here, we have a syntax error and handle it somehow
}
// result: an array $stream consisting of arrays with
// 0 => type of token
// 1 => token content
【问题讨论】:
-
个人资料。使用 XDebug 生成分析数据并将其加载到 KCacheGrind 中。如果可能,请避免在源字符串上一遍又一遍地运行 substr() ——一遍又一遍地重新分配字符串并不是免费的。并找到一些方法来减少您评估的正则表达式的数量。或者,更好的是,停止使用正则表达式。
-
显而易见的 D'oh!不是在阅读有关词法分析器如何真正工作的信息。关键思想是它们将 set 模式匹配(您称它们为正则表达式)组合成一个匹配器,就像它一次应用所有匹配器一样。再多的“优化”你的尝试模式方案,一次一个,在性能上永远不会接近。
标签: php performance parsing token lexer