【问题标题】:Haskell - Parsec :: Parse spaces until string literalHaskell - Parsec :: 解析空格直到字符串文字
【发布时间】:2021-01-04 21:12:39
【问题描述】:

我目前正在尝试使用 Parsec 在 Haskell 中设计一个解析器。 声明类型的语法应该是这样的:

Fruit is a Apple

类型也应该可以有参数:

Fruit a b is a Apple

在哪里

  • Fruit 的类型为 Name
  • a b 的类型为 [Parameter]
  • Apple 的类型为 Value

这里的问题是我的解析器目前不知道什么时候停止解析parameters并开始解析value

代码如下:

newtype Name = Name String deriving (Show)
newtype Parameter = Parameter String deriving (Show)
newtype Value = Value String deriving (Show)

data TypeAssignment = TypeAssignment Name [Parameter] Value deriving (Show)

-- first variant using `sepBy`
typeAssigment :: Parser TypeAssignment
typeAssigment =
    TypeAssignment
    <$> name
    <*> (space *> parameter `sepBy` space)
    <*> (string "is a" *> value)

-- second variant using manyTill
typeAssigment2 :: Parser TypeAssignment
typeAssigment2 =
    TypeAssignment 
    <$> name
    <*> (space *> manyTill parameter (string "is a"))
    <*> value

name :: Parser Name
name = Name <$> word

parameter :: Parser Parameter
parameter = Parameter <$> word

value :: Parser Value
value = Value <$> word

word :: Parser String
word = (:) <$> letter <*> many (letter <|> digit)

我尝试以两种我知道的方式解析参数/值(一次使用 sepBy,一次使用 manyTill)都失败了,几乎出现了解析错误:

*EParser> parseTest typeAssigment "Fruit a b is a Apple"
parse error at (line 1, column 21):
unexpected end of input
expecting space or "is a"

*EParser> parseTest typeAssigment2 "Fruit a b is a Apple"
parse error at (line 1, column 8):
unexpected " "
expecting letter, digit or "is a"

【问题讨论】:

    标签: haskell parsec


    【解决方案1】:

    typeAssignment1 的问题在于 "is""a" 是完全有效的 parameter 解析。所以,参数解析会吞噬整个输入,直到什么都没有,然后你会得到一个错误。事实上,如果您仔细查看该错误,您会发现这是真的:解析器需要一个空格(用于更多参数)或“is a”(整个解析器的终端)。

    另一方面,typeAssignment2 非常接近,但您似乎没有正确处理空格。为了解析多个parameters,您需要解析这些参数之间的所有spaces,而不仅仅是第一个。

    我认为以下替代方案应该可以解决问题:

    typeAssigment3 :: Parser TypeAssignment
    typeAssigment3 =
        TypeAssignment 
        <$> name
        <*> manyTill (space *> parameter) (try $ string "is a")
        <*> (space *> value)
    

    【讨论】:

    • 谢谢,这帮助很大!需要稍作修改,然后它对我来说非常适合:typeAssigment3 :: Parser TypeAssignment typeAssigment3 = TypeAssignment &lt;$&gt; name &lt;*&gt; (space *&gt; manyTill (parameter &lt;* space) (P.try $ string "is a")) &lt;*&gt; (space *&gt; value)
    • 哦,是的,感谢您指出这一点(这就是我自己没有实际测试代码的结果)。我更新了答案以包含try
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多