{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
module Language.Ninja.AST.Expr
(
Expr (..)
, _Exprs, _Lit, _Var
, askVar, askExpr, addBind, addBinds
, normalizeExpr
, ExprConstraint
) where
import Control.Arrow (second)
import qualified Control.Lens as Lens
import Data.Foldable (asum)
import Data.Maybe (fromMaybe)
import Data.Monoid (Endo (Endo, appEndo), (<>))
import Flow ((.>), (|>))
import Data.Text (Text)
import qualified Data.Text as Text
import Control.DeepSeq (NFData)
import Data.Data (Data)
import Data.Hashable (Hashable)
import GHC.Generics (Generic)
import qualified Test.QuickCheck as QC
import qualified Test.SmallCheck.Series as SC
import GHC.Exts (Constraint)
import Data.Aeson ((.:), (.=))
import qualified Data.Aeson as Aeson
import qualified Language.Ninja.AST.Env as AST
import qualified Language.Ninja.Misc as Misc
data Expr ann
=
Exprs !ann ![Expr ann]
|
Lit !ann !Text
|
Var !ann !Text
deriving (Eq, Show, Generic, Data, Functor, Foldable, Traversable)
{-# INLINE _Exprs #-}
_Exprs :: Lens.Prism' (Expr ann) (ann, [Expr ann])
_Exprs = Lens.prism' (uncurry Exprs)
$ \case (Exprs ann es) -> Just (ann, es)
_ -> Nothing
{-# INLINE _Lit #-}
_Lit :: Lens.Prism' (Expr ann) (ann, Text)
_Lit = Lens.prism' (uncurry Lit)
$ \case (Lit ann text) -> Just (ann, text)
_ -> Nothing
{-# INLINE _Var #-}
_Var :: Lens.Prism' (Expr ann) (ann, Text)
_Var = Lens.prism' (uncurry Var)
$ \case (Var ann name) -> Just (ann, name)
_ -> Nothing
askExpr :: AST.Env Text Text -> Expr ann -> Text
askExpr e (Exprs _ xs) = Text.concat (map (askExpr e) xs)
askExpr _ (Lit _ x) = x
askExpr e (Var _ x) = askVar e x
askVar :: AST.Env Text Text -> Text -> Text
askVar e x = fromMaybe Text.empty (AST.askEnv e x)
addBind :: Text -> Expr ann -> AST.Env Text Text -> AST.Env Text Text
addBind k v e = AST.addEnv k (askExpr e v) e
addBinds :: [(Text, Expr ann)] -> AST.Env Text Text -> AST.Env Text Text
addBinds bs e = map (second (askExpr e) .> uncurry AST.addEnv .> Endo) bs
|> mconcat
|> (\endo -> appEndo endo e)
normalizeExpr :: forall ann. (Monoid ann) => Expr ann -> Expr ann
normalizeExpr = flatten .> removeEmpty .> combineAdj .> listToExpr
where
flatten :: Expr ann -> [Expr ann]
flatten (Exprs _ es) = concatMap flatten es
flatten owise = [owise]
removeEmpty :: [Expr ann] -> [Expr ann]
removeEmpty [] = []
removeEmpty (Lit _ "" : rest) = removeEmpty rest
removeEmpty (owise : rest) = owise : removeEmpty rest
combineAdj :: [Expr ann] -> [Expr ann]
combineAdj = (\case
[] -> []
(Lit annX x : Lit annY y : rest) -> (Lit (annX <> annY) (x <> y))
|> (\e -> combineAdj (e : rest))
(owise : rest) -> owise : combineAdj rest)
listToExpr :: [Expr ann] -> Expr ann
listToExpr [e] = e
listToExpr es = Exprs (mconcat (map (Lens.view Misc.annotation) es)) es
instance (Data ann) => Lens.Plated (Expr ann)
instance Misc.Annotated Expr where
annotation' f = Lens.lens (helper .> fst) (helper .> snd)
where
helper (Exprs ann es) = (ann, \x -> Exprs x (map (fmap f) es))
helper (Lit ann text) = (ann, \x -> Lit x text)
helper (Var ann name) = (ann, \x -> Var x name)
instance (Aeson.ToJSON ann) => Aeson.ToJSON (Expr ann) where
toJSON (Exprs ann es) = Aeson.object ["ann" .= ann, "exprs" .= es]
toJSON (Lit ann text) = Aeson.object ["ann" .= ann, "lit" .= text]
toJSON (Var ann name) = Aeson.object ["ann" .= ann, "var" .= name]
instance (Aeson.FromJSON ann) => Aeson.FromJSON (Expr ann) where
parseJSON = Aeson.withObject "Expr" $ \o -> do
ann <- o .: "ann"
asum [ Exprs ann <$> (o .: "exprs")
, Lit ann <$> (o .: "lit")
, Var ann <$> (o .: "var")
]
instance forall ann.
( QC.Arbitrary ann, ExprConstraint QC.Arbitrary ann
) => QC.Arbitrary (Expr ann) where
arbitrary = QC.sized go
where
go :: Int -> QC.Gen (Expr ann)
go n | n <= 0 = [ litG (QC.resize litLength QC.arbitrary)
, varG (QC.resize varLength QC.arbitrary)
] |> QC.oneof
go n = [ go 0
, do width <- (`mod` maxWidth) <$> QC.arbitrary
let subtree = go (n `div` lossRate)
Exprs <$> QC.arbitrary <*> QC.vectorOf width subtree
] |> QC.oneof
litG, varG :: QC.Gen Text -> QC.Gen (Expr ann)
litG g = Lit <$> QC.arbitrary <*> g
varG g = Var <$> QC.arbitrary <*> g
litLength, varLength, lossRate, maxWidth :: Int
litLength = 10
varLength = 10
maxWidth = 5
lossRate = 2
instance (Hashable ann) => Hashable (Expr ann)
instance (NFData ann) => NFData (Expr ann)
instance ( Monad m, ExprConstraint (SC.Serial m) ann
) => SC.Serial m (Expr ann)
instance ( Monad m, ExprConstraint (SC.CoSerial m) ann
) => SC.CoSerial m (Expr ann)
type ExprConstraint (c :: * -> Constraint) (ann :: *) = (c Text, c ann)