Problems with the Haskell Parsec operator <|>

I am new to Haskell and Parsec. In an attempt to learn more about the language and this library, in particular, I am trying to create a parser that can parse Lua stored variable files. Variables can take the following forms in these files:

varname = value

varname = {value, value, ...}

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

I created parsers for each of these types, but when I build them together with the select operator <| >, I get a type error.

 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' 

My assumption (although I cannot find it in the documentation) is that every parser passed to the select statement should return the same type. Here is the code in question:

 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 ",")) 
+4
source share
2 answers

It is right.

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

Notice how both arguments are exactly the same.

I definitely don't understand your Variable data type. So I would do this:

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

This allows the values ​​to be arbitrarily nested, not just nested by two levels, the same as yours. Then write:

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

This is the parser for luaValue. Then you just need to write:

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

And you will have it. Using a data type that accurately represents what is possible is important.

+7
source

Indeed, parsers passed to the select statement must be of the same type. You can specify the type of select statement:

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

This suggests that he will gladly combine the two parsers if their types of tokens, types of states and types of results are the same.

So, how do we make sure that the parsers you are trying to combine have the same type of result? Well, you already have a Variable data type that captures various forms of variables that may appear in Lua, so we need not return String , [String] or [[String]] , but just Variable s.

But when we try, we are faced with a problem. We cannot allow nestList , etc. Return Variable , but since Variable constructors require variable names, we don’t know them yet. There are workarounds for this (for example, returning the String -> Variable function, which still expects this variable name), but there is a better solution: to separate the variable name from the different types of values ​​that the variable may have.

 data Variable = Variable String Value deriving Show data Value = LuaString String | LuaList [Value] deriving (Show) 

Note that I removed the NestedLuaList constructor. I modified LuaList to accept a list of Value , not String s, so now the nested list can be expressed as LuaList from LuaList s. This allows lists to be nested arbitrarily deep, and not just two levels, as in your example. I don't know if this is allowed in Lua, but it made writing parsers easier. :-)

Now we can leave lList and nestList return Value s:

 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) 

And varName , which I renamed Variable here, now returns Variable :

 variable :: GenParser Char st Variable variable = do vName <- (many letter) eq <- string " = " vCon <- try nestList <|> try lList <|> (do v <- varContent; return (LuaString v)) return (Variable vName vCon) 

I think you will find that when you start your parser at some input there are still some problems, but you are already much closer to the solution than before.

Hope this helps!

+3
source

All Articles