Haskell 秒差距 <|> 的问题操作员

发布于 2024-10-03 10:56:41 字数 1759 浏览 0 评论 0 原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

锦欢 2024-10-10 10:56:41

这是正确的。

(<|>) :: (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 <
gt; identifier)
       <|> (LuaList <
gt; between (string "{") (string "}") (sepBy (string ",") luaValue))

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

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

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

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 <
gt; identifier)
       <|> (LuaList <
gt; 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.

寻找我们的幸福 2024-10-10 10:56:41

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

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

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

那么我们如何确保您尝试组合的那些解析器具有相同的结果类型?好吧,你已经有了一个数据类型 Variable 来捕获 Lua 中可能出现的不同形式的变量,所以我们需要做的不是返回 String[ String][[String]] 但只是 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 的 >LuaList。这允许列表嵌套任意深度,而不是像您的示例中那样只有两层。我不知道 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)

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

我希望这有帮助!

Indeed, parsers passed to the choice operator must have equal types. You can tell by the type of the choice operator:

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

This says that it will happily combine two parsers as long as their token types, state types and result types are the same.

So how do we make sure those parsers you're trying to combine have the same result type? Well, you already have a datatype Variable that captures the different forms of variables that can appear in Lua, so what we need to do is not return String, [String] or [[String]] but just Variables.

But when we try that we run into a problem. We can't let nestList etc. return Variables yet because the constructors of Variable require variable names and we don't know those yet at that point. There are workarounds for this (such as return a function String -> Variable that still expects that variable name) but there is a better solution: separate the variable name from the different kinds of values that a variable can have.

data Variable = Variable String Value
  deriving Show

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

Note that I've removed the NestedLuaList constructor. I've changed LuaList to accept a list of Values rather than Strings, so a nested list can now be expressed as a LuaList of LuaLists. This allows lists to be nested arbitrarily deep rather than just two levels as in your example. I don't know if this is allowed in Lua but it made writing the parsers easier. :-)

Now we can let lList and nestList return 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)

And varName, which I've renamed variable here, now returns a 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'll find that when you run your parser on some input there are still some problems, but you're already a lot closer to the solution now than before.

I hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文