web-dev-qa-db-ja.com

条件式をデータベースに保存する

ユーザーがbound op x op bound2の形式で条件を入力できるアプリケーションがあり、これを文字列として保存し、実行時に解析して評価します。

非常に限られた条件付きステートメントでは、それはまともな作業量です。

少なくともswitchステートメントレベルの複雑さで、複雑な条件をシリアル化して評価する方法を探しています。

私はこの質問を見ました F#でラムダ式をシリアル化および逆シリアル化する方法? f#ではラムダをシリアル化できるだけです(テキスト文字列からラムダを取得する方法があると思います)、だが:

  1. Javaでこれを行う必要があります
  2. Javaコードをその場でコンパイルし、キーワードを削除してSystem.のようなものを禁止することで「安全」にすることができますが、セキュリティの悪夢でなくても、何千回も実行するのは非常に計算コストがかかります。

インタプリタが基本(条件、ループ、変数の割り当て)だけを実行してJavaとして実行できる、または実行時に定義されている必要があるこれらのタイプの式を実行する方法を知っている小さな言語を知っている人はいますか?持続する。

更新:明確にするために、必要な最低限の機能はif elseチェーンです。 1つの条件を評価する必要はありません。一つの条件は、私がすでに持っているものです。

3
soandos

また、非常に単純な式言語を作成して、その場で評価することもできます。式はツリーであるため、XML、JSON、s-expressions、または任意のものとしてシリアル化できます。例えば。 a + b * cはJSONでは["+", "a", ["*", "b", "c"]]として表すことができます。そのようなツリーのエバリュエーターを書くのも本当に簡単です。

次のようなifチェーン:

if a then x 
else if b then y
else if c then z
else q

次のようにJsonで表現できます:

["if", "a", "x", ["if", "b", "y", ["if", "c", "z", "q"]]]

疑似コードのインタプリタ:

def eval(args):
  operator = args[0]
  if operator=="+":
    return eval(args[1]) + eval(args[2])
  Elif operator =="*":
    return eval(args[1]) * eval(args[2])
  Elif operator=="<":
    return eval(args[1]) < eval(args[2])
  Elif operator=="if":
    if (eval(arg[1])):
        return eval(arg[2]) 
    else:
        return eval(arg[3])

等々...

もちろん、ラムダや関数を追加するとすぐに、さらに複雑になります。市販のスクリプトエンジンを使用することをお勧めします。ただし、式のみをサポートする必要がある場合は、独自のエバレーターを作成する方が簡単な場合があります。

3
JacquesB

「条件、ループ、変数の割り当て」は、注意しない限り、すでに任意の量のCPUを消費するのに十分です。

必要なものがすべて算術式である場合、いくつかの定義済みの演算/関数を使用して、それらのインタープリターを簡単に作成できます。また、ループを持つことができないため、式の実行時間を理解するのも簡単です。

私があなたの立場にあり、チューリング完全なスクリプト言語が必要な場合、私が最初にすべきことは、自分のロールバックを避けることです。

Java 6 スクリプトAPIを導入 。そのためのJavaScript実装があります。 Mozilla Rhino JavaでのJavaScript実装もあります。

私が知る限り、どちらの実装でも、JSプログラムの初期名前空間を制限して、明示的に渡されない機能にアクセスできないようにすることができます。

さらに、JVMにLua VMを実装する LuaJ があり、Luaにはより細かい実行制御/サンドボックス化が期待されます。

Groovy、Jython、JRubyを検討することもできますが、これらはかなり重く、サンドボックス化が難しいものでなければなりません。または、Goloを検討することもできます(サンドボックス化については考えていません)。

いずれにしても、別のスレッドでスクリプトを実行し、実行に割り当てられた時間よりも時間がかかるスクリプトを強制終了します。

そのようなスレッドからのヒープ割り当ての量を追跡して制限する方法も検討しますが、これはJavaでは不可能のようです。

厳密なパフォーマンスの考慮事項/ DoS保護が重要な場合は、おそらくLuaJを実行するか、JNIを介してC Lua VMを実行し、使用可能なCPUとRAMの量を完全に制御します。スクリプト化するオブジェクトに(小さな)プロキシのみを公開しました。

1
9000