Разобрать глубину дерева с помощью antlr

1

У меня есть правило antlr, которое обрабатывает выражения AND и OR. Это выглядит так:

expr : expr 'AND' expr 
     | expr 'OR' expr 
     | 'a' | 'b' | 'c' | 'd'; 

Это приводит к тому, что дерево разбора очень глубокое. Например, если у вас есть

a AND b AND c AND d

приводит к дереву:

              expr
           /    |  \
        expr   AND  d
     /    |  \
   expr AND  c
 / | \
a AND b

Это может стать довольно глубоким и дорогостоящим для оценки, поэтому я хотел добавить оптимизацию. Я хочу обрабатывать несколько последовательных И выражений вместе (аналогично для OR-s).

Поэтому я хочу сделать что-то вроде этого:

expr : expr ('AND' expr)+
     | expr ('OR' expr)+
     | 'a' | 'b' | 'c' | 'd'; 

И я думал, что будет генерировать один узел для всех AND-s в последовательности.

Однако, когда я делаю это, antlr все еще решает генерировать рекурсивное дерево. Полагаю, это потому, что правило неоднозначно. Любые идеи о том, как заставить его быть плоскими? Это вопрос упорядочения правил или что-то в этом роде? Причина, по которой я забочусь о глубине, связана с эффектами производительности из-за глубокой рекурсии.

Теги:
antlr4
antlr

2 ответа

2

Рекурсия создает реальную, измеренную проблему с производительностью. Если да, можете ли вы количественно оценить степень рекурсии, с которой имеете дело. Antlr обычно неплохо справляется с рекурсией, поэтому, если вы столкнулись с реальной проблемой производительности, это может быть связано с более глубокой проблемой в Antlr. Тер и Сэму нужно будет воспроизвести его, чтобы справиться с этим.

Тем не менее, каждый экземпляр expr по правилу rhs создаст рекурсию. Группировка и ограничение экземпляров

expr : expr ('AND' | 'OR') expr 
     | 'a' | 'b' | 'c' | 'd'
     ; 

не изменит количество рекурсий, необходимых для выполнения правила - это зависит от обрабатываемых данных.

Что может быть большим выигрышем, если ваша проблема с производительностью на самом деле проистекает из многих ошибок в правильной рекурсии, заключается в том, чтобы реорганизовать вашу грамматику раньше, чем правило (-ы) или ограничить количество правил (или подпунктов), которые могут быть применимы для любых заданной последовательности данных.

Точно, как сложно сказать на основе предоставленной информации.

обновленный

Чтобы уточнить, вызов recursive правила в Antlr не выполняется иначе, чем любой другой вызов правила; он не реализуется рекурсивным вызовом Java-метода.

Алгоритм LL (*) Antlr неявно является последовательным решателем пути, работающим против предварительно вычисленной сети действительного состояния. Информация контрольной точки сохраняется в каждой точке принятия решения, которая включает вызовы правил для обратного отслеживания. Данные контрольной точки, необходимые для захвата перехода состояния правила, относительно просты и нечувствительны к тому, целевой узел представляет собой recursive вызов на основе грамматики или нет. Производительность в значительной степени коррелирует с количеством оценок узлов правил, включая, в частности, все те из всех подтипов правил, которые пытались и в конечном итоге потерпели неудачу.

Вот почему разворачивание recursive правила в несколько правил вряд ли улучшит производительность. Если они коллективно реализуют одну и ту же рекурсивную функцию, то в лучшем случае выполнение с одним и тем же входом потребует одинакового количества вызовов правил. В худшем случае неминимальное выражение правил потребует больше вызовов правил и, скорее всего, будет иметь больше внутренних контрольных точек (каждый * и + в скобках неявно охраняется).

Предполагая, что ваша грамматика оптимально сведена к минимуму и что порядок правил выражения 20+ статистически оптимизирован для ваших исходных данных, вы могли бы оптимизировать дальше, только пройдя точное поддерево, которое будет фактически соответствовать следующей последовательности исходных данных.

Просто статистически заранее предскажите правильное правило поддерева. Учитывая количество раз, когда вы оцениваете дерево синтаксического анализа, вы должны иметь возможность довольно быстро сопоставить некоторую просто вычисленную сигнатуру последовательностей исходных данных с поддеревьями, которые в конечном итоге совпадают. Или, по крайней мере, решить более подходящий порядок поддеревья, чтобы попробовать.

Именно то, что должно быть функцией подписи, или если выгодная цена будет положительной, сложно сказать на основе предоставленной до сих пор информации. Действительно зависит от характера ваших исходных данных.

  • 0
    Обратите внимание, что вы не можете использовать [...] внутри правил парсера: только внутри правил лексера.
  • 0
    Это не проблема antlr, я посмотрел на стек, и это выглядит разумно. Я использую посетителя, чтобы пройтись по дереву и оценивать его сотни (скоро тысячи) раз в секунду. Проблема в том, что с моим определением грамматики глубина дерева в основном линейна по количеству выражений, и я могу легко получить 20+ выражений. Я согласен с вами в отношении короткого замыкания (например, ложь И все, что ложно), и я уже делаю это, и на данный момент способ чтения выходных данных моего профилировщика - это рекурсия, которая немного замедляет меня.
1

Вы можете легко это сделать, если у вас есть несколько правил, как в старых грамматиках (грамматика C).

expr:   orExpr
    ;

orExpr: andExpr ('OR' andExpr)*
        ;

andExpr : primExpr ('AND' primExpr)*
        ;

primExpr:'a' | 'b' | 'c' | 'd'; 

WS : ' ' -> skip;

Образец текста:

a AND b AND c AND d

результат:

Изображение 174551

  • 0
    Это звучит как то, что я спрашивал - почему это было отвергнуто?
  • 0
    Я не знаю. Даже не комментарий почему ...
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню