{-# LANGUAGE TemplateHaskell #-} module Pinchot.SyntaxTree.Wrappers where import qualified Control.Lens as Lens import Data.List.NonEmpty (NonEmpty) import Data.Maybe (catMaybes) import qualified Language.Haskell.TH as T import Pinchot.Names import Pinchot.Rules import Pinchot.Types -- # Wrapped -- | Creates a 'Lens.Wrapped' instance for each 'Rule' and its -- ancestors, if there is an instance. -- Only 'Pinchot.terminal', 'Pinchot.wrap', -- 'Pinchot.opt', 'Pinchot.star', and 'Pinchot.plus' -- get instances of 'Lens.Wrapped'. -- -- This must be -- spliced in the same module in which the syntax tree types are -- created; this way, no orphans are created. Since ancestors are -- included, you can get the entire tree of types that you need by -- applying this function to a single start symbol. -- -- Example: "Pinchot.Examples.SyntaxTrees". wrappedInstances :: [Rule t] -> T.DecsQ wrappedInstances = sequence . catMaybes . fmap singleWrappedInstance . families -- | Creates a 'Lens.Wrapped' instance for the 'Rule', if there is -- one. Only 'Pinchot.terminal', 'Pinchot.wrap', -- 'Pinchot.opt', 'Pinchot.star', and 'Pinchot.plus' -- get instances of 'Wrapped'. -- 'This must be spliced in the same module in which the -- syntax tree types are created. singleWrappedInstance :: Rule t -> Maybe (T.Q T.Dec) singleWrappedInstance (Rule nm _ ty) = case ty of Terminal _ -> Just $ wrappedTerminal nm Wrap (Rule inner _ _) -> Just $ wrappedWrap inner nm Opt (Rule inner _ _) -> Just $ wrappedOpt inner nm Star (Rule inner _ _) -> Just $ wrappedStar inner nm Plus (Rule inner _ _) -> Just $ wrappedPlus inner nm _ -> Nothing makeWrapped :: T.TypeQ -- ^ Name of wrapped type -> String -- ^ Name of wrapper type -> T.Q T.Dec makeWrapped wrappedType nm = T.instanceD (return []) typ decs where name = T.mkName nm typ = (T.conT ''Lens.Wrapped) `T.appT` ((T.conT name) `T.appT` (typeT) `T.appT` (typeA)) decs = [assocType, wrapper] where assocType = T.tySynInstD ''Lens.Unwrapped (T.tySynEqn [T.conT name `T.appT` (typeT) `T.appT` (typeA)] wrappedType) wrapper = T.funD 'Lens._Wrapped' [T.clause [] (T.normalB body) []] where body = (T.varE 'Lens.iso) `T.appE` unwrap `T.appE` doWrap where unwrap = do local <- T.newName "_local" let lambPat = T.conP name [T.varP local] T.lamE [lambPat] (T.varE local) doWrap = do local <- T.newName "_local" let expn = (T.conE name) `T.appE` (T.varE local) lambPat = T.varP local T.lamE [lambPat] expn wrappedOpt :: String -- ^ Wrapped rule name -> String -- ^ Wrapping Rule name -> T.Q T.Dec wrappedOpt wrappedName = makeWrapped maybeName where maybeName = (T.conT ''Maybe) `T.appT` ((T.conT (T.mkName wrappedName)) `T.appT` (typeT) `T.appT` (typeA)) wrappedTerminal :: String -- ^ Wrapper Rule name -> T.Q T.Dec wrappedTerminal = makeWrapped [t| ( $(typeT), $(typeA) ) |] wrappedTerminals :: String -- ^ Wrapper Rule name -> T.Q T.Dec wrappedTerminals = makeWrapped [t| [ ($(typeT), $(typeA)) ] |] wrappedStar :: String -- ^ Wrapped rule name -> String -- ^ Wrapping Rule name -> T.Q T.Dec wrappedStar wrappedName = makeWrapped innerName where innerName = [t| [ $(T.conT (T.mkName wrappedName)) $(typeT) $(typeA) ] |] wrappedPlus :: String -- ^ Wrapped rule name -> String -- ^ Wrapping Rule name -> T.Q T.Dec wrappedPlus wrappedName = makeWrapped tupName where tupName = [t| NonEmpty ( $(T.conT (T.mkName wrappedName)) $(typeT) $(typeA)) |] wrappedWrap :: String -- ^ Wrapped rule name -> String -- ^ Wrapping Rule name -> T.Q T.Dec wrappedWrap wrappedName = makeWrapped innerName where innerName = ((T.conT (T.mkName wrappedName)) `T.appT` (typeT) `T.appT` (typeA))