将语法生成翻译成秒差距
Translate grammar production to Parsec
我正在尝试转换以下语法产生式
callExpr:
primaryExpr
| callExpr primaryExpr
到 Haskell 中的秒差距表达式。
显然问题是它是左递归的,所以我试图解析它递归上升的风格。我试图实现的伪代码是:
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'
其中 primaryExpr
的类型为 IParser Expr
IParser 定义为
type IParser a = ParsecT String () (State SourcePos) a
然而,这给了我以下类型错误:
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;
.... }
如何修复此类型错误?
使用chainl1
。 chainl1 p op
以左关联方式解析由 op
-s 分隔的一个或多个 p
-s。 op
returns 一个二进制函数,用于将两侧的 p
-s 的结果合并为一个结果。
因为你的语法似乎没有分隔符,你可以使用 chainl1
和 op
只是 returns 组合函数:
callExpr :: IParser Expr
callExpr = chainl1 primaryExpr (return CallExpr)
关于您的 callExpr
实施,我可以发现两个错误。
首先,您使用 return $ callExpr' e
,但是 callExpr' e
已经是一个单子值,所以 callExpr' e
是正确的。
其次,在maybe e (\e2 -> callExpr' (CallExpr e e2)) e2m
中,默认的e
应该是monadic的(否则我们如何将它绑定到e'
?),所以它应该是return e
.
我正在尝试转换以下语法产生式
callExpr:
primaryExpr
| callExpr primaryExpr
到 Haskell 中的秒差距表达式。
显然问题是它是左递归的,所以我试图解析它递归上升的风格。我试图实现的伪代码是:
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'
其中 primaryExpr
的类型为 IParser Expr
IParser 定义为
type IParser a = ParsecT String () (State SourcePos) a
然而,这给了我以下类型错误:
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;
.... }
如何修复此类型错误?
使用chainl1
。 chainl1 p op
以左关联方式解析由 op
-s 分隔的一个或多个 p
-s。 op
returns 一个二进制函数,用于将两侧的 p
-s 的结果合并为一个结果。
因为你的语法似乎没有分隔符,你可以使用 chainl1
和 op
只是 returns 组合函数:
callExpr :: IParser Expr
callExpr = chainl1 primaryExpr (return CallExpr)
关于您的 callExpr
实施,我可以发现两个错误。
首先,您使用 return $ callExpr' e
,但是 callExpr' e
已经是一个单子值,所以 callExpr' e
是正确的。
其次,在maybe e (\e2 -> callExpr' (CallExpr e e2)) e2m
中,默认的e
应该是monadic的(否则我们如何将它绑定到e'
?),所以它应该是return e
.