目次
QLExpress
github: https://github.com/alibaba/QLExpress
アドバンテージ:
- スレッド セーフ。エンジン動作中に生成される一時変数はすべてスレッドローカル タイプです。
- 効率的な実行、時間のかかるスクリプトのコンパイル プロセスはローカル マシンにキャッシュでき、実行時の一時変数の作成にはバッファ プール テクノロジが使用され、Groovy のパフォーマンスに匹敵します。
- 弱い型指定のスクリプト言語は、groovy および JavaScript 構文に似ており、強く型指定されたスクリプト言語よりも低速ですが、ビジネスの柔軟性が大幅に向上します。
- セキュリティ制御では、関連する動作パラメータを設定することで、無限ループや高リスクのシステム API 呼び出しなどを防止できます。
- コードは合理化されており、依存関係は最小限であり、250k jar パッケージはすべての Java オペレーティング環境に適しており、Android システムのローエンド pos マシンでも広く使用されています。
- 高精度コンピューティングのサポート
—————————————————
著作権に関する声明: この記事は、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 コマンドを正しく実行して結果を取得します。