Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.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
Haskell Parsec:回溯不起作用_Haskell_F#_Parsec_Backtracking - Fatal编程技术网

Haskell Parsec:回溯不起作用

Haskell Parsec:回溯不起作用,haskell,f#,parsec,backtracking,Haskell,F#,Parsec,Backtracking,我试图解析F#类型语法。我开始编写[F]Parsec语法,遇到了一些问题,因此我简化为: type ::= identifier | type -> type identifier ::= [A-Za-z0-9.`]+ 在遇到FParsec的问题后,我切换到了Parsec,因为我有一个。我的语法代码是 typeP = choice [identP, arrowP] identP = do id <- many1 (digit <|> letter <|>

我试图解析F#类型语法。我开始编写[F]Parsec语法,遇到了一些问题,因此我简化为:

type ::= identifier | type -> type
identifier ::= [A-Za-z0-9.`]+
在遇到FParsec的问题后,我切换到了Parsec,因为我有一个。我的语法代码是

typeP = choice [identP, arrowP]
identP = do
   id <- many1 (digit <|> letter <|> char '.' <|> char '`')
   -- more complicated code here later
   return id
arrowP = do
  domain <- typeP
  string "->"
  range <- typeP
  return $ "("++domain++" -> "++range++")"
run = parse (do t <- typeP
                eof
                return t) "F# type syntax"
我尝试的第一件事是重新排序typeP:

typeP = choice [arrowP, identP]
但这只是堆栈溢出,因为语法是递归的——typeP永远不会尝试
identP
,因为它不断地尝试
arrowP
。接下来,我在不同的地方尝试了
try
,例如:

typeP = choice [try identP, arrowP]
但我所做的一切似乎都没有改变(1)堆栈溢出或(2)标识符后面不识别“->”的基本行为


我的错误对于任何成功编写了Parsec语法的人来说都是显而易见的。有人能指出这一点吗?

这并不能帮助您理解哪里出了问题,但我建议您使用
sepBy1
来解析由
->
符号分隔的类型。这将为您提供一个已解析类型的列表,之后您可以将其转换回函数类型。

我认为问题在于,我假设F#(因为我不知道),箭头是正确关联的。我不确定链接语法应该有多精确,因为我不太精通不同的语法。但是如果我们假设箭头是正确的,那么问题就容易了

因此,根据这一假设,我们可以轻松做到:

identP = many1 (digit <|> letter <|> char '.' <|> char '`')

typeP = try arrowP <|> identP

arrowP = do
  i <- identP
  string "->"
  t <- typeP
  return $ "(" ++ i ++ " -> " ++ t ++ ")"

run = flip parse "F# type syntax" $ do
        t <- typeP
        eof
        return t
进一步扩展,可能会让您感到困惑的是,在该语法中,它表示type->type,这意味着您可以在左侧有一个箭头。那很好,但它需要放在括号里。这很有帮助,也许看到下面的动作会有所帮助。它帮助了我

typeP = try arrowP <|> parens typeP <|> identP

arrowP = do
 i <- parens typeP <|> identP
 string "->"
 t <- typeP
 return $ "(" ++ i ++ " -> " ++ t ++ ")"

parens p  = between (char '(') (char ')') p

我认为你应该把左递归排除在语法之外。而不是

type ::= identifier | type -> type 
identifier ::= [A-Za-z0-9.`]+ 
你会得到这样的结果

typeStart ::= identifier 
type ::= typeStart (-> type)?
identifier ::= [A-Za-z0-9.`]+ 
我认为,这将更容易直接翻译成parsec。(有人会认为,
try
会起作用,我也希望它能起作用,但是,是的,我的经验是,在我理解“在哪里放置
try
”以使事情起作用之前,我必须至少在Parsec中有齐腰深。)


考虑看一些基础知识(以及之前的7篇C#博客文章)。我认为(试着自上而下地阅读,如果我没记错的话,它们是不错的)以及各种研究论文中的一些例子谈论的问题与你问题中的问题类似

很好的解释。正如您所注意到的,问题的根源在于您需要打破arrowP可以下降到typeP的循环,而typeP本身也可以下降到typeP。我认为您的
parens
示例特别有启发性。因此,Parsec语法与LR(1)语法基本上存在相同的非组合问题,即您必须规划整个语法,以便每个规则的左边缘最终重写为明确的文字。哦,好吧,我想我应该知道Parsec是神奇的。是的,我想我最终会这么做,但由于Seppy1可能涉及我必须手动编写的相同递归,我想我应该从更简单的语法开始。@Nathan-是的,即使你使用Seppy1,你仍然需要使用类似克里斯托弗的方法来打破递归。你是对的,研究论文可能是最终回答这个问题的最佳选择。我,我只是没有和他们一起写过很多语法。我认为Parsec会神奇地比我用Python和C#编写的hoky示例更聪明。
Haskell> run "int->int->int"
Right "(int -> (int -> int))"
Haskell> run "(int->int)->int"
Right "((int -> int) -> int)"
type ::= identifier | type -> type 
identifier ::= [A-Za-z0-9.`]+ 
typeStart ::= identifier 
type ::= typeStart (-> type)?
identifier ::= [A-Za-z0-9.`]+