如何使用Parsec解析仅在某个范围内的整数字符串?

时间:2016-11-01 14:31:24

标签: parsing haskell parsec

我试图通过解析格式为" YYYYMMDD"的日期字符串来学习Parsec,例如" 20161030"。我的解决方案是:

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

但问题在于&#34; 20161356&#34;也是我的代码的有效日期。

如何验证&#34; MM&#34;在1到12之间;和&#34; DD&#34;在1到31之间?

1 个答案:

答案 0 :(得分:2)

您可以按照Thomas M. DuBuisson的建议添加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)

作为旁注,我认为 对验证(或至少是完整性检查)解析器中的日期很有价值 - 它确保日期验证与解析器的其余部分组成并出错 - 处理代码。您可以忘记在代码中稍后检查日期,并且错误已正确本地化,如果您正在解析包含大量日期的文档,这非常有用。