6

我正在尝试使用 jparsec 来定义和利用我相当简单的语法,但我完全不知道如何去做。在这一点上我不知道是我对问题空间的理解不足,还是 jparsec 的稀疏和无信息文档。或两者。

我有一个语法是这样的:

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

所以你可以看到它支持诸如AND, OR, NOT, IN, =,之类的运算符<>。它还支持任意嵌套的括号来指示优先级。

我认为我在标记化方面已经取得了相当大的进展。这是我所拥有的:

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通过,所以我认为它工作正常。

现在,我已经有了一个表示语法的类型层次结构。例如,我有名为Node, BinaryNode, 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 ?>)

所以看起来我的泛型在某个地方被scewed,但我不知道在哪里或如何解决这个问题。我什至不确定我是否会以正确的方式处理这件事。任何人都可以启发我吗?

4

1 回答 1

6

您正在混合两个不同级别的“解析器”:字符串级别的解析器。扫描仪或词法分析器,以及令牌级解析器。这就是 JParsec 如何实现词法分析和句法分析的传统分离。

为了使您的代码编译干净,您可以.cast()在解析器定义的末尾添加对方法的调用,但这不会解决您的问题,因为您将遇到的下一个错误将类似于cannot run a character-level parser at token level. 这个问题来自于.from()定义你的顶级解析器,它隐式地设置了两个世界之间的边界。

这是您的解析器的工作实现(和单元测试):

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 回答