maximebf/parsec

此包已被弃用且不再维护。没有建议的替代包。

PHP5.3 的上下文解析库

1.0.0 2012-08-18 10:50 UTC

This package is not auto-updated.

Last update: 2024-05-25 10:35:06 UTC


README

PHP 5.3 的上下文文本解析器。

Build Status

Parsec 是一个非常简单的解析工具包,可用于在 PHP 中创建小型的 领域特定语言(DSL)

安装

安装 Parsec 最简单的方法是使用以下要求的 Composer

{
    "require": {
        "maximebf/parsec": ">=1.0.0"
    }
}

或者,您可以 下载存档 并将 src/ 文件夹添加到 PHP 的 include 路径

set_include_path('/path/to/src' . PATH_SEPARATOR . get_include_path());

Parsec 不提供自动加载器,但遵循 PSR-0 规范
您可以使用以下代码片段来自动加载 Parsec 类

spl_autoload_register(function($className) {
    if (substr($className, 0, 6) === 'Parsec') {
        $filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
        require_once $filename;
    }
});

创建词法分析器

在使用解析器之前,必须使用 词法分析器 对字符串进行标记化。可以使用 Parsec\Lexer 类创建词法分析器。标记定义为键值对的关联数组,键是名称,值是正则表达式(不带分隔符,需要转义字符)。

$lexer = new Parsec\Lexer(array(
    'number' => '[0-9]+',
    'plus' => '\+',
    'minus' => '\-',
    'multi' => '\*',
    'div' => '\/',
    'bracketOpen' => '\(',
    'bracketClose' => '\)'
));
$tokens = $lexer->tokenize($string);

最后一行的结果是包含每个标记的关联数组,每个标记都有一个 'token' 键和一个 'value' 键。未匹配的字符串也添加到数组中。

创建解析器

由于大多数解析器都旨在解析文本,因此 Parsec 包含 Parsec\StringParser 类。它已提供所有必要的代码来遍历标记数组。也可以从底层创建解析器,使用 Parsec\AbstractParser

以下示例中,将使用 StringParser。配置词法分析器和上下文工厂可以通过覆盖构造函数来完成。上下文工厂定义了在哪些命名空间中查找上下文类。也可以覆盖解析方法来指定开始解析的上下文。

class ArithParser extends Parsec\StringParser
{
    public function __construct()
    {
        $factory = new Parsec\ContextFactory(array('\\'));
        $lexer = new Parsec\Lexer(array(
            'number' => '[0-9]+',
            'plus' => '\+',
            'minus' => '\-',
            'multi' => '\*',
            'div' => '\/',
            'bracketOpen' => '\(',
            'bracketClose' => '\)'
        ));
        parent::__construct($lexer, $factory);
    }
    
    public function parse($string)
    {
        return parent::parse($string, 'Expression');
    }
}

创建上下文

上下文是继承自 Context 的类,它们将对标记执行某些操作。必须为每个需要处理的标记创建一个方法。这些方法必须以 "token" 开头,后跟大写的标记名称。它们将接收标记的值。

可以使用 enterContext() 方法进入新的上下文或使用 exitContext() 方法退出当前上下文。最后一个方法接收一个参数,该参数将被作为上下文的结果返回。

"eos" 标记(字符串结束)将自动附加到标记数组。可以使用 "text" 标记捕获未匹配的字符串。

class Expression extends Parsec\Context
{
    protected $number;
    
    public function tokenNumber($value)
    {
        $this->number = $value;
    }
    
    public function tokenPlus()
    {
        $this->exitContext($this->number + $this->enterContext('Expression'));
    }
    
    public function tokenMinus()
    {
        $this->exitContext($this->number - $this->enterContext('Expression'));
    }
    
    public function tokenMulti()
    {
        $this->exitContext($this->number * $this->enterContext('Expression'));
    }
    
    public function tokenDiv()
    {
        $this->exitContext($this->number / $this->enterContext('Expression'));
    }
    
    public function tokenBracketOpen()
    {
        if ($this->number === null) {
            $this->number = 1;
        }
        $this->exitContext($this->number * $this->enterContext('Expression'));
    }
    
    public function tokenBracketClose()
    {
        $this->exitContext($this->number);
    }
    
    public function tokenEos($text)
    {
        $this->exitContext($this->number);
    }
}

使用

$parser = new ArithParser();
printf("3 + 4 = %s\n", $parser->parse('3 + 4'));
printf("6 * (2 / 3) = %s\n", $parser->parse('6 * (2 / 3)'));