在PHP中编写类似Scala的解析器组合器

在PHP中编写类似Scala的解析器组合器,php,parsing,scala,parser-combinators,Php,Parsing,Scala,Parser Combinators,在我被迫再次用PHP做一些事情之前,我已经用Scala编写了几个月的代码。我已经意识到,对于我的项目来说,用这种语言准备好解析器组合器会很方便 我发现了这个实现,但是我对此非常失望(特别是因为它与Scala相比过于冗长) 我开始使用二阶函数在PHP中实现解析器组合器。正则表达式解析器的示例如下: interface Result {}; class Success implements Result { function __construct($payload, $next) { $this-

在我被迫再次用PHP做一些事情之前,我已经用Scala编写了几个月的代码。我已经意识到,对于我的项目来说,用这种语言准备好解析器组合器会很方便

我发现了这个实现,但是我对此非常失望(特别是因为它与Scala相比过于冗长)

我开始使用二阶函数在PHP中实现解析器组合器。正则表达式解析器的示例如下:

interface Result {};
class Success implements Result { function __construct($payload, $next) { $this->payload = $payload; $this->next = $next; } }
class Failure implements Result { function __construct($payload, $next) { $this->payload = $payload; $this->next = $next; } }

function r($regex) {
  return function($input) use ($regex) {
    if(preg_match($regex, $input, $matches)) {
      return new Success($matches[0], substr($input, strlen($matches[0])));
    } else {
      return new Failure('Did not match', $input);
    }
  };
}
以及作为组合器示例的
cons

function consF($fn) {
  $args = array_slice(func_get_args(), 1);
  return function($input) use ($fn, $args) {
    $matches = array();
    foreach($args as $p) {
      $r = $p(ltrim($input));
      if($r instanceof Failure) return $r;

      $input = $r->next;
      $matches[] = $r->payload;
    }

    return new Success($fn($matches), $input);
  };
}
这使我能够非常简洁地编写解析器,如下所示:

$name = r('/^[A-Z][a-z]*/');
$full_name = consF(function($a) { return $a; }, $name, $name);
$brackets = alt('()', cons('(', $brackets, ')'));
function($input) {
  $fn = $GLOBALS['brackets'];
  return $fn($input);
}
当语法需要递归时,问题就出现了——在这种情况下,我不能对变量进行排序,以便在使用它们之后定义所有变量。为了编写一个语法来解析括号的输入,比如
(())
,我需要这样的东西:

$name = r('/^[A-Z][a-z]*/');
$full_name = consF(function($a) { return $a; }, $name, $name);
$brackets = alt('()', cons('(', $brackets, ')'));
function($input) {
  $fn = $GLOBALS['brackets'];
  return $fn($input);
}
其中,
alt
组合器在其中一个备选方案成功时成功。将变量作为引用传递应该可以解决这个问题,但是新版本的PHP要求在函数声明中指示通过引用传递,这在使用参数数目可变的函数时是不可能的

我通过传递一个函数作为参数解决了这个问题,如下所示:

$name = r('/^[A-Z][a-z]*/');
$full_name = consF(function($a) { return $a; }, $name, $name);
$brackets = alt('()', cons('(', $brackets, ')'));
function($input) {
  $fn = $GLOBALS['brackets'];
  return $fn($input);
}
然而,这真的很糟糕,需要在最顶层的范围内定义解析器(这也不是一个好主意)

你能给我一些技巧来帮助我克服这个问题,而不需要在定义语法时使用太多额外的代码吗


谢谢

您是否考虑过混合方法(使用PHP调用某种scala服务,甚至在JVM上运行PHP)?我根本没有考虑过-我一定会检查它!然而,我担心将整个项目迁移到Quercus会非常困难(尽管我可能会尝试这样做)。谢谢从他们所说的,你应该能够在他们的平台上运行,用最少的努力。这似乎真的很有趣-我可能会尝试一下:-)虽然如果有一个PHP唯一的解决方案,我会很高兴只是出于我的好奇心。。。