我跟着this tutorial实现了一个准引用的DSL,现在我想以引用的模式支持non-linear patterns。这将允许模式中重复的绑定器断言匹配数据的相等性。例如,可以编写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
是否在范围内。如果没有,那么只需创建一个具有相同名称的活页夹。否则,创建一个view pattern (== s) -> True
,声明匹配的数据等于已绑定到s
的数据。基本上,我想将引用的模式[expr| $a + $a |]
转换为Haskell模式(Add a ((== a) -> True))
。
但那没用。生成的Haskell模式为Add a a
,这意味着lookupValueName
永远不会认为a
在范围内。我误解了lookupValueName
的工作原理吗?或者是否有更好的方法来实现非线性模式?
如果您想使用它,则完整代码为here。简而言之,我正在制作一个准引号以匹配Java源代码。
更新1:
正如@chi所指出的那样,lookupValueName
只检查拼接的上下文,而我需要检查拼接的内容。知道如何继续吗?
更新2:
所以我咬了一下子弹并用范围内的monad编写了范围内的名称,然后使用transformM
遍历解析树,用{{{}替换每个范围内的元变量x
。 1}}:
((== 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
从头到底遍历树,因此可以首先将内部元变量添加到集合中。