2010-11-20 103 views
4

我是Haskell和Parsec的新手。在努力了解更多的语言,特别是我想创建一个解析器,可以解析Lua中保存的变量文件库。在这些文件中的变量可以采取以下形式:Haskell's Parsec的问题<|>运营商

VARNAME =价值

VARNAME = {值,值,...}

VARNAME = {{值,值},{价值,价值, ...}}

我创建的解析器每种类型的,但是当我把它们串与选择<一起|>运营商,我得到一个类型错误。

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 ",")) 

回答

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] 

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

+0

变量数据类型旨在将变量名称和变量内容封装在元组中。我可以从你和Martijn的回应中看出,这不是最好的行动方式,因为我需要一个额外的抽象层。 我有一个关于你的回应的问题,但: 运营商在你的代码中的作用是什么? (我以为他们只是为了错误信息)。 – GraemeFore 2010-11-20 11:42:01

+0

我没有使用''。也许你的意思是'<$>'。这只是'fmap'的中缀速记,所以'LuaString <$> identifier'的意思是“解析一个标识符,然后用'LuaString'函数包装结果。 – luqui 2010-11-20 12:07:08

+0

Yikes!为什么你不应该在通宵的时候发布问题的完美例子。再次感谢。 – GraemeFore 2010-11-20 22:18:09

3

事实上,传递给操作者选择解析器必须具有相同的类型。您可以通过选择运营商的类型告诉:

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

这是说,它会很高兴,只要他们的标记类型,状态类型和结果类型是相同的组合两个解析器。

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

但是,当我们尝试我们遇到问题。我们不能让nestList等返回Variable s因为Variable的构造函数需要变量名称,我们现在还不知道那些。这有变通方法(如返回一个函数String -> Variable仍然预计,变量名),但有一个更好的解决方案:从不同类型的一个变量可以具有的值分隔变量名。

data Variable = Variable String Value 
    deriving Show 

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

请注意,我删除了NestedLuaList构造函数。我已经改变了LuaList接受Value真是让人不是String个列表,所以现在嵌套列表可以表示为LuaListLuaList A S。这允许列表任意嵌套,而不仅仅是两个级别,如你的例子。我不知道在Lua中是否允许这样做,但是它使编写解析器变得更容易。 :-)

现在我们可以让lListnestList回报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) 

而且varName,我在这里已经改名为variable,现在返回一个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) 

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

我希望这有助于!

+0

这有助于很多谢谢你。我认为你是对的,我的数据类型不足以满足我想要做的事情。尽管我发现Haskell的类型系统很吸引人,但我不得不承认,我还没有完全理解它。正确定义递归数据类型等事情仍然让我兴奋不已。 – GraemeFore 2010-11-20 11:23:36