Нелинейные шаблоны в квази-кавычках
Я следовал этому руководству, чтобы реализовать квази-цитируемый 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 |]
в паттерн Хаскелла (Add a ((== a) -> True))
,
Но это не сработало. Результирующий паттерн Хаскелла 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
обходит дерево снизу вверх, поэтому внутренние метапеременные могут быть добавлены в набор первыми.