Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing 使用Parsec在Haskell中编写小型解析器时出现问题_Parsing_Haskell_Parsec - Fatal编程技术网

Parsing 使用Parsec在Haskell中编写小型解析器时出现问题

Parsing 使用Parsec在Haskell中编写小型解析器时出现问题,parsing,haskell,parsec,Parsing,Haskell,Parsec,我正试图用下面的代码为一种小型语言编写一个解析器 import Text.ParserCombinators.Parsec import Text.Parsec.Token data Exp = Atom String | Op String Exp instance Show Exp where show (Atom x) = x show (Op f x) = f ++ "(" ++ (show x) ++ ")" parse_exp :

我正试图用下面的代码为一种小型语言编写一个解析器

import Text.ParserCombinators.Parsec
import Text.Parsec.Token

data Exp = Atom String | Op String Exp

instance Show Exp where
  show (Atom x) = x
  show (Op f x) = f ++ "(" ++ (show x) ++ ")"

parse_exp :: Parser Exp
parse_exp = (try parse_atom) <|> parse_op

parse_atom :: Parser Exp
parse_atom = do
  x <- many1 letter
  return (Atom x)

parse_op :: Parser Exp
parse_op = do
  x <- many1 letter
  char '(' 
  y <- parse_exp
  char ')'
  return (Op x y)
然后我得到正确的结果

>>> parse (parse_exp <* eof) "<error>" "s(t)"
Right s(t)

>>parse(parse_exp问题是字符串
“s”
根据您的定义计算为一个原子。请尝试以下操作:

parse parse_atom "" "s(t)"
> Atom "s"
因此,您的解析器
parse_exp
实际上成功了,返回了
Atom“s”
,但是您也期望在它之后出现一个EOF,这就是它失败的地方,遇到的是一个打开的paren而不是EOF(就像错误消息所说的!)


当您交换备选方案时,它将首先尝试
parse_op
,这将成功,返回
op“s”t
,然后遇到EOF,正如预期的那样。

当Parsec解析器(如
parse_atom
)在特定字符串上运行时,有四种可能的结果:

  • 它成功了,消耗了一些输入
  • 它失败了,消耗了一些输入
  • 它成功了,不消耗任何输入
  • 它失败,不消耗任何输入
  • 在Parsec源代码中,这些被称为“消耗的ok”、“消耗的err”、“空的ok”和“空的err”(有时缩写为cok、cerr、eok、eer)

    当在另一种情况下使用两个Parsec解析器时,如
    pq
    ,以下是解析方式。首先,Parsec尝试使用
    p
    进行解析。然后:

    • 如果这导致“已消费ok”或“空ok”,则解析成功,这将成为整个解析器的结果
      pq
    • 如果这导致“empty err”,Parsec将尝试替代
      q
      ,这将成为整个
      pq
      解析器的结果
    • 如果这导致“消耗的错误”,则整个解析器
      pq
      将以“消耗的错误”(cerr)失败
    注意
    p
    返回cerr(导致整个解析器失败)与返回eer(导致尝试替代解析器
    q
    )之间的关键区别

    try
    函数通过将“cerr”结果转换为“eerr”结果来更改解析器的行为

    这意味着,如果您试图使用不同的解析器解析文本
    “s(t)”

    • 使用解析器
      parse_atom parse_op
      ,解析器
      parse_atom
      返回“cok”,消耗
      “s”
      ,并留下不可解析的文本
      “(t)”
      ,从而导致错误
    • 使用解析器
      try-parse\u-atom-parse\u-op
      ,解析器
      parse\u-atom
      仍然返回“cok”消费
      “s”
      ,因此
      try
      (仅将cerr更改为eerr)无效,并且不可解析的文本
      (t)
      导致相同的错误
    • 使用解析器
      parse\u op parse\u atom
      ,解析器
      parse\u op
      成功解析字符串(实际上,这不是因为对
      parse\u exp
      的递归调用无法解析
      “t”
      ,但让我们忽略这一点);但是,如果对文本
      “s”使用相同的解析器
      ,则
      parse\u op
      将在失败之前消耗
      “s”
      (即cerr),导致整个解析失败,而不是尝试替代的
      parse\u atom
    • 使用解析器
      try parse_op parse_atom
      ,这将解析
      “s(t)”
      ,与前面的示例完全相同,
      try
      将没有任何效果;但是,它也将对文本
      “s”
      ,因为
      parse_op
      将消耗
      “s”
      在cerr失败之前,然后
      try
      将通过将cerr转换为eerr来“拯救”解析,并检查备选
      parse_atom
      ,成功解析(cok)atom
      “s”
    这就是为什么针对您的问题的“正确”解析器是
    尝试parse\u op parse\u atom

    请注意,这种行为不是一元语法分析器的一个基本方面。它是由Parsec(以及兼容的语法分析器,如Megaparsec)做出的设计选择。其他一元语法分析器可以有不同的规则来说明
    的替代方案如何工作

    这类Parsec解析问题的“一般解决方案”是了解表达式
    pq
    中的事实:

    • p
      首先尝试,如果成功,
      q
      将被忽略,即使
      q
      将提供“更长的”或“更好的”或“更合理的”解析或避免进一步出现其他解析错误。在
      parse_atom parse_op
      中,由于
      parse_atom
      可以在用于
      parse_op
      的字符串上成功,因此此顺序将无法正常工作
    • q
      只有在
      p
      失败而不消耗输入时才会尝试。如果您希望检查备选方案
      q
      ,则必须安排
      p
      在失败时不消耗任何东西,可能通过使用
      try
      。因此,如果
      parse\u op,则
      parse\u-opparse\u-atom
      不起作用e> 在意识到无法继续并返回cerr之前,开始使用某些东西(如标识符)
    除了使用
    try
    ,您还可以更仔细地考虑解析器的结构。例如,编写
    parse\u exp
    的另一种方法是:

    parse_exp :: Parser Exp
    parse_exp = do
      -- there's always an identifier
      x <- many1 letter
      -- there *might* be an expression in parentheses
      y <- optionMaybe (parens parse_exp)
      case y of
        Nothing -> return (Atom x)
        Just y' -> return (Op x y')
    
      where parens = between (char '(') (char ')')
    
    parse_exp::Parser exp
    parse_exp=do
    --总有一个标识符
    
    有没有方法可以正确解析整个字符串?如果我删除eof,那么解析器将停止在s,但我想解析整个字符串“s(t)”,这就是翻转的AtAlternative将要做的
    >>> parse (parse_exp <* eof) "<error>" "s(t)"
    Right s(t)
    
    parse parse_atom "" "s(t)"
    > Atom "s"
    
    parse_exp :: Parser Exp
    parse_exp = do
      -- there's always an identifier
      x <- many1 letter
      -- there *might* be an expression in parentheses
      y <- optionMaybe (parens parse_exp)
      case y of
        Nothing -> return (Atom x)
        Just y' -> return (Op x y')
    
      where parens = between (char '(') (char ')')