2012-10-15 43 views
5

我试图使用jparsec来定义和使用我相当简单的语法,但是我对如何去做它完全感到困惑。在这一点上,我不知道这是否是我对问题空间的理解不足,或者是jparsec的稀疏和无用的文档。或两者。jparsec中的混淆

我有一个语法是这样的:

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

所以你可以看到它支持运营商如ANDORNOTIN=<>。它还支持任意嵌套的括号来决定优先级。

我想我用标记化得到了相当多的东西。下面是我有:

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 ?>) 

所以它看起来像我的仿制药在某处,但我不知道在哪里或如何解决这个问题。我甚至不确定我是否以正确的方式去解决这个问题。任何人都可以启发我吗?

回答

6

您正在混合两个不同级别的“解析器”:字符串级解析器aka。扫描仪或词法分析器以及令牌级解析器。这是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()方法与标记生成器和WHITESPACES作为分离器,
  • fieldNodeParser使用Terminals.fragment(...)解析令牌而不是字符。

希望帮助, 阿尔诺