3

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.

rem
  • 893
  • 4
  • 18
  • 2
    I think `lookupValueName` considers variables in scope just _outside_ the splice, and not the ones which are being defined within the pattern. E.g. `eval (Add x [expr| ... |]) = ...` can see `x`. You'll have to find the variables defined _within_ the pattern-sllice on your own. – chi Feb 22 '18 at 18:02

0 Answers0