Я выполнил это руководство, чтобы реализовать 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
пересекает дерево снизу вверх, поэтому сначала в набор могут быть добавлены внутренние метапеременные.
lookupValueName
рассматривает переменные в области видимости только вне соединения, а не те, которые определяются в шаблоне. Например.eval (Add x [expr| ... |]) = ...
может видетьx
. Вам придется самостоятельно найти переменные, определенные внутри фрагмента шаблона. - person chi   schedule 22.02.2018