Исправление неверной грамматики JSON - PullRequest
3 голосов
/ 02 января 2012

Я только начал изучать синтаксический анализ, и я написал этот простой синтаксический анализатор на Haskell (с использованием parsec), чтобы прочитать JSON и построить для него простое дерево. Я использую грамматику в RFC 4627 .

Однако, когда я пытаюсь разобрать строку {"x":1 }, я получаю вывод:

parse error at (line 1, column 8):
unexpected "}"
expecting whitespace character or ","

Это, кажется, происходит только тогда, когда у меня есть пробелы перед закрывающей скобкой (]) или усами (}).

Что я сделал не так? Если я избегаю пробелов перед закрывающим символом, он отлично работает.

Ответы [ 2 ]

6 голосов
/ 02 января 2012

Parsec не выполняет автоматическую перемотку и возврат. Когда вы пишете sepBy member valueSeparator, valueSeparator занимает пустое пространство, поэтому анализатор будет анализировать ваше значение следующим образом:

{"x":1 }
[------- object
%        beginObject
 [-]     name
    %    nameSeparator
     %   jvalue
      [- valueSeparator
       X In valueSeparator: unexpected "}"

Legend:
[--]     full match
%        full char match
[--      incomplete match
X        incomplete char match

При сбое valueSeparator Parsec не вернется и не попробует другую комбинацию разборов, потому что один символ уже соответствует в valueSeparator.

У вас есть два варианта решения вашей проблемы:

  1. Поскольку в JSON пробелы незначительны, всегда используйте пробел после значимого токена, никогда прежде. Таким образом, tok должен занимать только пробел после символа, поэтому его определение равно tok c = char c *> ws ((*>) из Control.Applicative); примените то же правило ко всем другим парсерам. Поскольку вы никогда не будете использовать пробелы после ввода «неправильного парсера» таким образом, вам не придется возвращаться назад.
  2. Используйте обратное отслеживание в Parsec, добавив try перед синтаксическими анализаторами, которые могут потреблять более одного символа, и это должно перемотать их ввод в случае неудачи.

РЕДАКТИРОВАТЬ: обновлена ​​графика ASCII, чтобы иметь больше смысла.

1 голос
/ 02 января 2012

Общее решение состоит в том, чтобы все ваши парсеры пропускали конечный пробел. Проверьте lexemeParsecToken) в документах Parsec для аккуратного способа сделать это или просто подхватить простую версию самостоятельно:

 lexeme parser = do result <- parser
                    spaces
                    return result

Затем используйте эту функцию на всех ваших токенах (например, числовых литералах). Таким образом, вам только нужно беспокоиться о пробелах в самом начале выражения.

Подробнее о ParsecToken и друзьях см. В разделе «Лексический анализ» документа Parsec .

.

Имеет смысл пропускать только пробел после токена, кроме как в самом начале, где вы можете пропустить его вручную. Вам следует использовать этот подход, даже если вы в конечном итоге не используете модуль ParsecToken.

Кажется, у вас уже есть tok, который действует как мой lexeme, за исключением того, что он использует пробелы на обеих сторонах. Измените его на использование только пробела после токена и просто игнорируйте пробелы в самом начале ввода вручную. Это должно (в идеале :)) решить проблему.

...