2013-11-15 2 views
0

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

key[value] 

или

key[value1][value2]...[valuen] 

То есть, может быть 1 или много значений. Улов состоит в том, что тип значения зависит от ключа. Так, например, если ключ B (для игры черный камень в движении). Значение должно быть координатой, описываемой двумя буквами, например: B[ab]. Также может быть, что ключ AB (для добавления нескольких черных камней, для настройки платы), тогда это список таких координат: AB[ab][cd][fg]. Также может быть, что ключ C (для комментария). Тогда значение представляет собой только строку C[this is a comment].

Конечно, это может быть описано по типу

type Property = (String, [String]) 

Но я думаю, что было бы лучше, чтобы иметь что-то вроде

data Property = B Coordinate | AB [Coordinate] | C String ... 

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

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

Как бы вы разобрали что-то вроде этого?

+5

Монадически. Разберите ключ (тип этого синтаксического анализа: 'm Key').Затем подайте этот ключ на функцию, которая анализирует регистр на ключе, и выбирает соответствующий синтаксический анализатор на основе ключа (тип: 'Key -> m Property'); вы можете использовать '(>> =)' для этого или просто do-notation: 'do {key <- parseKey; ключевой ключ {KeyB -> parseB; KeyAB -> parseAB; keyC -> parseC}} ' –

+2

Второе поколение @Rhymoid. Это также, по сути, является разницей между аппликативным функтором и монадой. С помощью монады вы можете использовать информацию, которую только что разобрали, чтобы решить, как вы собираетесь разбирать то, что будет дальше. К счастью, Parsec является примером как аппликации, так и монады. Очень наглядный пример разницы! – kqr

ответ

6

Это на самом деле простой выбор и не нуждается в монадическом разборе. Я буду использовать аппликативный интерфейс, чтобы продемонстрировать этот момент.

Построить синтаксический анализатор для каждого свойства идентификатора и его значения свойств несколько, как это:

black = B <$> (char 'C' *> coordinate) 
white = W <$> (char 'W' *> coordinate) 
addBlack = AB <$> (string "AB" *> many1 coordinate) 

(предполагая, что вы построили coordinate анализатор, который съедает скобки и возвращает что-то типа Coordinate).

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

myparser = black <|> white <|> addBlack 

Но я подозреваю, что есть более AW идентификатор для добавления белых камней, так что мы должны были бы предупредить, что они перекрывают друг друга, используя попытку, которая откатывается, когда анализатор не может:

mybetterparser = black <|> white <|> (try addBlack <|> try addWhite) 

Я в квадратные скобки вместе парсеры с общим началом и используется попробовать на них, чтобы вернуться начало.

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