summaryrefslogtreecommitdiff
path: root/src/Simplify/TH.hs
blob: 2e0076a767fea5db505bd05253030f11436f2aec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
{-# LANGUAGE TemplateHaskellQuotes #-}
module Simplify.TH (simprec) where

import Data.Bifunctor (first)
import Data.Char
import Data.List (foldl1')
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Text.ParserCombinators.ReadP


-- [simprec| EPair ext *a *b |]
-- ~>
-- do a' <- within (\a' -> EPair ext a' b) (simplify' a)
--    b' <- within (\b' -> EPair ext a' b') (simplify' b)
--    pure (EPair ext a' b')

simprec :: QuasiQuoter
simprec = QuasiQuoter
  { quoteDec = \_ -> fail "simprec used outside of expression context"
  , quoteType = \_ -> fail "simprec used outside of expression context"
  , quoteExp = handler
  , quotePat = \_ -> fail "simprec used outside of expression context"
  }

handler :: String -> Q Exp
handler str =
  case readP_to_S pTemplate str of
    [(template, "")] -> generate template
    _:_:_ -> fail "simprec: template grammar ambiguous"
    _ -> fail "simprec: could not parse template"

generate :: Template -> Q Exp
generate (Template topitems) =
  let takePrefix (Plain x : xs) = first (x:) (takePrefix xs)
      takePrefix xs = ([], xs)

      itemVar "" = error "simprec: empty item name?"
      itemVar name@(c:_) | isLower c = VarE (mkName name)
                         | isUpper c = ConE (mkName name)
                         | otherwise = error "simprec: non-letter item name?"

      loop :: Exp -> [Item] -> Q [Stmt]
      loop yet [] = return [NoBindS (VarE 'pure `AppE` yet)]
      loop yet (Plain x : xs) = loop (yet `AppE` itemVar x) xs
      loop yet (Recurse x : xs) = do
        primeName <- newName (x ++ "'")
        let appPrePrime e (Plain y) = e `AppE` itemVar y
            appPrePrime e (Recurse y) = e `AppE` itemVar y
        let stmt = BindS (VarP primeName) $
                     VarE (mkName "within")
                     `AppE` LamE [VarP primeName] (foldl' appPrePrime (yet `AppE` VarE primeName) xs)
                     `AppE` (VarE (mkName "simplify'") `AppE` VarE (mkName x))
        stmts <- loop (yet `AppE` VarE primeName) xs
        return (stmt : stmts)

      (prefix, items') = takePrefix topitems
  in DoE Nothing <$> loop (foldl1' AppE (map itemVar prefix)) items'

data Template = Template [Item]
  deriving (Show)

data Item = Plain String | Recurse String
  deriving (Show)

pTemplate :: ReadP Template
pTemplate = do
  items <- many (skipSpaces >> pItem)
  skipSpaces
  eof
  return (Template items)

pItem :: ReadP Item
pItem = (char '*' >> Recurse <$> pName) +++ (Plain <$> pName)

pName :: ReadP String
pName = do
  c1 <- satisfy (\c -> isAlpha c || c == '_')
  cs <- munch (\c -> isAlphaNum c || c `elem` "_'")
  return (c1:cs)