Haskell ReadP至少解析其中一个列表
Haskell ReadP parse at least one of the list
我尝试使用 ReadP 标准库解析命令(创建)。我的命令应以字符串 create
开头,然后至少包含一个 word/tag/due,并且可能包含一个选项。这是我的实际表达:
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
skipSpaces
return $ cmd : rest
问题是,如果我只用一个选项调用 create
,它会很好地解析。但它不应该,因为我预计至少有一个 word/tag/due。我该如何表达?
[编辑] 我找到了解决方案,感谢 @M. Aroosi。
其实我用错了运算符。 <++
、本地的、独占的、偏左的选择,更符合我的需要。一旦一个表达式被匹配,它不应该检查其他的:
notAnOpt arg = case arg of
AddOpt _ -> False
_ -> True
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddTag <$> addTagExpr)
<++ (SetDue <$> dueExpr)
<++ (AddOpt <$> optExpr)
<++ (AddWord <$> wordExpr)
skipSpaces
guard $ isJust $ find notAnOpt rest
return $ cmd : rest
一个简单的解决方案是使用 Control.Monad
中的 guard
。
假设一个类似 isOpt :: Arg -> Bool
的函数是
isOpt :: Arg -> Bool
isOpt (AddOpt _) = True
isOpt _ = False
然后您对 createExpr
的定义更改为
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
guard $ at_least_one_non_optional rest
skipSpaces
return $ cmd : rest
where at_least_one_non_optional = not . null . filter (not . isOpt)
guard
当其参数为 False
时,解析器基本上会失败,更一般地,当参数为 [=17] 时,它通过返回 empty
来处理任何 Alternative
=].
我尝试使用 ReadP 标准库解析命令(创建)。我的命令应以字符串 create
开头,然后至少包含一个 word/tag/due,并且可能包含一个选项。这是我的实际表达:
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
skipSpaces
return $ cmd : rest
问题是,如果我只用一个选项调用 create
,它会很好地解析。但它不应该,因为我预计至少有一个 word/tag/due。我该如何表达?
[编辑] 我找到了解决方案,感谢 @M. Aroosi。
其实我用错了运算符。 <++
、本地的、独占的、偏左的选择,更符合我的需要。一旦一个表达式被匹配,它不应该检查其他的:
notAnOpt arg = case arg of
AddOpt _ -> False
_ -> True
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddTag <$> addTagExpr)
<++ (SetDue <$> dueExpr)
<++ (AddOpt <$> optExpr)
<++ (AddWord <$> wordExpr)
skipSpaces
guard $ isJust $ find notAnOpt rest
return $ cmd : rest
一个简单的解决方案是使用 Control.Monad
中的 guard
。
假设一个类似 isOpt :: Arg -> Bool
的函数是
isOpt :: Arg -> Bool
isOpt (AddOpt _) = True
isOpt _ = False
然后您对 createExpr
的定义更改为
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
guard $ at_least_one_non_optional rest
skipSpaces
return $ cmd : rest
where at_least_one_non_optional = not . null . filter (not . isOpt)
guard
当其参数为 False
时,解析器基本上会失败,更一般地,当参数为 [=17] 时,它通过返回 empty
来处理任何 Alternative
=].