Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Criando sua DSL com jparsec

Criando sua DSL com jparsec

Como criar uma DSL simples com a lib jparsec.

3c1b9c8d515093124b8c081107d4830c?s=128

Felipe Mamud

October 19, 2017
Tweet

Transcript

  1. CRIANDO SUA DSL COM JPARSEC DANIEL MEZZATTO @mezzatto FELIPE MAMUD

    @mamud
  2. COMO TUDO COMEÇOU... @masobrinho Ow mamud, tem uma lib dahora

    ai chamada jparsec...
  3. AGENDA • O que é? • Vantagens e Desvantagens •

    Motivação • Exemplo simples • Caso de uso em produção
  4. Biblioteca inspirada no parsec, da linguagem Haskell, onde é possível

    a criação de parsers de gramáticas preditivas ou sensíveis a contextos. • Desde 2013 • Latest version: 3.0 (requires Java 8+) • compile 'org.jparsec:jparsec:3.0' O QUE É?
  5. VANTAGENS E DESVANTAGENS • Combinação entre parsers • Alguns scanners

    prontos • Performance • Estável • Documentação pobre • Ordem de declaração
  6. MOTIVAÇÃO • Abstrair as queries para nosso backend; • Permitir

    aos clientes da SearchAPI v2: ◦ Autonomia; ◦ Facilidade; ◦ Flexibilidade. • Validar a estrutura de uma query válida; • DSL do Elasticsearch incompleta.
  7. Exemplo simples

  8. EXEMPLO SIMPLES EM 3 PASSOS Criar um parser de strings

    separadas por espaço e aninhadas por parênteses. • "tincas" • "tincas trolha dumbs" • "tincas (dunhas)" • "(tincas (dunhas))" • "(tincas (dunhas (socks (xpto cool))))"
  9. Parser<Strings> STRING = Scanners.IDENTIFIER .map(Strings::new); STRING.parse("tincas"); // ["tincas"] PASSO #1

  10. Parser<Strings> STRING = Scanners.IDENTIFIER .sepBy(Scanners.WHITESPACES) .map(Strings::new); // ["tincas","trolha","dumbs"] STRING.parse("tincas trolha

    dumbs"); PASSO #2
  11. Parser<Strings> STRING = Scanners.IDENTIFIER .sepBy(Scanners.WHITESPACES).map(Strings::new); public static Parser<Strings> getRecursive() {

    Parser.Reference<Strings> ref = Parser.newReference(); Parser<Strings> parser = ref.lazy() .between(isChar('('),isChar(')')).or(STRING) .sepBy(Scanners.WHITESPACES) .many() .map(Strings::new); ref.set(parser); return parser; } // ["tincas","dunhas","socks","xpto","cool"] getRecursive() .parse("(tincas (dunhas (socks (xpto cool))))"); PASSO #3
  12. CASO DE USO EM PRODUÇÃO

  13. None
  14. ABSTRACT SYNTAX TREE FRAGMENT FRAGMENT LIST FRAGMENT ITEM FRAGMENT OPERATOR

    FILTER RELATIONAL FIELD VALUE LOGICAL OPERATOR
  15. FIELD PARSER Parsers.IDENTIFIER .sepBy1(isChar('.')) .label("field") .map(Field::new); Parser de identificadores que

    podem ser separados por um ponto.
  16. FIELD PARSER Parsers.IDENTIFIER .sepBy1(isChar('.')) .label("field") .map(Field::new); Parser de identificadores que

    podem ser separados por um ponto. • "_tincas" • "BonaFont10" • "address.street"
  17. FIELD PARSER Parsers.IDENTIFIER .sepBy1(isChar('.')) .label("field") .map(Field::new); public class Field {

    private final List<String> names; public Field(final List<String> names) { this.names = names; } ... } Parser de identificadores que podem ser separados por um ponto. • "_tincas" • "BonaFont10" • "address.street"
  18. • DIFFERENT("NE", "<>") • EQUAL("EQ", ":", "=") • GREATER("GT", ">")

    • GREATER_EQUAL("GTE", ">=") • IN("INTO") • LESS("LT", "<") • LESS_EQUAL("LTE", "<=") • VIEWPORT("@") • LIKE("LK") • RANGE("RG") • POLYGON("PG") RELATIONAL OPERATORS
  19. VALUE PARSER • BOOLEAN → a = true • NULL

    → b:null • STRING → c EQ "tincas" • NUMBER → d > 42 • IN → v IN [1,"abc",true,NULL] • LIKE → x LIKE "tinc%" • RANGE → y RANGE [1, 3] • GEOPOINT → z VIEWPORT [[-23.1234,-46.4534],[-23.0987,-46.9087]]
  20. FILTER PARSER FIELD + RELATIONAL OPERATOR + VALUE

  21. FILTER PARSER Parsers.sequence( FIELD_PARSER, RELATIONAL_OPERATOR_PARSER, VALUE_PARSER, Filter::new); FIELD + RELATIONAL

    OPERATOR + VALUE
  22. QUERY PARSER "(rooms EQUAL 3 AND NOT pimba EQUAL 2

    AND (suites EQUAL 1 OR (parkingLots GT 1 AND xpto DIFFERENT 3)))"
  23. QUERY PARSER "(rooms EQUAL 3 AND NOT pimba EQUAL 2

    AND (suites EQUAL 1 OR (parkingLots GT 1 AND xpto DIFFERENT 3)))" QUERY_PARSER = Parsers.sequence( LOGICAL_OPERATOR_PARSER.asOptional(), FILTER_PARSER, QueryFragmentItem::new); Parsers.between(isChar('('), isChar(')')) .or(QUERY_PARSER, LOGICAL_OPERATOR_PARSER, NOT_PARSER)
  24. LINKS • SearchAPI v2 Wiki https://github.com/VivaReal/search-api/wiki • Parsec https://wiki.haskell.org/Parsec •

    jparsec https://github.com/jparsec/jparsec • parsertest https://github.com/fmamud/parsertest
  25. None