Parsing 在给定令牌列表的情况下生成解析器 背景
我正在尝试使用Parsec实现一个日期打印和解析系统 我已经成功地实现了类型的打印功能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
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