4

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

变量名 = 值

varname = {值,值,...}

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

2 回答 2

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]

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

于 2010-11-20T09:13:59.933 回答
3

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

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

这表示只要它们的标记类型、状态类型和结果类型相同,它就会很高兴地组合两个解析器。

那么我们如何确保您尝试组合的那些解析器具有相同的结果类型呢?好吧,你已经有了一个数据类型Variable,它可以捕获 Lua 中可能出现的不同形式的变量,所以我们需要做的不是 return String[String]或者[[String]]只是Variables。

但是当我们尝试这样做时,我们遇到了问题。我们还不能让nestListetc. 返回Variables,因为构造函数Variable需要变量名,而那时我们还不知道这些。有一些解决方法(例如返回String -> Variable仍然需要该变量名的函数),但有一个更好的解决方案:将变量名与变量可以具有的不同类型的值分开。

data Variable = Variable String Value
  deriving Show

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

请注意,我已经删除了NestedLuaList构造函数。我已更改LuaList为接受 s 的列表Value而不是Strings,因此嵌套列表现在可以表示为 a LuaListof LuaLists。这允许列表嵌套任意深度,而不是像您的示例中那样仅嵌套两个级别。我不知道这在 Lua 中是否允许,但它使编写解析器更容易。:-)

现在我们可以让lListnestList返回Values:

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)

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

我希望这有帮助!

于 2010-11-20T09:11:38.787 回答