2016-11-01 3 views
2

Я пытаюсь изучить Parsec, разобрав строку даты формата «YYYYMMDD», например «20161030». И мое решение:Как разбирать строку целых чисел только в некотором диапазоне с Parsec?

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?

+2

'guard $ month> = 0 && month <= 12'? –

+1

'> = 1' возможно? : D – ThreeFx

+0

Сторона примечания 20160229 действительна, в то время как 20000229 не является, не нужно угадывать, что слишком – epsilonhalbe

ответ

2

Вы можете добавить guard как предложил Томас М. Dubuisson:

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) 

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

+2

И, конечно, в реальном коде (в отличие от педагогического кода, как показано здесь) вы должны использовать ['fromGregorianValid'] (http://hackage.haskell.org/package/time-1.6.0.1/docs/Data-Time -Calendar.html # v: fromGregorianValid) из почтенного пакета 'time', а не для кодирования простого, но неправильного валидатора. –

+0

@ DanielWagner: Да, хотя вы также можете сделать несколько своих проверок здравомыслия, чтобы иметь более подробные сообщения об ошибках. –

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