2012-01-02 2 views
3

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

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

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

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

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

+1

Немного несвязанный: подсветка синтаксиса в пастебине не блестящая. На самом деле есть версия haskell pastebin: hpaste.org –

+0

Исправлено. Спасибо. –

ответ

6

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 терпит неудачу, Парсеки не вернуться назад и попробовать различные комбинации разборов, потому что один символ уже соответствует valueSeparator.

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

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

EDIT: обновленный ASCII-графический, чтобы иметь больше смысла.

+0

Что было бы самым лучшим способом (в коде), чтобы исправить это? Должен ли я отказаться от sepBy в этом случае? –

+0

Кроме того, причина, по которой я читаю пробелы перед и после токена, заключается в том, что это то, что он говорит в RFC =/ –

+1

'sepBy' отлично, если вы выберете один из двух вариантов, которые я предложил. В частности, 'member 'sepBy' try valueSeparator' должен работать как ожидалось (заменить 'на'). – dflemstr

1

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

lexeme parser = do result <- parser 
        spaces 
        return result 

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

Для получения дополнительной информации о ParsecToken и друзьях, посмотрите раздел «Лексический анализ» раздела Parsec docs.

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

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

+0

Мой код находится по адресу http://pastebin.com/ewGH7QMh. Нажмите «этот простой парсер» в моем первоначальном вопросе. –

+0

Да, я пропустил ссылку в первый раз, когда я прочитал вопрос. Я, вероятно, собираюсь пересмотреть свой ответ, когда закончу его чтение. –

+0

Я пробовал только в конце, все еще не удалось. –

Смежные вопросы