我按照本教程实现了一个准引用 DSL,现在我想在引用模式中支持非线性模式。这将允许模式中的重复绑定器断言匹配数据的相等性。例如,然后可以编写eval [expr| $a + $a|] = 2 * eval a
. 我修改antiExprPat
如下:
antiExpPat (MetaExp s) =
Just (do b <- lookupValueName s
let n = mkName s
p0 = VarP n
p1 <- (viewP [|(== $(varE n))|] [p|True|])
let res = case b of Nothing -> p0
_ -> p1
return res)
antiExpPat _ = Nothing
这个想法是用来lookupValueName
检查反引号名称s
是否在范围内。如果没有,那么只需创建一个具有相同名称的活页夹。否则,创建一个视图模式 (== s) -> True
,断言匹配的数据等于已经绑定到的数据s
。本质上,我想将引用的模式转换[expr| $a + $a |]
为 Haskell 模式(Add a ((== a) -> True))
。
但这没有用。生成的 Haskell 模式是Add a a
,这意味着lookupValueName
从不认为a
在范围内。我误解了它的lookupValueName
工作原理吗?还是有更好的方法在这里实现非线性模式?
如果你想玩它,完整的代码在这里。简而言之,我正在制作一个准引用器以匹配 Java 源代码。
更新1:
正如@chi 指出的那样,lookupValueName
只检查拼接的上下文,而我需要检查拼接的内容。知道如何进行吗?
更新 2:
所以我咬紧牙关,用一个状态单子串接了一组范围内的名称,并遍历了解析树,用它替换了每个范围内的transformM
元变量:x
((== x) -> True)
dataToPatQ (const Nothing `extQ` ...) (evalState (rename s) DS.empty)
...
rename :: Language.Java.Syntax.Stmt -> State (DS.Set String) Language.Java.Syntax.Stmt
rename p = transformM rnvar p
where rnvar (MetaStmt n) = do s <- get
let res = if DS.member n s
then (SAssertEq n)
else (MetaStmt n)
put (DS.insert n s)
return res
rnvar x = return x
它在我的输入上得到了正确的结果,但我不知道它是否正确,特别是考虑transformM
到自下而上遍历树,因此可以首先将内部元变量添加到集合中。