这是一个从未编写过解析器/词法分析器的人提出的菜鸟问题。
我正在为 PHP 中的 CSS 编写标记器/解析器(请不要重复“OMG,为什么在 PHP 中?”)。W3C在此处 (CSS2.1)和此处 (CSS3, draft)巧妙地编写了语法。
这是 21 个可能的标记的列表,所有(除了两个)都不能表示为静态字符串。
我目前的方法是一遍又一遍地循环遍历包含 21 种模式的数组,并if (preg_match())
通过匹配减少源字符串匹配。原则上,这真的很好。然而,对于一个 1000 行的 CSS 字符串,这需要 2 到 8 秒,这对我的项目来说太长了。
现在我正在思考其他解析器如何在几分之一秒内标记和解析 CSS。好吧,C总是比 PHP 快,但是,有什么明显的D'Oh!是我掉进去的吗?
我做了一些优化,比如检查 '@'、'#' 或 '"' 作为剩余字符串的第一个字符,然后只应用相关的正则表达式,但这并没有带来任何巨大的性能提升。
到目前为止我的代码(片段):
$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