Parsing 在给定令牌列表的情况下生成解析器 背景

Parsing 在给定令牌列表的情况下生成解析器 背景,parsing,haskell,Parsing,Haskell,我正在尝试使用Parsec实现一个日期打印和解析系统 我已经成功地实现了类型的打印功能 showDate::String->Date->Parser String 它将解析格式化字符串,并基于格式化字符串显示的标记创建新字符串 比如说 showDate“%d-%m-%Y”$Date 2015 3 17 “17-3-2015”的输出是否正确 我已经编写了一个标记器用于showDate函数,因此我认为我可以使用该函数的输出以某种方式使用函数readDate::[Token]->parser Date

我正在尝试使用Parsec实现一个日期打印和解析系统

我已经成功地实现了类型的打印功能

showDate::String->Date->Parser String

它将解析格式化字符串,并基于格式化字符串显示的标记创建新字符串

比如说

showDate“%d-%m-%Y”$Date 2015 3 17

“17-3-2015”的输出是否正确

我已经编写了一个标记器用于
showDate
函数,因此我认为我可以使用该函数的输出以某种方式使用函数
readDate::[Token]->parser Date
生成一个解析器。我的想法很快就停止了,因为我意识到我不知道如何实现这一点

我想要完成的 假设我们有以下函数和类型(实现并不重要):

解析器
readDate[DayNumber,Literal“-”,MonthNumber,Literal“-”,Year]
应该与

do
     d <- pDayNum
     pLiteral "-"
     m <- pMonthNum
     pLiteral "-"
     y <- pYear
     return $ Date y m d
do
     pLiteral "~~"
     m <- pMonthNum
     pLiteral "hello"
     d <- pDayNum
     pLiteral " "
     y <- pYear
     return $ Date y m d
我的直觉告诉我有某种concat/map/fold使用monad绑定,我可以使用它,但我不知道

问题 parsec是用于此的正确工具吗

我的方法是复杂的还是无效的

  • 如果没有,如何实现此功能

  • 如果是的话,我应该怎么做


您的
令牌
是日期格式的小型语言说明
[Token]

import Data.Functor
import Text.Parsec
import Text.Parsec.String

data Date = Date Int Int Int deriving (Show)

data Token = DayNumber | Year | MonthNumber | Literal String
为了解释这种语言,我们需要一个表示解释器状态的类型。我们一开始不知道日期的任何组成部分,然后在遇到日期、年份或月数时发现它们。以下
DateState
表示知道或不知道
日期的每个组件的状态

data DateState = DateState {dayState :: (Maybe Int), monthState :: (Maybe Int), yearState :: (Maybe Int)}
readDateToken :: Token -> DateState -> Parser DateState
readDateToken (DayNumber) ds =
    do
        day <- pNatural
        return ds {dayState = Just day}
readDateToken (MonthNumber) ds =
    do
        month <- pNatural
        return ds {monthState = Just month}
readDateToken (Year) ds =
    do
        year <- pNatural
        return ds {yearState = Just year}
readDateToken (Literal l) ds = string l >> return ds

pNatural :: Num a => Parser a
pNatural = fromInteger . read <$> many1 digit
我们将开始用
DateState Nothing
解释
[Token]

每个
标记
都将转换为一个函数,该函数读取
日期状态
,并生成一个解析器来计算新的
日期状态

data DateState = DateState {dayState :: (Maybe Int), monthState :: (Maybe Int), yearState :: (Maybe Int)}
readDateToken :: Token -> DateState -> Parser DateState
readDateToken (DayNumber) ds =
    do
        day <- pNatural
        return ds {dayState = Just day}
readDateToken (MonthNumber) ds =
    do
        month <- pNatural
        return ds {monthState = Just month}
readDateToken (Year) ds =
    do
        year <- pNatural
        return ds {yearState = Just year}
readDateToken (Literal l) ds = string l >> return ds

pNatural :: Num a => Parser a
pNatural = fromInteger . read <$> many1 digit
我们将运行几个示例

runp p s = runParser p () "runp" s

main = do
    print . runp (readDate [DayNumber,Literal "-",MonthNumber,Literal "-",Year])                   $ "12-3-456"
    print . runp (readDate [Literal "~~", MonthNumber,Literal "hello",DayNumber,Literal " ",Year]) $ "~~3hello12 456"
    print . runp (readDate [DayNumber,Literal "-",MonthNumber,Literal "-",Year,Literal "-",Year])  $ "12-3-456-789"
    print . runp (readDate [DayNumber,Literal "-",MonthNumber])                                    $ "12-3"
这将产生以下输出。请注意,当我们要求两次阅读
年份
时,这两年中的第二年被用于
日期
。您可以通过修改
readDateToken
的定义并可能修改
DateState
类型来选择不同的行为。当
[Token]
没有指定如何读取其中一个日期字段时,我们得到的错误是
日期格式不完整
,描述稍有错误;这是可以改进的

Right (Date 12 3 456)

Right (Date 12 3 456)

Right (Date 12 3 789)

Left "runp" (line 1, column 5):
unexpected end of input
expecting digit
Date format is incomplete