6

jparsec を使用して、かなり単純な文法を定義および利用しようとしていますが、その方法について完全に混乱しています。問題空間に対する私の不十分な理解なのか、それとも jparsec のまばらで有益でないドキュメントなのか、現時点ではわかりません。または両方。

私はこのようなグラマーを持っています:

foo='abc' AND bar<>'def' OR (biz IN ['a', 'b', 'c'] AND NOT baz = 'foo')

、、、、、、などANDの演算子ORをサポートしていることがわかります。また、優先順位を指定するために、任意にネストされた括弧もサポートしています。NOTIN=<>

トークン化についてはかなり進んだと思います。ここに私が持っているものがあります:

public final class NewParser {
    // lexing
    private static final Terminals OPERATORS = Terminals.operators("=", "OR", "AND", "NOT", "(", ")", "IN", "[", "]", ",", "<>");
    private static final Parser<?> WHITESPACE = Scanners.WHITESPACES;
    private static final Parser<?> FIELD_NAME_TOKENIZER = Terminals.Identifier.TOKENIZER;
    private static final Parser<?> QUOTED_STRING_TOKENIZER = Terminals.StringLiteral.SINGLE_QUOTE_TOKENIZER.or(Terminals.StringLiteral.DOUBLE_QUOTE_TOKENIZER);
    private static final Parser<?> IGNORED = Parsers.or(Scanners.WHITESPACES).skipMany();
    private static final Parser<?> TOKENIZER = Parsers.or(OPERATORS.tokenizer(), WHITESPACE, FIELD_NAME_TOKENIZER, QUOTED_STRING_TOKENIZER).many();

    @Test
    public void test_tokenizer() {
        Object result = TOKENIZER.parse("foo='abc' AND bar<>'def' OR (biz IN ['a', 'b', 'c'] AND NOT baz = 'foo')");
        Assert.assertEquals("[foo, =, abc, null, AND, null, bar, <>, def, null, OR, null, (, biz, null, IN, null, [, a, ,, null, b, ,, null, c, ], null, AND, null, NOT, null, baz, null, =, null, foo, )]", result.toString());
    }
}

test_tokenizerパスなので、問題なく動作していると思います

これで、構文を表す型階層が既にできました。たとえば、、、、、などNodeBinaryNode呼ばFieldNodeれるクラスがLogicalAndNodeあります。ConstantNodeそして、私がやろうとしているのParserは、トークンを取得して を吐き出す を作成することNodeです。そして、これは私が立ち往生し続ける場所です。

私は、次のような非常に単純なものから始めようと考えました。

private static Parser<FieldNode> fieldNodeParser =
    Parsers.sequence(FIELD_NAME_TOKENIZER)
    .map(new Map<Object, FieldNode>() {
        @Override
        public FieldNode map(Object from) {
            Fragment fragment = (Fragment)from;
            return new FieldNode(fragment.text());
        }
    });

私はこれを行うことができると思いました:

public static Parser<Node> parser = fieldNodeParser.from(TOKENIZER);

しかし、それは私にコンパイルエラーを与えます:

The method from(Parser<? extends Collection<Token>>) in the type Parser<FieldNode> is not applicable for the arguments (Parser<capture#6-of ?>)

したがって、ジェネリックがどこかで壊れているように見えますが、これをどこでどのように修正すればよいかわかりません。私はこれについて正しい方法で行っているかどうかさえ確信が持てません。誰でも私を啓発できますか?

4

1 に答える 1

6

2 つの異なるレベルの「パーサー」を混在させています。文字列レベルのパーサーとも呼ばれます。スキャナーまたはレクサー、およびトークンレベルのパーサー。これが、JParsec が字句解析と構文解析の従来の分離を実装する方法です。

コードをきれいにコンパイルするために、.cast()パーサーの定義の最後にメソッドの呼び出しを追加できますが、次のエラーは のようなものになるため、これは問題を解決しませんcannot run a character-level parser at token level。この問題は.from()、2 つの世界の間の境界を暗黙的に設定するトップレベルのパーサーを定義するために を使用することに起因します。

パーサーの実際の実装 (および単体テスト) は次のとおりです。

public class SampleTest {


private static Parser<FieldNode> fieldNodeParser = Parsers.sequence(Terminals.fragment(Tokens.Tag.IDENTIFIER).map(new Map<String, FieldNode>() {
            @Override
            public FieldNode map(String from) {
                String fragment = from;
                return new FieldNode(fragment);
            }
        })).cast();

public static Parser<FieldNode> parser = fieldNodeParser.from(NewParser.TOKENIZER, Scanners.WHITESPACES);


@Test
public void test_tokenizer() {
    Object result = Parsers.or(NewParser.TOKENIZER, Scanners.WHITESPACES.cast()).many().parse("foo='abc' AND bar<>'def' OR (biz IN ['a', 'b', 'c'] AND NOT baz = 'foo')");
    Assert.assertEquals("[foo, =, abc, null, AND, null, bar, <>, def, null, OR, null, (, biz, null, IN, null, [, a, ,, null, b, ,, null, c, ], null, AND, null, NOT, null, baz, null, =, null, foo, )]", result.toString());
}

@Test
public void test_parser() throws Exception {
    FieldNode foo = parser.parse("foo");
    assertEquals(foo.text, "foo");
}

public static final class NewParser {
    // lexing
    static final Terminals OPERATORS = Terminals.operators("=", "OR", "AND", "NOT", "(", ")", "IN", "[", "]", ",", "<>");
    static final Parser<String> FIELD_NAME_TOKENIZER = Terminals.Identifier.TOKENIZER.source();
    static final Parser<?> QUOTED_STRING_TOKENIZER = Terminals.StringLiteral.SINGLE_QUOTE_TOKENIZER.or(Terminals.StringLiteral.DOUBLE_QUOTE_TOKENIZER);
    static final Terminals TERMINALS = Terminals.caseSensitive(new String[] { "=", "(", ")", "[", "]", ",", "<>" }, new String[] { "OR", "AND", "NOT", "IN" });
    static final Parser<?> TOKENIZER = Parsers.or(TERMINALS.tokenizer(), QUOTED_STRING_TOKENIZER);
}

private static class FieldNode {
    final String text;

    public FieldNode(String text) {

        this.text = text;
    }
}

}

私が変更したのは次のとおりです。

  • このメソッドを使用してTerminals.caseSensitive、端末専用 (キーワード、演算子、および識別子) のレクサーを作成します。使用される識別子レクサーは、暗黙的に jParsec によってネイティブに提供されるものです (例: Terminals.IDENTIFIER) 。
  • .from()は TOKENIZER を使用した方法を使用し、WHITESPACESセパレーターとして、
  • 文字ではなくトークンを解析するためにfieldNodeParser使用します。Terminals.fragment(...)

お役に立てば幸いです、アルノー

于 2012-11-02T09:30:31.057 に答える