alexandrebrach/tokentree

此包的最新版本(1.0.1)没有可用的许可信息。

解析文件的工具。以“令牌树”的形式提供结果

1.0.1 2018-08-18 08:44 UTC

This package is not auto-updated.

Last update: 2024-09-22 15:01:04 UTC


README

该库解析您的文本并将其根据您定义的规则转换为令牌。您可以在多个层上定义令牌以实现复杂解析,从而得到一个令牌数组。 (或是一个n级令牌数组,具体取决于您定义的规则的深度)

安装库

composer require alexandrebrach/tokentree

运行单元测试

phpunit tests

如何使用它

定义令牌提取规则

为了提取每个令牌,必须在扩展 Tokentree\Lexic\Lexic 的类中定义规则。

每个规则可以是

  • 一个简单的字符串(一个逗号或像 class 这样的关键字)
  • 一个正则表达式(用于提取 $dollarBaseNamedVariable
  • 一个回调函数来处理关键事情(例如 SET_DELIMITER SQL 指令)

使用SQL解析的示例(这仅用于说明目的,可能不功能。代码中提供了一个完整的SQL令牌化器)

以下说明如何使用3层令牌解析SQL CREATE TABLE 语句。

第一个类能够提取SQL CREATE TABLE 语句中可能出现的任何单词或字符。它们将被转换为令牌。每个令牌都将有一个令牌类型(CREATE_TABLENAME 等),以及一个值(字符串的值或正则表达式的匹配结果)

class Token extends Lexic
{
    protected function _init()
    {
        $this
            ->_addRule( 'CREATE_TABLE',	'regexp', '|^(CREATE\s*TABLE(\s*IF\s*NOT\s*EXISTS)?)|' )
            ->_addRule( 'NAME',	'regexp', '|^`([^`]*)`|' )
            ->_addRule( 'DATATYPE', 'regexp', '/^(\b(int|tinyint|float|decimal|text|varchar|datetime|date|timestamp|time|point|char)\b(\s*\(\s*(\d*(\s*\,\d*)?)\s*\))?)/' )
            ->_addRule( 'CHARSET', 'regexp', '|^CHARACTER\s*SET\s*(\S*)|' )
            ->_addRule( 'UNSIGNED', 'string', 'UNSIGNED' )
            ->_addRule( 'NOT_NULL', 'string', 'NOT NULL' )
            ->_addRule( 'NULL',	'string', 'NULL' )
            ->_addRule( 'DEFAULT', 'string', 'DEFAULT' )
            ->_addRule( 'STRING', 'regexp', '|^\'(.*)\'|' )
            ->_addRule( 'STRING', 'regexp' ,'|^\"(.*)\"|' )
            ->_addRule( 'EXPRESSION', 'regexp',	'|^(CURRENT_TIMESTAMP)|' )
            ->_addRule( 'VALUE', 'regexp', '|^(DEFAULT\s*CHARSET\s*)|')
            ->_addRule( 'VALUE', 'regexp', '|^([a-zA-Z_0-9]+)|')
            ->_addRule( 'ON', 'string',	'ON' )
            ->_addRule( 'ACTION', 'regexp',	'/^(INSERT|UPDATE|DELETE)/' )
            ->_addRule( 'AUTO_INCREMENT', 'string',	'AUTO_INCREMENT' )
            ->_addRule( 'SQLCOMMENT', 'string',	'COMMENT' )
            ->_addRule( 'KEY', 'regexp', '/^(PRIMARY|UNIQUE)?\s*KEY/' )
            ->_addRule( 'USING', 'string', 'USING' )
            ->_addRule( 'CONSTRAINT', 'string',	'CONSTRAINT' )
            ->_addRule( 'FOREIGN_KEY', 'regexp', '|^(FOREIGN\s*KEY)|' )
            ->_addRule( 'REFERENCES', 'string',	'REFERENCES' )
            ->_addRule( 'TRIGGER_ACTION', 'regexp', '/^(CASCADE|RESTRICT|SET\s*NULL|NO\s*ACTION)/' )
            ->_addRule( 'ALIAS', 'regexp', '|^@+([a-zA-Z_0-9]+)|')
        ;
    }
}

一旦完成,解析器将连接第一层中找到的每个令牌类型的值。生成的字符串将以与之前相同的方式由第二层解析。因此,它提供了一个更高的抽象层:能够识别 CREATE TABLE 语句的开始、列定义等。

class Expression extends Lexic
{
    protected function _init()
    {
        $this
            ->_addRule( 'CREATE_TABLE',	'regexp', '/^CREATE_TABLE\s*NAME/' )
            ->_addRule( 'COLUMN', 'regexp',	'/^(NAME\s*DATATYPE\s*(CHARSET)?\s*(UNSIGNED)?\s*(NULL|NOT_NULL)?\s*(DEFAULT)?\s*(STRING|NULL|EXPRESSION|VALUE)?\s*(ON ACTION)?\s*(STRING|NULL|EXPRESSION)?\s*(AUTO_INCREMENT)?\s*(SQLCOMMENT\s*STRING)?)\s*/' )
            ->_addRule( 'KEY', 'regexp', '/^(KEY\s*(NAME)?\s*\(\s*(NAME(\s*\,\s*NAME\s*)*)\s*\)\s*(USING\s*VALUE)?)/' )
            ->_addRule( 'FOREIGN_KEY',	'regexp', '/^(CONSTRAINT\s*NAME\s*FOREIGN_KEY\s*\(\s*NAME\s*\)\s*REFERENCES\s*NAME\s*\(\s*NAME\s*\)\s*(ON\s*ACTION\s*TRIGGER_ACTION\s*)*)/' )
            ->_addRule( 'ASSIGNMENT', 'regexp',	'/^((VALUE|ALIAS|AUTO_INCREMENT) \= (VALUE|ALIAS|STRING))/' )
            ->_addRule( 'ASSIGNMENT', 'regexp',	'/^((NAMES) (VALUE))/' )
        ;
    }
}

最终,以我们从前一层到第二层的方式,我们将第二层每个令牌的值连接起来,并将结果字符串提供给第三层。最后一层能够使用第二层的令牌识别任何 CREATE TABLE 语句的结构。只需要一个唯一的规则就可以实现这一点

class Statement extends Lexic
{
    protected function _init()
    {
        $this
            ->_addRule( 'CREATE_TABLE', 'regexp', '/^(CREATE_TABLE\s*\(\s*COLUMN(\s*\,\s*(COLUMN|KEY|FOREIGN_KEY))*\s*\)(\s*ASSIGNMENT)*)/' )
    }
}

使用层来解析字符串

使用之前的类(按正确的顺序)来定义多层解析引擎

$tokenTree = new \Tokentree\Tokentree();
$tokenTree->setLexics( [ new Token(), new Expression(), new Statement() ] );

解析字符串

$tokenizer->tokenize( $sql );

获取结果令牌

$tokens = $tokenizer->shiftAllStack();