解析數學表達式 代碼解析AST語法樹


2019年2月20日09:18:22

 

AST語法樹自己寫代碼解析的話就比較麻煩,有現成的庫可以解析PHP,就像webpack就是自己解析js的語法代碼,編譯成各種版本的可用代碼

github https://github.com/josdejong/mathjs

 

Extension Description
mathsteps A step-by-step math solver library that is focused on pedagogy (how best to teach). The math problems it focuses on are pre-algebra and algebra problems involving simplifying expressions.
mathjs‑expression‑parser This custom build of mathjs contains just the expression parser and basic arithmetic functions for numbers. About four times as small as the full mathjs library.
mathjs-simple-integral Extends Math.js to be able to compute simple integrals.
math.diff.js Symbolic differentiation plugin for Math.js
postcss-math PostCSS plugin for making calculations with math.js

 

 沒有辦法,自己去實現  前綴,中綴,后綴表達式來實現解析字符串,對於簡單的加減乘除都是比較容易的,但是需要支持一些復雜一點邏輯的計算就比較麻煩,比如開方,乘方等

 

其他一些解析工具基本都是java ,c,cpp的

又嘗試找了一些工具,發現JavaScript里面有一些,但是不符合我的個人需求,但是可以滿足大部分,簡單數學字符數解析和計算

http://mathjs.org   

PHP可用的庫

composer require nikic/php-parser

一直在更新可以使用

namespace App\Http\Controllers\Data\V2;

use App\Http\Controllers\Data\V2\BaseController as Base;

use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;

class CommonController extends Base {

    public static function index(Request $Request) {
        $code = <<<CODE
<?php
((99 + 1)*4-1);
CODE;
        $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
        try {
            $ast = $parser->parse($code);
        } catch (Error $error) {
            echo "Parse error: {$error->getMessage()}\n";
            return;
        }

        $dumper = new NodeDumper;
        echo $dumper->dump($ast);

結果

我是使用pre打印的

array(
    0: Stmt_Expression(
        expr: Expr_BinaryOp_Minus(
            left: Expr_BinaryOp_Mul(
                left: Expr_BinaryOp_Plus(
                    left: Scalar_LNumber(
                        value: 99
                    )
                    right: Scalar_LNumber(
                        value: 1
                    )
                )
                right: Scalar_LNumber(
                    value: 4
                )
            )
            right: Expr_BinaryOp_Plus(
                left: Scalar_LNumber(
                    value: 1
                )
                right: Scalar_LNumber(
                    value: 2
                )
            )
        )
    )
)

 

基本就可以達到語法樹解析,元素自己解析就可以了,但是支持的運算符只能支持官方已有的運算符,特殊的運算符或者自定義的運算符,得自己標記去解析,特別復雜的需要自己根據解析的ast去重新轉換成自己實際業務的需求

如果你需要添加一個vistor 遍歷真個ast樹 ,並獲取,修改數據 

 

<?php

namespace App\Service;

use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;

class Visitor extends NodeVisitorAbstract {

    public $data;
    public $operator;

    public function __construct() {
        $this->data = new \SplStack();
        $this->operator = new \SplStack();
    }

    public function leaveNode(Node $node) {
        //所有的符號
        if ($node instanceof Node\Expr) {
            $this->operator->push($node->getType());
        }
        //所有運算符
//        if ($node instanceof Node\Expr\BinaryOp) {
//            $this->operator->push($node->getType());
//        }
//        $this->operator->push($node->getType());
        if ($node instanceof Node\Scalar) {
            $this->data->push((string) $node->value);
        }
    }

    public function getData() {
        return $this->data;
    }

    public function getOperator() {
        return $this->operator;
    }

}

然后在解析ast樹的代碼添加vistor

 

   public function TransformToAst($string = '') {
        try {
            if (empty($string)) {
                throw new \Exception('公式不能為空');
            }
            $code = <<<CODE
<?php
$string;
CODE;
            $ParserFactory = new ParserFactory();
            $parser = $ParserFactory->create(ParserFactory::PREFER_PHP7);
            $ast = $parser->parse($code);

            $traverser = new NodeTraverser;
            $Visitor = new Visitor();
            $traverser->addVisitor($Visitor);
            
            $modifiedStmts = $traverser->traverse($ast);
            p($Visitor->getOperator());
            pp($Visitor->getData());
//            pp($modifiedStmts);
//           
            die;


            if (empty($ast_object)) {
                throw new \Exception('解析表達式為空');
            }
            $ast_new = self::ParseAstToArray($ast_object['0']);
            return $ast_new;
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage() . $e->getFile() . $e->getLine());
        }
    }

其他提供的很多工具類,但是缺少demo實例,讓處入手的人很難直接上手使用

 參考 https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown 以后有時間在翻譯一下文檔

Microsoft/tolerant-php-parser

這個微軟的庫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM