packages feed

alms-0.6.0: src/Message/Quasi.hs

{-# LANGUAGE GADTs #-}
module Message.Quasi (
  msg, Message(), H, V,
) where

import Message.AST
import Message.Parser
import Meta.THHelpers
import Syntax.PprClass
import Util hiding (lift)

import Prelude ()
import qualified Data.Map as M
import Language.Haskell.TH
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH.Syntax (lift)

msg :: QuasiQuoter
msg  = (newQuasi "msg") { quoteExp = parseMessageQ >=> msgAstToExpQ }

msgAstToExpQ :: Message d -> ExpQ
msgAstToExpQ msg0 = do
  namelist <- sequence
                [ (,) (show z) `liftM` newName ("_v"++show z)
                | z <- [ 1 .. highest msg0 ] ]
  let expQ = translate (M.fromList namelist) msg0
  foldr (\(_, var) -> lamE [varP var]) expQ namelist
  where
  highest :: Message d -> Int
  highest msg1 = case msg1 of
    Words _           -> 0
    Flow msgs         -> maximum (map highest msgs)
    Exact _           -> 0
    Surround _ _ msg' -> highest msg'
    Quote msg'        -> highest msg'
    Stack _ msgs      -> maximum (map highest msgs)
    Table rows        -> maximum (map (highest . snd) rows)
    Indent msg'       -> highest msg'
    Printable _ _     -> 0
    Showable _        -> 0
    AntiMsg _ name    -> case readsPrec 0 name of
      (z,""):_          -> z
      _                 -> 0
  --
  translate :: M.Map String Name -> Message d -> ExpQ
  translate namemap = loop where
    loop :: Message d -> ExpQ
    loop msg1 = case msg1 of
      Words s     -> [| Words $(lift s) |]
      Flow msgs   -> [| Flow $(listE (map loop msgs)) |]
      Exact s     -> [| Exact $(lift s) |]
      Surround s e msg'
                  -> [| Surround $(lift s) $(lift e) $(loop msg') |]
      Quote msg'  -> [| Quote $(loop msg') |]
      Stack sty msgs
                  -> [| Stack $(styleQ sty)
                              $(listE (map loop msgs)) |]
        where styleQ Numbered  = [| Numbered |]
              styleQ Bulleted  = [| Bulleted |]
              styleQ Separated = [| Separated |]
              styleQ Broken    = [| Broken |]
      Table rows  -> [| Table $(listE (map each rows)) |]
        where each (s,msg') = [| ($(lift s), $(loop msg')) |]
      Indent msg' -> [| Indent $(loop msg') |]
      Printable d a
                  -> [| Exact $(lift (show (pprDepth d a))) |]
      Showable a  -> [| Exact $(lift (show a)) |]
      AntiMsg tag name -> case tag of
        "words"   -> [| Words $var |]
        "flow"    -> [| Flow $var |]
        "exact"   -> [| Exact $var |]
        "msg"     -> var
        "ol"      -> [| Stack Numbered $var |]
        "ul"      -> [| Stack Bulleted $var |]
        "br"      -> [| Stack Broken $var |]
        "p"       -> [| Stack Separated $var |]
        "dl"      -> [| Table $var |]
        "indent"  -> [| Indent $var |]
        "show"    -> [| Showable $var |]
        'v':tag'  -> [| $(loop (AntiMsg tag' name)) :: Message V |]
        'h':tag'  -> [| $(loop (AntiMsg tag' name)) :: Message H |]
        'q':tag'  -> [| Quote $(loop (AntiMsg tag' name)) |]
        ""        -> [| Printable 0 $var |]
        _ | [(d,"")] <- (reads tag :: [(Int,String)])
                  -> [| Printable d $var |]
        _         -> fail $
          "Unknown message antiquote tag: ‘" ++ tag ++ "’"
        where var = varE (M.findWithDefault (mkName name) name namemap)