Как разобрать строку целых чисел только в некотором диапазоне с помощью Parsec?


Я пытаюсь изучить Parsec, разбирая строку даты формата "YYYYMMDD", например"20161030". И мое решение таково:

date :: Parser (String, String, String)
date = do
  year <- replicateM 4 digit
  month <- replicateM 2 digit
  day <- replicateM 2 digit
  return (year, month, day)

Но проблема в том, что "20161356" также является допустимой датой для моего кода.

Как я могу проверить ,что " мм " находится между 1 и 12; и "DD" находится между 1 и 31?

1 2

1 ответ:

Вы можете добавить guard, Как предложил Томас М. Дюбюиссон:

date :: Parser (String, String, String)
date = do
  year <- replicateM 4 digit
  month <- replicateM 2 digit
  day <- replicateM 2 digit
  guard $ read month > 0 && read month <= 12 && read day > 0 && read day <= 31
  return (year, month, day)

Однако это приводит к плохому сообщению об ошибке:

λ> parse date "" "20161356"
Left (line 1, column 9):unknown parse error

Мы можем исправить это, объединив guard с <?>, чтобы обеспечить лучшее сообщение об ошибке:

date :: Parser (String, String, String)
date = do
  year <- replicateM 4 digit
  month <- replicateM 2 digit
  guard (read month > 0 && read month <= 12) <?> "valid month (1–12)"
  day <- replicateM 2 digit
  guard (read day > 0 && read day <= 31) <?> "valid day (1–31)"
  return (year, month, day)

При таком подходе вы получите более полезное сообщение об ошибке:

λ> parse date "" "20161356"
Left (line 1, column 7):
expecting valid month (1–12)

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