I am new to both Haskell and Parsec. In an effort to learn more about the language and that library in particular I am trying to create a parser that can parse Lua saved variable files. In these files variables can take the following forms:
varname = value
varname = {value, value,...}
varname = {{value, value},{value,value,...}}
I've created parsers for each of these types but when I string them together with the choice <|> 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 is (although I can't find it in the documentation) that each parser passed to the choice operator must return the same type. Here's 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 ","))
That's correct.
(<|>) :: (Alternative f) => f a -> f a -> f a
Notice how both arguments are exactly the same type.
I don't exactly understand your Variable
data type. This is the way I would do it:
data LuaValue = LuaString String | LuaList [LuaValue]
data Binding = Binding String LuaValue
This allows values to be arbitrarily nested, not just nested two levels deep like yours has. 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'll have it. Using a data type that accurately represents what is possible is important.