Как я могу упростить прогнозирование токенов DFA? - PullRequest
5 голосов
/ 22 сентября 2011

Lexer DFA приводит к ошибке «слишком большой код»

Я пытаюсь проанализировать страницы сервера Java с помощью ANTLR 3.

Java имеет ограничение 64 КБ для байтового кода одного метода, и я продолжаю сталкиваться с ошибкой «слишком большой код» при компиляции исходного кода Java, сгенерированного ANTLR.

В некоторых случаях я смог исправить это, скомпрометировав свой лексер. Например, JSP использует токен XML «Имя», который может содержать самые разные символы. Я решил принять только символы ASCII в моем токене «Имя», что значительно упростило некоторые тесты в, и лексер позволил его скомпилировать.

Тем не менее, я дошел до того, что не могу больше срезать углы, но DFA все еще слишком сложен.

Что мне с этим делать?

Существуют ли распространенные ошибки, приводящие к сложным DFA?

Есть ли способ запретить генерацию DFA, возможно, полагаться на семантические предикаты или фиксированный прогноз, чтобы помочь с предсказанием?

Писать этот лексер вручную будет легко, но прежде чем я откажусь от ANTLR, я хочу убедиться, что я не пропускаю что-то очевидное.

Фон

Лексеры ANTLR 3 используют DFA, чтобы решить, как токенизировать ввод. В сгенерированном DFA есть метод с именем specialStateTransition(). Этот метод содержит оператор switch с регистром для каждого состояния в DFA. В каждом случае есть ряд if операторов, по одному для каждого перехода из состояния. Условие каждого оператора if проверяет вводимый символ, чтобы определить, соответствует ли он переходу.

Эти условия тестирования персонажа могут быть очень сложными. Они обычно имеют следующую форму:

int ch = … ; /* "ch" is the next character in the input stream. */
switch(s) { /* "s" is the current state. */
  …
  case 13 :
    if ((('a' <= ch) && (ch <= 'z')) || (('A' <= ch) && (ch <= 'Z')) || … )
      s = 24; /* If the character matches, move to the next state. */
    else if …

Кажущееся незначительным изменение моего лексера может привести к десяткам сравнений для одного перехода, нескольким переходам для каждого состояния и множеству состояний. Я думаю, что некоторые из рассматриваемых состояний невозможно достичь из-за моих семантических предикатов, но кажется, что семантические предикаты игнорируются DFA. (Возможно, я что-то неправильно понимаю - этот код определенно не тот, который я мог бы написать вручную!)

Я нашел грамматику ANTLR 2 в инструменте Jsp2x, но меня не устраивает его дерево разбора, и я хочу обновить свои навыки ANTLR, поэтому я решил попробовать написать свои собственные. Я использую ANTLRWorks, и я попытался сгенерировать графики для DFA, но в ANTLRWorks, похоже, есть ошибки, которые этому препятствуют.

Ответы [ 2 ]

4 голосов
/ 22 сентября 2011

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

Иногда это можно исправить, установив определенные правила лексера fragments в отличие от «полных» правил лексера, которые производят токены и / или изменяют порядок сопоставления символов в правилах, но, глядя на то, какВы уже пробовали себя, я сомневаюсь, что в вашем случае можно многое получить.Однако, если вы хотите опубликовать здесь свою грамматику лексера на SO, я или кто-то еще, возможно, увидим что-то, что можно изменить.

В общем, эта проблема решается путем разбиения грамматики лексера на 2или более отдельных грамматик лексера, а затем импортировать их в одну «основную» грамматику.В терминах ANTLR они называются составные грамматики .Посмотрите эту вики-страницу ANTLR о них: http://www.antlr.org/wiki/display/ANTLR3/Composite+Grammars

РЕДАКТИРОВАТЬ

Как @Gunther справедливо упоминается в комментарии под OP, см. Q & A: Почему мой класс antlr lexer java«слишком большой код»? , где небольшое изменение (удаление определенного предиката) привело к исчезновению ошибки «слишком большой код».

3 голосов
/ 18 сентября 2012

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

К сожалению, даже этот магический скрипт не помогает в некоторых сложных случаях. Компилятор может начать ругаться на слишком большой блок переходов DFA (статические поля String []) .

Я нашел простой способ решить эту проблему, переместив (используя возможности рефакторинга IDE) таких полей в другой класс с произвольно сгенерированным именем. Это всегда помогает при перемещении только одного или нескольких полей таким образом.

...