Сопоставление выражения ИЛИ с использованием Grappa (Java PEG Parser)

Я новичок в синтаксическом анализе PEG и пытаюсь написать простой синтаксический анализатор для анализа выражения типа: «term1 OR term2 Anotherterm», в идеале в AST, который будет выглядеть примерно так:

          OR
-----------|---------
|                    |
"term1"            "term2 anotherterm"

В настоящее время я использую Grappa (https://github.com/fge/grappa), но она не соответствует даже более простое выражение «термин1 ИЛИ термин2». Вот что у меня есть:

package grappa;

import com.github.fge.grappa.annotations.Label;
import com.github.fge.grappa.parsers.BaseParser;
import com.github.fge.grappa.rules.Rule;

public class ExprParser extends BaseParser<Object> {

  @Label("expr")
  Rule expr() {
    return sequence(terms(), wsp(), string("OR"), wsp(), terms(), push(match()));
  }

  @Label("terms")
  Rule terms() {
    return sequence(whiteSpaces(),
        join(term()).using(wsp()).min(0),
        whiteSpaces());
  }

  @Label("term")
  Rule term() {
    return sequence(oneOrMore(character()), push(match()));
  }

  Rule character() {
    return anyOf(
        "0123456789" +
        "abcdefghijklmnopqrstuvwxyz" +
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
        "-_");
  }

  @Label("whiteSpaces")
  Rule whiteSpaces() {
    return join(zeroOrMore(wsp())).using(sequence(optional(cr()), lf())).min(0);
  }

}

Может кто-то указать мне верное направление?


person rainkinz    schedule 09.10.2015    source источник


Ответы (1)


(автор граппы здесь...)

Итак, то, что вы, кажется, хотите, на самом деле является деревом синтаксического анализа.

Совсем недавно было разработано расширение для граппы (2.0.x+), которое может удовлетворить ваши потребности: https://github.com/ChrisBrenton/grappa-parsetree.

Граппа по умолчанию только «вслепую» сопоставляет текст и имеет в своем распоряжении стек, поэтому вы могли бы, например:

public Rule oneOrOneOrEtc()
{
    return join(one(), push(match())).using(or()).min(1));
}

Но тогда все ваши совпадения были бы в стеке... Не очень практично, но все же можно использовать в некоторых ситуациях (см., например, sonar-sslr-grappa).

В вашем случае вам нужен этот пакет. Вы можете сделать это с ним:

// define your root node
public final class Root
    extends ParseNode
{
    public Root(final String match, final List<ParseNode> children)
    {
        super(match, children);
    }
}

// define your parse node
public final class Alternative
    extends ParseNode
{
    public Alternative(final String match, final List<ParseNode> children)
    {
        super(match, children);
    }
}

Это минимальная реализация. И тогда ваш парсер может выглядеть так:

@GenerateNode(Alternative.class)
public Rule alternative() // or whatever
{
    return // whatever an alternative is
}

@GenerateNode(Root.class)
public Rule root
{
    return join(alternative())
        .using(or())
        .min(1);
}

Здесь происходит то, что корневой узел сопоставляется перед альтернативой, если, скажем, у вас есть строка:

a or b or c or d

тогда корневой узел будет соответствовать «всей последовательности», и у него будет четыре альтернативы, соответствующие каждому a, b, c и d.

Полная благодарность здесь принадлежит Кристоферу Брентону, который впервые предложил эту идею!

person fge    schedule 21.03.2016