I followed this tutorial to implement a quasi quoted DSL, and I now want to support non-linear patterns in a quoted pattern. That will allow a repeated binder in a pattern to assert the equality of the matched data. For example, one can then write eval [expr| $a + $a|] = 2 * eval a
. I modified antiExprPat
as follows:
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
The idea is to use lookupValueName
to check if the anti-quoted name s
is in scope. If not, then just create a binder with the same name. Otherwise, create a view pattern (== s) -> True
that asserts the matched data equals to the data already bound to s
. Essentially, I want to convert the quoted pattern [expr| $a + $a |]
to the Haskell pattern (Add a ((== a) -> True))
.
But that didn't work. The resulting Haskell pattern is Add a a
, which means lookupValueName
never thinks a
is in scope. Am I misunderstanding how lookupValueName
works? Or is there a better way to implement non linear patterns here?
The full code is here if you want to play with it. In short, I'm making a quasi quoter to match on Java source.
Update 1:
As @chi pointed out, lookupValueName
only checks for the splice's context, whereas I need to check for the splice's content. Any idea how to proceed with that?
Update 2:
So I bit the bullet and threaded the set of in-scope names with a state monad, and traversed the parse tree with transformM
which replaces every in-scope meta-variable x
with ((== 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
It got the right result on the inputs I have, but I have no idea if it is correct, especially given transformM
traverses the tree bottom-up so inner meta-variables may be added to the set first.