Parsing 将语法生成转换为Parsec
我正在尝试转换以下语法产品Parsing 将语法生成转换为Parsec,parsing,haskell,functional-programming,parsec,left-recursion,Parsing,Haskell,Functional Programming,Parsec,Left Recursion,我正在尝试转换以下语法产品 callExpr: primaryExpr | callExpr primaryExpr 到Haskell中的Parsec表达式 显然,问题在于它是左递归的,所以我尝试用递归上升的方式来解析它。我试图实现的伪代码是: e = primaryExp while(true) { e2 = primaryExp if(e2 failed) break; e = CallExpr(e, e2) } 我试图将此翻译成Haskell: ca
callExpr:
primaryExpr
| callExpr primaryExpr
到Haskell中的Parsec表达式
显然,问题在于它是左递归的,所以我尝试用递归上升的方式来解析它。我试图实现的伪代码是:
e = primaryExp
while(true) {
e2 = primaryExp
if(e2 failed) break;
e = CallExpr(e, e2)
}
我试图将此翻译成Haskell:
callExpr :: IParser Expr
callExpr = do
e <- primaryExpr
return $ callExpr' e
where
callExpr' e = do
e2m <- optionMaybe primaryExpr
e' <- maybe e (\e2 -> callExpr' (CallExpr e e2)) e2m
return e'
但是,这给了我以下类型错误:
Couldn't match type `ParsecT String () (State SourcePos) t0'
with `Expr'
Expected type: ParsecT String () (State SourcePos) Expr
Actual type: ParsecT
String
()
(State SourcePos)
(ParsecT String () (State SourcePos) t0)
In a stmt of a 'do' block: return $ callExpr' e
In the expression:
do { e <- primaryExpr;
return $ callExpr' e }
In an equation for `callExpr':
callExpr
= do { e <- primaryExpr;
return $ callExpr' e }
where
callExpr' e
= do { e2m <- optionMaybe primaryExpr;
.... }
无法匹配类型'ParsecT String()(State SourcePos)t0'
带“Expr”
预期类型:ParsecT String()(State SourcePos)Expr
实际类型:ParsecT
一串
()
(State SourcePos)
(解析字符串()(状态SourcePos)t0)
在'do'块的stmt中:返回$callExpr'e
在表达式中:
不要使用。chainl1p op
以左关联方式解析一个或多个由op
-s分隔的p
-s。op
返回一个二进制函数,用于将两侧p
-s的结果合并为一个结果
由于语法中似乎没有分隔符,因此可以将chainl1
与只返回组合函数的op
一起使用:
callExpr :: IParser Expr
callExpr = chainl1 primaryExpr (return CallExpr)
至于您的callExpr
实现,我可以发现两个错误
首先,使用return$callExpr'e
,但是callExpr'e
已经是一个一元值,所以只要callExpr'e
就可以了
其次,在e(\e2->callExpr'(callExpr e e2))e2m
中,默认的e
应该是一元的(否则我们如何将其绑定到e'
?),所以它应该是return e
你能解释一下为什么需要这个组合器吗?是什么让它比使用many
然后折叠结果更好呢?我还在尝试学习解析的基础知识。它可能快一些,但我认为它主要是约定和意图交流。我认为parser combinators通常应类似于“纯”尽可能多地使用CFG表示法,并应尽量减少事后AST操作的数量。我们的代码读者应该能够清楚地看到语法,并且不会被操作细节过度拖累。左递归是我们必须偏离抽象表示法的一个点,因此我们尝试将其封装在chainl
中。
callExpr :: IParser Expr
callExpr = chainl1 primaryExpr (return CallExpr)