6

私は今年のホリデーシーズンに飽きて、Java用の簡単なリスト内包/フィルタリングライブラリをランダムに作成することにしました(素晴らしいものがいくつかあることは知っていますが、それを自分で書きたかっただけです)。

このリストの場合:

LinkedList<Person> list = new LinkedList<Person>();
            list.add(new Person("Jack", 20));
            list.add(new Person("Liz", 58));
            list.add(new Person("Bob", 33));

構文は次のとおりです。

Iterable<Person> filtered = Query.from(list).where(
    Condition.ensure("Age", Op.GreaterEqual, 21)
    .and(Condition.ensure("Age", Op.LessEqual, 50));

私はその醜いことを知っていますが、静的インポートを使用し、より短いメソッド名を使用すると、かなり簡潔になります。

次の構文が最終的な目標です。

Iterable<Person> list2 = Query.from(list).where("x=> x.Age >= 21 & x.Age <= 50");

どうやら式の解析は私の最強の領域ではなく、ネストされた/複数の条件の解析に問題があります。誰かが私が役立つと思うかもしれないいくつかのリソース/文学を知っていますか?

現時点では、文字列ラムダ構文から正常に解析されている条件式は1つだけです"x=> x.Name == Jack"。私の基礎となるExpression構造はかなり堅固で、任意の量のネストを簡単に処理できます。問題は、文字列からの式の解析だけです。

ありがとう

キックのためだけに、舞台裏での発現構造がどのように機能するかについて少し洞察します(明らかに、次の例では「op.GreaterEqual」などを指定できますが、それがどのように柔軟であるかを示したいと思いました任意の量のネスト):

Condition minAge1 = Condition.ensure("Age", Op.Equal, 20);
Condition minAge2 = Condition.ensure("Age", Op.Greater, 20);
Expression minAge = new Expression(minAge1, Express.Or, minAge2);
Expression maxAge = Condition.ensure("Age", Op.Equal, 50).or(Condition.ensure("Age", Op.Less, 50));
Expression ageExpression = new Expression(minAge, Express.And, maxAge);

Condition randomException = Condition.ensure("Name", Op.Equal, "Liz");
Expression expressionFinal = new Expression(ageExpression, Express.Or, randomException);
4

3 に答える 3

5

基本的に必要なのは、式の再帰下降パーサーです。これはコンパイラ理論で大きく取り上げられているトピックなので、コンパイラに関する本ならどれでもこのトピックをカバーできます。正式な文法用語では、次のようになります。

condition  : orAtom ('||' orAtom)+ ;
orAtom     : atom ('&&' atom)+ ;
atom       : '(' condition ')'
           | expression ;
expression : value OPER value ;
value      : VARIABLE | LITERAL '
VARIABLE   : (LETTER | '_') (LETTER | DIGIT | '_')* ;
LITERAL    : NUMBER
           | STRING ;
NUMBER     : '-'? DIGIT+ ('.' DIGIT+)? ;
STRING     : '"' . CHAR* . '"' '
CHAR       : ('\\' | '\"' | .) + ;
LETTER     : 'a'..'z' | 'A'..'Z' ;
DIGIT      : '0'..'9' ;
OPER       : '>' | '>=' | '<' | '<=' | '=' | '!=' ;

上記の文法は、私が最もよく知っているものとして、 (ほとんど) ANTLR形式です。

ブール式または算術式の構文解析は、構文解析を扱う際の古典的な入門トピックであるため、それに関する多くの文献を見つけることができるはずです。ANTLRを追求したい場合(Javaを使用しているため)、「決定的なANTLRリファレンス:ドメイン固有言語の構築」を読むことを強くお勧めします。

これがすべてやり過ぎのように見え、すべてを取り入れることが少し多い場合は、あなたは正しいかもしれません。始めるのは難しいトピックです。

あなたが持っている1つの選択肢は、任意の文字列式を作成するのではなく、代わりに流暢なインターフェイスを使用することです(あなたがしているように):

List results = from(source)
  .where(var("x").greaterThan(25), var("x").lessThan(50))
  .select("field1", "field2");

これは、式ツリーをコードで記述しているため、実装が簡単なはずです。

于 2010-01-17T08:01:51.077 に答える
1

cletusの答えに追加するには、最初にグラマーを定義する必要があります。

次の式グラマーは、ほとんどの場合に非常にうまく機能しますが、残念ながら、通常の再帰下降では、各プロダクションで最初に再帰部分を定義することはできません。これにより、スタックオーバーフローが発生するまで、プロダクションメソッドを再帰的に呼び出すことになります。

        orexpr :: = orexpr'|' andexpr  
                  | andexpr  

        andexpr :: = andexpr'&'比較
                   | 比較

        比較::=addexpr compareOp addexpr
                     | addexpr

        addexpr :: = addexpr'+' mulexpr
                   | addexpr'-' mulexpr
                   | mulexpr

        mulexpr :: =mulexpr'*'値
                   | mulexpr'/'値
                   | mulexpr'%'値
                   | 価値

        値::=整数
                   | 浮く
                   | 変数
                   | 引用
                   | '(' orexpr')'

通常の再帰下降では、mulexprを次のように定義する必要があります。

         mulexpr::=値'*'mulexpr  
                    | 値'/'mulexpr
                    | 値'%' mulexpr

ただし、この文法の問題は、操作の順序がすべて逆になるように式ツリーが作成されることです。

妥協:上記の元の文法で再帰下降を逆に使用します。式を右から左に解析します。右から左にツリーを構築します。操作の順序は保持されます。

再帰下降では、通常、プロダクションごとに解析メソッドを記述します。parseOr()メソッドは次のようになります。

プライベートMyExpressionparseOr(MyScannerスキャナー){
        MyExpression式=null;

        MyExpression rightExpr = parseAnd(scanner);
        トークントークン=scanner.getCurrentToken();
        if(token.hasValue( "|"){
            expression = new MyExpression();
            expression.setOperator(OR);
            トークンnextToken=Scanner.getNextToken(); //覚えておいてください、これは逆にスキャンしています
            MyExpression leftExpression = parseOr(scanner);
            expression.setLeft(leftExpression);
            expression.setRight(rightExpression);
        }
        そうしないと {
            式=rightExpression;
        }
        式を返します。
    }

于 2010-01-17T08:34:25.293 に答える
1

すべてのヒントをありがとう。私はこれのほとんどが必要以上のものであると判断したので、20〜30行のコードで簡単に解析できる管理可能なグループに物事を入れるためにそれを悔やむことになりました。

文字列LambdaExpressionインターフェイスは、流暢なインターフェイスとほぼ同じように機能しますが、1つまたは2つの小さなバグがあります。

楽しみのために少し開発を続けると思いますが、反射率が約90%なので、実際に使用するには明らかに非効率的です。

于 2010-01-21T04:53:58.153 に答える