2012-10-15 41 views
5

Tôi đang cố gắng sử dụng jparsec để xác định và sử dụng ngữ pháp khá đơn giản của tôi, nhưng tôi hoàn toàn bối rối về cách thực hiện nó. Tôi không biết tại thời điểm này cho dù đó là sự hiểu biết không đầy đủ của tôi về không gian vấn đề, hoặc tài liệu thưa thớt và không rõ ràng của jparsec. Hoặc cả hai.Lẫn lộn trong jparsec

Tôi có một cái gì đó ngữ pháp như thế này:

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

Vì vậy, bạn có thể thấy nó hỗ trợ các nhà khai thác như AND, OR, NOT, IN, =, <>. Nó cũng hỗ trợ các dấu ngoặc đơn tùy ý để ra lệnh ưu tiên.

Tôi nghĩ rằng tôi đã khá xa vời với cách viết hoa. Dưới đây là những gì tôi có:

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 đèo, vì vậy tôi nghĩ rằng nó hoạt động OK.

Bây giờ, tôi đã có một hệ thống phân cấp kiểu đại diện cho cú pháp. Ví dụ: tôi có các lớp được gọi là Node, BinaryNode, FieldNode, LogicalAndNode, ConstantNode và vân vân. Và những gì tôi đang cố gắng làm là tạo ra một Parser lấy mã của tôi và nhổ ra một số Node. Và đây là nơi tôi tiếp tục gặp khó khăn.

tôi nghĩ rằng tôi muốn bắt đầu với một cái gì đó thực sự đơn giản như thế này:

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()); 
     } 
    }); 

tôi nghĩ rằng tôi muốn có thể làm điều này:

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

Nhưng điều đó mang lại cho tôi một lỗi biên dịch:

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

Vì vậy, có vẻ như generics của tôi bị siết chặt ở đâu đó, nhưng tôi không biết làm cách nào hoặc cách khắc phục điều này. Tôi thậm chí không chắc chắn tôi đang đi về điều này một cách đúng đắn. Ai có thể khai sáng cho tôi không?

Trả lời

6

Bạn đang trộn hai cấp độ khác nhau của "trình phân tích cú pháp": trình phân tích cú pháp cấp chuỗi. các trình quét hoặc lexers và các trình phân tích cú pháp cấp mã thông báo. Đây là cách JParsec thực hiện phân tách truyền thống về phân tích từ vựng và cú pháp.

Để làm cho mã của bạn được biên dịch một cách rõ ràng, bạn có thể thêm một cuộc gọi vào phương thức .cast() ở cuối định nghĩa của trình phân tích cú pháp, nhưng điều này sẽ không khắc phục được sự cố của bạn vì lỗi tiếp theo bạn sẽ có là cannot run a character-level parser at token level. Vấn đề này xuất phát từ việc sử dụng .from() để xác định trình phân tích cú pháp cấp cao nhất của bạn, điều này ngầm đặt ranh giới giữa hai thế giới.

Đây là một thực hiện làm việc (và kiểm tra đơn vị) cho phân tích cú pháp của bạn:

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; 
    } 
} 

}

gì tôi đã thay đổi là:

  • tôi sử dụng phương pháp Terminals.caseSensitive để tạo ra một lexer chỉ dành cho thiết bị đầu cuối (từ khóa, toán tử và số nhận dạng).Lexer nhận dạng sử dụng là mặc nhiên một trong những cung cấp natively bởi jParsec (ví dụ. Terminals.IDENTIFIER),
  • tôi sử dụng phương pháp .from() với tokenizer và WHITESPACES như tách,
  • các fieldNodeParser sử dụng Terminals.fragment(...) để phân tích tokens và không ký tự.

Hy vọng rằng sẽ giúp, Arnaud