didix16 / php-interpreter
一个用 PHP 编写的简单解释器。它允许解析抽象语言的标记并执行你想要的操作
1.0.4
2021-08-19 09:18 UTC
Requires
- didix16/php-datatransformer: ^1.0
- didix16/php-grammar: ^1.0
README
一个用 PHP 编写的简单解释器。它允许解析抽象语言的标记并执行你想要的操作
内容
什么是解释器
解释器是一种软件,它允许解析任何语言的标记数组,并赋予它们“意义”。例如,您可以处理一个函数、调用一个服务、做一些有趣的事情...
例如,假设我们有一个 APILexer 和 APIParser,它们理解一种元语言,用于编写与 REST API 相关的指令
$script = " GET https://some-awesome-api.com/endpoint?token=TOKEN PIPETO $mySuperService SAVEDB localhost:10000 "; $lexer = new APILexer($script); $parser = new APIParser($lexer); $tokens = $parser->parse(); // var_dump($tokens) should be: array(12) { [0]=> object(Token)#4 (2) { ["value":protected]=> string(3) "GET" ["type":protected]=> int(2) } [1]=> object(Token)#2 (2) { ["value":protected]=> string(1) " " ["type":protected]=> int(3) } [2]=> object(Token)#5 (2) { ["value":protected]=> string(49) "https://some-awesome-api.com/endpoint?token=TOKEN" ["type":protected]=> int(4) } [3]=> object(Token)#6 (2) { ["value":protected]=> string(1) " " ["type":protected]=> int(5) } [4]=> object(Token)#7 (2) { ["value":protected]=> string(6) "PIPETO" ["type":protected]=> int(2) } [5]=> object(Token)#8 (2) { ["value":protected]=> string(1) " " ["type":protected]=> int(3) } [6]=> object(Token)#9 (2) { ["value":protected]=> string(15) "$mySuperService" ["type":protected]=> int(4) } [7]=> object(Token)#10 (2) { ["value":protected]=> string(1) " " ["type":protected]=> int(5) } [8]=> object(Token)#11 (2) { ["value":protected]=> string(6) "SAVEDB" ["type":protected]=> int(2) } [9]=> object(Token)#12 (2) { ["value":protected]=> string(1) " " ["type":protected]=> int(3) } [10]=> object(Token)#13 (2) { ["value":protected]=> string(15) "localhost:10000" ["type":protected]=> int(4) } [11]=> object(Token)#14 (2) { ["value":protected]=> string(5) "<EOF>" ["type":protected]=> int(1) } }
使用这些标记,我们可以做一些魔法般的事情,创建一个解释器来处理它们。例如,我们可以称它为:APIInterpreter。
public class APIInterpreter extends Interpreter { public function __construct(APIParser $parser) { parent::__construct($parser, null); $this // Allows make GET using an http client or whatever... ->loadFunction(new APIGetFunction(...)) // Allows to interact with DDBB and store values ->loadFunction(new DBFunction(...)) // ...whatever } protected function consume(): TokenInterface { $token = parent::consume(); if( $token->getValue() !== Lexer::EOF_VALUE && $token->getType() === YOUR_TYPE) //do something return $token; } /** * Execute this interpreter using the parsed tokens */ public function run() { // All your tokens processing logic goes here $token = $this->lookahead(); // do something with GET token and whitespace token, or whatever... do { //... }while(($token = $this->lookahead())); } // Write your auxiliar functions here /** * this one could parse each starting command line: * GET, PIPETO, SAVEDB, etc... */ public function processCommand() { ... } /** * This one could resolve some service from PIPETO command... */ public function processService() { ... } }
这是一个非常简单的例子。您需要发挥想象力并做一些特别的事情 :)
安装
composer require didix16/php-interpreter
使用
使用上面的例子,我将展示如何简单地指导 APIInterpreter 在读取我们伟大的 APIScript 时应该如何行动
一个简单的脚本结构可能如下所示
GET https://some-awesome-api.com/endpoint?token=TOKEN
PIPETO $mySuperService
SAVEDB localhost:10000
我们可以识别每一行,每一行都是一个动作。每个动作都有一个键名、一个空格和一个参数(它可能是一组参数)
所以,假设我们需要识别以下标记
T_ACTION、T_WHITESPACE、T_ARG 和 T_NEWLINE
<?php // APILexer.php use didix16\Grammar\Lexer; use didix16\Grammar\Token; use didix16\Interpreter\Interpreter; // Imported an APIParser use ...\APIParser; public class APIGetFunction extends InterpreterFunction { public function __construct() { parent::__construct("GET"); } public function run(...$args) { /** * $url = $args[0]; * return request()->get($url) * or whatever */ } } public class DBFunction extends InterpreterFunction { public function __construct() { parent::__construct("SAVEDB"); } public function run(...$args) { /** * Get the data to be stored to DDBB. * Preprocess the data if you need it * * return DDBB::connection($args[0])->store($data) */ } } public class APIInterpreter extends Interpreter { /** * Hold API response result, for example or result from executed function */ private $currentResult; public function __construct(APIParser $parser) { parent::__construct($parser, null); $this // Allows make GET using an http client or whatever... ->loadFunction(new APIGetFunction(...)) // Allows to interact with DDBB and store values ->loadFunction(new DBFunction(...)) // ...whatever } /** * Execute this interpreter using the parsed tokens */ public function run() { // All your tokens processing logic goes here $token = $this->lookahead(); do { switch($token->getType()){ case APILexer::T_ACTION: $this->processCommand(); break; default: // ignore whitespaces and newlines // arguments should never be at start of a line $this->consume(); } }while(($token = $this->lookahead())); // Returns something meaningful to understand that your interpreter has been finished ok // Or return some data if needs to } // Write your auxiliar functions here /** * this one could parse each starting command line: * GET, PIPETO, SAVEDB, etc... */ public function processCommand() { $command = $this->consume()->getValue(); $args = []; while($this->lookahead()->getType() !== APILexer::T_NEWLINE) { if($this->lookahead()->getType() === APILexer::T_WHITESPACE) $this->consume(); // ' ' else $args[] = $this->processCommandArg(); } $this->consume(); // consume new line $this->currentResult = $this->executeFunction($command, $args); } public function processCommandArg() { $value = $this->consume()->getValue(); // make some processing of the argument if you need... //transform($value); /** * if $value === $mySuperService then * import superService or whatever */ return $value; } /** * This one could resolve some service from PIPETO command... */ public function processService() { ... // call the imported service and do something with previous result // Note that services array must be defined $this->services[$mySuperService]->doSomething($this->currentResult); } }
现在,使用解释器,您可以根据每个命令执行一些操作。您可以自由地添加您需要的任何命令。
也查看
- php-grammar - 一个简单的库,用于创建构建语言所需的词法和语法分析器。