ルール エンジン -- QLExpress: 通常の式の操作

QLExpress

github: https://github.com/alibaba/QLExpress

アドバンテージ:

  1. スレッド セーフ。エンジン動作中に生成される一時変数はすべてスレッドローカル タイプです。
  2. 効率的な実行、時間のかかるスクリプトのコンパイル プロセスはローカル マシンにキャッシュでき、実行時の一時変数の作成にはバッファ プール テクノロジが使用され、Groovy のパフォーマンスに匹敵します。
  3. 弱い型指定のスクリプト言語は、groovy および JavaScript 構文に似ており、強く型指定されたスクリプト言語よりも低速ですが、ビジネスの柔軟性が大幅に向上します。
  4. セキュリティ制御では、関連する動作パラメータを設定することで、無限ループや高リスクのシステム API 呼び出しなどを防止できます。
  5. コードは合理化されており、依存関係は最小限であり、250k jar パッケージはすべての Java オペレーティング環境に適しており、Android システムのローエンド pos マシンでも広く使用されています。
  6. 高精度コンピューティングのサポート
    —————————————————
    著作権に関する声明: この記事は、CSDN ブロガー「Bao Zidan」によるオリジナルの記事であり、CC 4.0 BY-SA 著作権契約に従っています。ソースリンクとこの声明を転載するための原文を添付してください。
    元のリンク: https://blog.csdn.net/weixin_37590206/article/details/108344988

実行中のアーキテクチャ図:
ここに画像の説明を挿入

通常の式の実行

@Test
public void testDemo() throws Exception {
    
    
    String express = "10 * 10 + 1 + 2 * 3 + 5 * 2";
    ExpressRunner runner = new ExpressRunner();
    Object r = runner.execute(express, null, null, false, false);
    Assert.assertTrue("表达式计算", r.toString().equalsIgnoreCase("117"));
    System.out.println("表达式计算:" + express + " = " + r);
}

デバッグ:
ここに画像の説明を挿入

解析して ExpressNode に変換する

ここに画像の説明を挿入
ここに画像の説明を挿入

文法解析では、次の構文ツリーを取得します。

ここに画像の説明を挿入

これは、10 * 10 + 1 + 2 * 3 + 5 * 2同様の接尾辞表現 (逆ポーランド語) に変換されます。10 10 * 1 + 2 3 * + 5 2 * +

1:   STAT_BLOCK:STAT_BLOCK                                                         	STAT_BLOCK
2:      STAT_SEMICOLON:STAT_SEMICOLON	STAT_SEMICOLON
3:         +:+	+
4:            +:+	+
5:               +:+	+
6:                  *:*	*
7:                     10:CONST_INTEGER	CONST
7:                     10:CONST_INTEGER	CONST
6:                  1:CONST_INTEGER	CONST
5:               *:*	*
6:                  2:CONST_INTEGER	CONST
6:                  3:CONST_INTEGER	CONST
4:            *:*	*
5:               5:CONST_INTEGER	CONST
5:               2:CONST_INTEGER	CONST

ExpressNode ツリーに基づいて命令ツリーを生成する

ここに画像の説明を挿入

つまり、後置式 (逆ポーランド語) に従って、10 10 * 1 + 2 3 * + 5 2 * +次のようになります。

1:LoadData 10
2:LoadData 10
3:OP : * OPNUMBER[2]
4:LoadData 1
5:OP : + OPNUMBER[2]
6:LoadData 2
7:LoadData 3
8:OP : * OPNUMBER[2]
9:OP : + OPNUMBER[2]
10:LoadData 5
11:LoadData 2
12:OP : * OPNUMBER[2]
13:OP : + OPNUMBER[2]

命令ツリーを実行して結果を取得します

private Object executeReentrant(InstructionSet sets, IExpressContext<String, Object> iExpressContext,
       List<String> errorList, boolean isTrace, boolean isCatchException) throws Exception {
    
    
       try {
    
    
           int reentrantCount = threadReentrantCount.get() + 1;
           threadReentrantCount.set(reentrantCount);

           return reentrantCount > 1 ?
               // 线程重入
               InstructionSetRunner.execute(this, sets, this.loader, iExpressContext, errorList, isTrace,
                   isCatchException, true, false) :
               InstructionSetRunner.executeOuter(this, sets, this.loader, iExpressContext, errorList, isTrace,
                   isCatchException, false);
       } finally {
    
    
           threadReentrantCount.set(threadReentrantCount.get() - 1);
       }
   }

ここに画像の説明を挿入
結果を取得するための具体的な実行プロセスは次のとおりです。

structConstDataの命令実行

 @Override
    public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
    
    
        environment.push(this.operateData);
        environment.programPointAddOne();
    }

com.ql.util.express.RunEnvironment#push

public void push(OperateData data) {
    
    
       this.point++;
       if (this.point >= this.dataContainer.length) {
    
    
           ensureCapacity(this.point + 1);
       }
       this.dataContainer[point] = data;
   }

オペランドを追加

命令Operatorの命令実行

com.ql.util.express.instruction.detail.structOperator#execute

ここに画像の説明を挿入

特定の命令に従ってフェッチして実行します

 @Override
    public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
    
    
        InstructionSetContext instructionSetContext = environment.getContext();
        ArraySwap parameters = environment.popArray(this.opDataNumber);
        try {
    
    
            OperateData result = this.operator.execute(instructionSetContext, parameters, errorList);
            environment.push(result);
            environment.programPointAddOne();
        } catch (QLException e) {
    
    
            throw new QLException(getExceptionPrefix(), e);
        } catch (Throwable t) {
    
    
            throw new QLBizException(getExceptionPrefix(), t);
        }
    }

ついに結果が出ました

com.ql.util.express.structSetRunner#execute
ここに画像の説明を挿入

特記事項: 命令の繰り返し生成を回避し、操作効率を向上させるために、コード実行中にさまざまなキャッシュが存在します。

in 式の設定パラメータの実行を見てください。

  @Test
    public void testOperatorIn() throws Exception {
    
    
        String express1 = "2 in (2, 3) ";
        String express2 = "2 in a";
        String express3 = "2 in b";

        ExpressRunner runner = new ExpressRunner(true, true);
        DefaultContext<String, Object> context = new DefaultContext<>();
        int[] a = {
    
    1, 2, 3};
        context.put("a", a);
        List<Integer> b = new ArrayList<>();
        b.add(2);
        b.add(3);

        context.put("b", b);
        System.out.println(runner.execute(express1, context, null, false, false));
        System.out.println(runner.execute(express2, context, null, false, false));
        System.out.println(runner.execute(express3, context, null, false, false));
    }

式「2 in b」の構文ツリーは次のように解析されます。

1:   STAT_BLOCK:STAT_BLOCK                                                         	STAT_BLOCK
2:      STAT_SEMICOLON:STAT_SEMICOLON	STAT_SEMICOLON
3:         in:in	in
4:            2:CONST_INTEGER	CONST
4:            b:ID	ID

実行コマンド:
ここに画像の説明を挿入

in 演算子を実行すると、ここに画像の説明を挿入

b パラメータを取得し、com.ql.util.express.instruction.opdata.OperateDataAttr#getObjectInnerそれを呼び出してコンテキストから値を取得します。
ここに画像の説明を挿入
最後に in コマンドを正しく実行して結果を取得します。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_26437925/article/details/131140000