неоднозначность лексера / парсера - PullRequest
4 голосов
/ 14 апреля 2010

Как лексер решает эту двусмысленность?

/*/*/

Почему это не просто говорит, о да, это начало многострочного комментария, за которым следует еще один многострочный комментарий.

Разве жадный лексер не вернет следующие жетоны?

  • / *
  • / *
  • /

Я сейчас нахожусь в процессе написания парсера с уменьшением смещения для CSS, и все же этот простой комментарий у меня на пути. Вы можете прочитать этот вопрос , если вы не хотите больше справочной информации.

UPDATE

Извините, что пропустил это в первую очередь. Я планирую добавить расширения к языку CSS в этой форме /* @ func ( args, ... ) */, но я не хочу путать редактор, который понимает CSS, но не мой комментарий к расширению. Вот почему лексер просто не может игнорировать комментарии.

Ответы [ 6 ]

8 голосов
/ 14 апреля 2010

Один из способов сделать это - войти в другое внутреннее состояние при обнаружении первого /*.Например, flex называет эти «условия начала» (соответствие комментариев в стиле C является одним из примеров на этой странице).

6 голосов
/ 14 апреля 2010

Самый простой способ, вероятно, состоит в том, чтобы лексировать комментарий как один единственный токен, то есть не испускать токен «START COMMENT», а вместо этого продолжать читать при вводе, пока вы не сможете испустить токен «COMMENT BLOCK», который включает в себя весь /*(anything)*/ бит.

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

3 голосов
/ 14 апреля 2010

В большинстве языков это не является двусмысленным: первая косая черта и звездочка потребляются для создания токена «начало многострочного комментария». За ним следует косая черта, которая является простым «контентом» в комментарии, и, наконец, последние два символа являются маркером «конец многострочного комментария».

Поскольку используются первые 2 символа, первая звездочка также не может использоваться для создания маркера конца комментария. Я только что заметил, что он может выдать второй токен «начала комментария» ... упс, это может быть проблемой, в зависимости от количества контекста, доступного для синтаксического анализатора.

Я говорю здесь о токенах, предполагая обработку комментариев на уровне парсера. Но то же самое относится и к лексеру, согласно которому базовое правило должно начинаться с '/*', а не останавливаться до тех пор, пока не будет найдено '*/'. По сути, обработка всего комментария на уровне лексера не будет смущена вторым «началом комментария».

0 голосов
/ 14 апреля 2010

Один из способов решить эту проблему - вернуть ваш лексер:

/
*
/
*
/

И пусть ваш парсер разберется с этим оттуда. Это то, что я, вероятно, сделал бы для большинства языков программирования , поскольку / и * также могут быть использованы для умножения и других подобных вещей, которые слишком сложны для беспокойства лексера. Лексер должен просто возвращать элементарных символов .

Если токен начинает слишком сильно зависеть от контекста, то, что вы ищете, вполне может быть более простым токеном.

При этом CSS не является языком программирования, поэтому / и * не могут быть перегружены. На самом деле они не могут быть использованы ни для чего другого, кроме комментариев. Так что я бы очень хотел просто передать все это как маркер комментария, если у вас нет веских причин не делать этого: /\*.*\*/

0 голосов
/ 14 апреля 2010

Поскольку CSS не поддерживает вложенные комментарии, ваш пример обычно разбирается в один токен, COMMENT. То есть лексер будет видеть /* в качестве маркера начального комментария, а затем потреблять все, вплоть до */ последовательности.

0 голосов
/ 14 апреля 2010

Используйте алгоритм регулярного выражения, ищите от начала строки, возвращаясь к текущему местоположению.

if (chars[currentLocation] == '/' and chars[currentLocation - 1] == '*') {
  for (int i = currentLocation - 2; i >= 0; i --) {
    if (chars[i] == '/' && chars[i + 1] == '*') {
      // .......
    }
  }
}

Это похоже на применение регулярных выражений /\*([^\*]|\*[^\/])\*/, жадных и восходящих.

...