Haskell的Parsec问题< |>操作者

时间:2010-11-20 08:04:23

标签: parsing haskell parsec

我是Haskell和Parsec的新手。为了更多地了解语言和特定库,我正在尝试创建一个可以解析Lua保存的变量文件的解析器。在这些文件中,变量可以采用以下形式:

varname = value

varname = {value,value,...}

varname = {{value,value},{value,value,...}}

我已经为这些类型中的每一种创建了解析器,但是当我将它们与选项< |>组合在一起时运算符我遇到类型错误。

Couldn't match expected type `[Char]' against inferred type `Char'
  Expected type: GenParser Char st [[[Char]]]
  Inferred type: GenParser Char st [[Char]]
In the first argument of `try', namely `lList'
In the first argument of `(<|>)', namely `try lList'

我的假设是(虽然我在文档中找不到)传递给选择运算符的每个解析器必须返回相同的类型。 这是有问题的代码:

data Variable = LuaString ([Char], [Char])
          | LuaList ([Char], [[Char]])
          | NestedLuaList ([Char], [[[Char]]])
          deriving (Show)

main:: IO()
main = do
       case (parse varName "" "variable = {{1234,\"Josh\"},{123,222}}") of
            Left err -> print err
            Right xs -> print xs 

varName :: GenParser Char st Variable
varName = do{
        vName <- (many letter);
        eq <- string " = ";
        vCon <- try nestList
             <|> try lList 
             <|> varContent;
        return (vName, vCon)}

varContent :: GenParser Char st [Char]
varContent =  quotedString 
    <|> many1 letter
    <|> many1 digit

quotedString :: GenParser Char st [Char]
quotedString = do{
         s1 <- string "\""; 
         s2 <- varContent;
         s3 <- string "\"";
         return (s1++s2++s3)}

lList :: GenParser Char st [[Char]]
lList = between (string "{") (string "}") (sepBy varContent (string ","))

nestList :: GenParser Char st [[[Char]]]
nestList = between (string "{") (string "}") (sepBy lList (string ","))

2 个答案:

答案 0 :(得分:7)

这是对的。

(<|>) :: (Alternative f) => f a -> f a -> f a

注意两个参数是如何完全相同的类型。

我并不完全了解您的Variable数据类型。这就是我的方式:

data LuaValue = LuaString String | LuaList [LuaValue]
data Binding = Binding String LuaValue

这允许值被任意嵌套,而不仅仅是像你的那样嵌套两个级别。然后写:

luaValue :: GenParser Char st LuaValue
luaValue = (LuaString <$> identifier)
       <|> (LuaList <$> between (string "{") (string "}") (sepBy (string ",") luaValue))

这是luaValue的 解析器。然后你只需要写:

binding :: GenParser Char st Binding
content :: GenParser Char st [Binding]

你会拥有它。使用准确表示可能性的数据类型非常重要。

答案 1 :(得分:3)

实际上,传递给选择运算符的解析器必须具有相同的类型。您可以通过选择运算符的类型来判断:

(<|>) :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a

这表示只要它们的令牌类型,状态类型和结果类型相同,它就会愉快地组合两个解析器。

那么我们如何确保您尝试组合的解析器具有相同的结果类型?好吧,你已经有了一个数据类型Variable来捕获可以在Lua中出现的不同形式的变量,所以我们需要做的不是返回String[String]或{{1}但只是[[String]] s。

但是当我们尝试我们遇到问题时。我们不能让Variable等返回nestList,因为Variable的构造函数需要变量名称,而我们当时还不知道那些。有一些解决方法(比如返回一个仍然需要变量名的函数Variable)但是有一个更好的解决方案:将变量名与变量可以拥有的不同类型的值分开。

String -> Variable

请注意,我已删除了data Variable = Variable String Value deriving Show data Value = LuaString String | LuaList [Value] deriving (Show) 构造函数。我已将NestedLuaList更改为接受LuaList而不是Value的列表,因此嵌套列表现在可以表示为String LuaList秒。这允许列表被任意深度嵌套,而不是像您的示例中那样只是两个级别。我不知道Lua是否允许这样做,但它使编写解析器更容易。 : - )

现在我们可以让LuaListlList返回nestList s:

Value

lList :: GenParser Char st Value lList = do ss <- between (string "{") (string "}") (sepBy varContent (string ",")) return (LuaList (map LuaString ss)) nestList :: GenParser Char st Value nestList = do vs <- between (string "{") (string "}") (sepBy lList (string ",")) return (LuaList vs) 我已在此处重命名varName,现在返回variable

Variable

我想你会发现,当你在一些输入上运行你的解析器时仍然存在一些问题,但你现在已经比以前更接近解决方案了。

我希望这有帮助!