将 Excel 公式解析为语法树

v1.1.1 2023-11-24 14:38 UTC

This package is auto-updated.

Last update: 2024-09-24 16:20:33 UTC


README

vexcel

允许在您的应用程序中使用 Excel 公式。

描述

Excel 公式是一种伟大的发明,它允许用户在不依赖程序员的情况下进行计算。经常会有这种情况,在您的应用程序中需要这样的功能!这个库允许您使用类似于 Excel 公式的表达式,唯一的区别是,您可以使用自己的变量而不是单元格坐标。

演示

http://vexcel.vladimir-sobolev.ru

特性

  • 类似于 Excel 的语法。
  • 支持外部定义的变量。
  • 数学运算顺序(例如:2+2*3 得到 8)。
  • 支持函数(IF、NOT、ROUND 等)。
  • 可以添加自己的函数。
  • 将语法树转换为 JSON 并反向转换。
  • 将语法树中的变量存储为标识符。
  • 可以将语法树反向转换为公式。(如果变量名称可能随时间改变,这很有用)。

语法

字符串

以单引号或双引号开头和结尾。例如:"我的字符串"'我的字符串'

数字

数字有两种类型:整数和小数。小数用点表示。

整数示例:1200

小数示例:3.2

变量

变量名称必须以字母开头,可以包含字母、数字和下划线。例如:ВасяМоя_ПЕР

变量还可以包含任何符号,包括空格,如果变量名称用 '$' 或 '' 包围。例如:$Моя ПЕР$,*\Моя ПЕР*

函数

函数名称必须以字母开头,可以包含字母、数字和下划线。函数名称后跟一个圆括号,其中传递函数的参数,参数之间用冒号分隔。函数在关闭括号后结束。例如:МОЯ_ФУНКЦИЯ(ПЕР1; ПЕР2)

安装

composer require sobolevwladimir/vexcel

开始

连接

<?php
...
use SobolevWladimir\Vexcel\Parser\Parser;

接下来,我们可以将我们的公式转换为抽象语法树(以下简称 AST)。

$parser = new Parser();
$ast = $parser->parse('3+3');// Получили синтаксическое дерево

要获取值,请调用 calculate() 函数

$answer = $ast->calculate(); // $answer = 6

为什么这里有这么多步骤?因为计算是在 AST 中进行的,您只需要解析一次公式,然后,在改变变量的过程中,计算新字段的价值(关于变量 下面)。例如

$parser = new Parser();
$ast = $parser->parse('ВАША_ПЕРЕМЕННАЯ+3');// Получили синтаксическое дерево

foreach($repositorys as $repository) {
    $answer = $ast->calculate($repository);
    // ... you code
}

在公式中使用变量

要使用变量,我们需要向系统解释变量的值。为此,创建一个实现 ValueRepository 接口的类。例如,让我们假设我们有变量,其名称对应于数字("ПЕРЕМЕН_ОДИН"=1, "ПЕРЕМЕН_ДВА"=2, "ПЕРЕМЕН_ТРИ"=3 等)。那么这个类的样子将是

use SobolevWladimir\Vexcel\Repository\ValueRepositoryInterface;

class ValueRepositoryFake implements ValueRepositoryInterface
{
    /** @var array<string, int> */
    private array $variables = [
        'ПЕРЕМЕН_ОДИН'   => 1,
        'ПЕРЕМЕН_ДВА'    => 2,
        'ПЕРЕМЕН_ТРИ'    => 3,
        'ПЕРЕМЕН_ЧЕТЫРЕ' => 4,
        'ПЕРЕМЕН_ПЯТЬ'   => 5,
        'ПЕРЕМЕН_ШЕСТЬ'  => 6,
    ];

    public function getValueByIdentifier(string $identificator): mixed
    {
        return $this->variables[$identificator];
    }

   
}

函数getValueByIdentifier接受变量标识符(默认标识符等于变量名称)。了解更多:存储公式在数据库中

然后我们将这个类的实例传递给计算器

$parser = new Parser();
$ast = $parser->parse('ПЕРЕМЕН_ТРИ+3');// Получили синтаксическое дерево

$repository = new ValueRepositoryFake();// Наш репозиторий

$answer = $ast->calculate($repository); // $answer = 6

使用函数

$parser = new Parser();
$ast = $parser->parse('ЕСЛИ(НЕ(3<2);"ДА";"НЕТ")');// Получили синтаксическое дерево
$answer = $ast->calculate(); // $answer = 'ДА'

目前默认实现了以下函数:'IF', 'NOT', 'ROUNDUP', 'ROUNDDOWN'。如果您需要实现自己的函数,则创建一个继承自FunctionBuilder的类并重写"build"方法

$parser = new Parser(functionBuilder: new YouFunctionBuilder());
$ast = $parser->parse('МОЯФУНЦИЯ(3<2)');// Получили синтаксическое дерево
$answer = $ast->calculate(); 

存储公式在数据库中

如果您的公式中有变量名称可能随时间变化的变量(例如:您的变量存储在数据库中),则存储用户输入的公式而不是公式本身,并在其中存储这些变量的恒定标识符是有意义的。然后在将树转换回公式时,将根据标识符恢复变量名称。

为此

  1. 创建一个实现VariableRepositoryInterface接口的类,并将其传递给Parser()构造函数。
  2. 通过调用$parser->parse()将公式转换为AST。
  3. 将此树以json格式保存在数据库中(json_encode($ast))。

要将json转换回语法树,执行以下步骤

$ast = FormulaAST::fromJson(json_decode((string)$formulaJson, true)); 

然后可以将此树转换回公式

$encoder = new VexcelEncoder($youVariableRepository); 
$code = $ast->toCode($encoder);