Как получить Ragel для анализа двух имен, разделенных (пробел * ":" пробел *)? - PullRequest
2 голосов
/ 23 января 2012

Я хотел бы проанализировать следующее:

name:name

, где имена начинаются и заканчиваются на alnum и могут содержать любую комбинацию alnum и пробелов внутри.Они также могут быть пустыми.Мои правила для этого:

identifier = alnum (space* alnum)*;
name       = (identifier | zlen) >sName $pName %fName;

Имена могут быть разделены двоеточием и, возможно, пробелами между именами и двоеточием.Мои правила для этого:

sep = space* ":" space*;
main := name sep name;

Это не работает, потому что, очевидно, space* в identifier и space* в sep сбивают с толку синтаксический анализатор.Я заканчиваю тем, что получаю действие fName, выполняемое в каждом месте имени.

Если я изменю sep на:

sep = ":";

, тогда все в порядке.Как мне изменить эти правила, чтобы синтаксический анализатор делал то, что я намереваюсь?

Исходный код этого вопроса здесь: https://gist.github.com/1661150

1 Ответ

10 голосов
/ 23 января 2012

Существует два основных решения этой проблемы:

  1. Определите действие, чтобы его можно было безопасно выполнить несколько раз,
  2. Измените синтаксис, чтобы действие выполнялось толькоодин раз.

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

/* C code */
char *name_start, *name_end;

/* Ragel code */
action markNameStart { name_start = p; }
action markNameEnd { name_end = p; }
action nameAction {
    /* Clumsy since name is not nul-terminated */
    fputs("Name = ", stdout);
    fwrite(name_start, 1, name_end - name_start, stdout);
    fputc('\n', stdout);
}

name = space* %markNameStart
       (alnum+ %markNameEnd <: space*)+
       %nameAction ;
main := name ":" name ;

Здесь синтаксис для name включает произвольные пробелы и хотя бы один буквенно-цифровой символ.Когда встречается первый буквенно-цифровой символ, его местоположение сохраняется в name_start.Всякий раз, когда выполнение буквенно-цифровых символов заканчивается, местоположение следующего символа сохраняется в name_end.<: технически не нужен, но он уменьшает частоту выполнения действия * 1017. *

Только убедитесь, что такое выражение не ставится рядом с пробелами.

IВы не проверяли приведенный выше код. Вы должны посмотреть на визуализацию конечного автомата Graphviz перед использованием.

Что делает Ragel

С вашим исходным кодом, давайте предположим, что вводвыглядит следующим образом:

Hello world : Goodbye world

Машина Ragel сканирует слева направо, находит начало name и просматривает буквенно-цифровые символы.

Hello world : Goodbye world
    ↑

Следующий символэто пространство.Так что либо мы встретили пробел внутри слова, либо первый пробел после конца слова.Как выбирает Ragel?

Ragel выбирает оба варианта одновременно. Это очень важно.Ragel пытается симулировать недетерминированный конечный автомат, но поскольку ваш компьютер является детерминированным, самый простой способ сделать это - преобразовать NFA в DFA, который параллельно имитирует неограниченное количество NFA.Поскольку NFA имеют конечное число состояний (отсюда и название), DFA также имеют конечное число состояний, поэтому этот метод работает.

После обнаружения пространства у вас есть один NFA в следующем состоянии,ищет остальную часть name:

identifier = alnum (space* alnum)*;
                    ↑

main := name sep name;
        ↑

Второй NFA находится в следующем состоянии и предполагает, что name уже завершен (и этот NFA выполняет действие fName "преждевременно "):

sep = space* ":" space*;
      ↑

main := name sep name;
             ↑

Это очевидно для вас и для меня очевидно, что только первый NFA является правильным.Но машины, созданные с помощью Ragel, смотрят только на одного персонажа за раз, они не смотрят в будущее, чтобы увидеть, какой вариант правильный.Второй NFA в конечном итоге встретится с буквенно-цифровым символом, где он ожидал увидеть ":", и поскольку это не разрешено, второй NFA исчезнет.

Посмотрите документацию по Ragel

Вотописание %:

expr % action

Оператор оставляющего действия ставит в очередь действие для встраивания в переходы, которые выходят из машины через конечное состояние.

Действие выполняется для переходов, которые не обязательно способствуют успешному анализу.Обратитесь к руководству по Ragel, глава 4, «Управление недетерминизмом» для получения дополнительной информации о недетерминизме в Ragel, хотя методы в главе 4 не помогут вам в этом конкретном случае, поскольку действия на вашем компьютере могут быть устранены только с помощью несвязанного просмотра,не допускается в конечных автоматах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...