2018-05-10 10:14:13 +02:00
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
|
|
{-# LANGUAGE LambdaCase #-}
|
2018-04-07 21:02:50 +02:00
|
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
|
|
|
|
|
|
{-# OPTIONS_GHC -Wno-missing-fields #-}
|
|
|
|
|
|
|
|
module Nix.TH where
|
|
|
|
|
|
|
|
import Data.Fix
|
|
|
|
import Data.Generics.Aliases
|
2019-03-17 22:47:38 +01:00
|
|
|
import Data.Set ( Set
|
|
|
|
, (\\)
|
|
|
|
)
|
|
|
|
import qualified Data.Set as Set
|
|
|
|
import qualified Data.Text as Text
|
|
|
|
import Data.List.NonEmpty ( NonEmpty(..) )
|
|
|
|
import Data.Maybe ( mapMaybe )
|
2018-04-07 21:02:50 +02:00
|
|
|
import Language.Haskell.TH
|
|
|
|
import Language.Haskell.TH.Quote
|
2018-05-10 10:14:13 +02:00
|
|
|
import Nix.Atoms
|
2018-04-07 21:02:50 +02:00
|
|
|
import Nix.Expr
|
|
|
|
import Nix.Parser
|
|
|
|
|
|
|
|
quoteExprExp :: String -> ExpQ
|
|
|
|
quoteExprExp s = do
|
2019-03-17 22:47:38 +01:00
|
|
|
expr <- case parseNixText (Text.pack s) of
|
|
|
|
Failure err -> fail $ show err
|
2020-09-18 14:54:39 +02:00
|
|
|
Success e -> pure e
|
2019-03-17 22:47:38 +01:00
|
|
|
dataToExpQ (const Nothing `extQ` metaExp (freeVars expr)) expr
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2018-05-10 10:18:28 +02:00
|
|
|
quoteExprPat :: String -> PatQ
|
|
|
|
quoteExprPat s = do
|
2019-03-17 22:47:38 +01:00
|
|
|
expr <- case parseNixText (Text.pack s) of
|
|
|
|
Failure err -> fail $ show err
|
2020-09-18 14:54:39 +02:00
|
|
|
Success e -> pure e
|
2019-03-17 22:47:38 +01:00
|
|
|
dataToPatQ (const Nothing `extQ` metaPat (freeVars expr)) expr
|
2018-05-10 10:18:28 +02:00
|
|
|
|
2018-09-11 00:51:28 +02:00
|
|
|
freeVars :: NExpr -> Set VarName
|
|
|
|
freeVars e = case unFix e of
|
2019-03-17 22:47:38 +01:00
|
|
|
(NConstant _ ) -> Set.empty
|
|
|
|
(NStr string ) -> foldMap freeVars string
|
|
|
|
(NSym var ) -> Set.singleton var
|
|
|
|
(NList list ) -> foldMap freeVars list
|
2019-05-16 22:30:52 +02:00
|
|
|
(NSet NNonRecursive bindings) -> foldMap bindFree bindings
|
|
|
|
(NSet NRecursive bindings) -> foldMap bindFree bindings \\ foldMap bindDefs bindings
|
2019-03-17 22:47:38 +01:00
|
|
|
(NLiteralPath _ ) -> Set.empty
|
|
|
|
(NEnvPath _ ) -> Set.empty
|
|
|
|
(NUnary _ expr ) -> freeVars expr
|
|
|
|
(NBinary _ left right ) -> freeVars left `Set.union` freeVars right
|
|
|
|
(NSelect expr path orExpr) ->
|
|
|
|
freeVars expr
|
|
|
|
`Set.union` pathFree path
|
|
|
|
`Set.union` maybe Set.empty freeVars orExpr
|
|
|
|
(NHasAttr expr path) -> freeVars expr `Set.union` pathFree path
|
|
|
|
(NAbs (Param varname) expr) -> Set.delete varname (freeVars expr)
|
2018-09-11 00:51:28 +02:00
|
|
|
(NAbs (ParamSet set _ varname) expr) ->
|
|
|
|
-- Include all free variables from the expression and the default arguments
|
2019-03-17 22:47:38 +01:00
|
|
|
freeVars expr
|
|
|
|
`Set.union` Set.unions (mapMaybe (fmap freeVars . snd) set)
|
2018-09-11 00:51:28 +02:00
|
|
|
-- But remove the argument name if existing, and all arguments in the parameter set
|
2019-03-17 22:47:38 +01:00
|
|
|
\\ maybe Set.empty Set.singleton varname
|
|
|
|
\\ Set.fromList (map fst set)
|
|
|
|
(NLet bindings expr) ->
|
|
|
|
freeVars expr
|
|
|
|
`Set.union` foldMap bindFree bindings
|
|
|
|
\\ foldMap bindDefs bindings
|
|
|
|
(NIf cond th el) ->
|
|
|
|
freeVars cond `Set.union` freeVars th `Set.union` freeVars el
|
2018-09-11 00:51:28 +02:00
|
|
|
-- Evaluation is needed to find out whether x is a "real" free variable in `with y; x`, we just include it
|
|
|
|
-- This also makes sense because its value can be overridden by `x: with y; x`
|
2019-03-17 22:47:38 +01:00
|
|
|
(NWith set expr) -> freeVars set `Set.union` freeVars expr
|
2018-09-11 00:51:28 +02:00
|
|
|
(NAssert assertion expr) -> freeVars assertion `Set.union` freeVars expr
|
2019-03-17 22:47:38 +01:00
|
|
|
(NSynHole _ ) -> Set.empty
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
where
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
staticKey :: NKeyName r -> Maybe VarName
|
|
|
|
staticKey (StaticKey varname) = Just varname
|
|
|
|
staticKey (DynamicKey _ ) = Nothing
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
bindDefs :: Binding r -> Set VarName
|
|
|
|
bindDefs (Inherit Nothing _ _) = Set.empty
|
|
|
|
bindDefs (Inherit (Just _) keys _) = Set.fromList $ mapMaybe staticKey keys
|
|
|
|
bindDefs (NamedVar (StaticKey varname :| _) _ _) = Set.singleton varname
|
|
|
|
bindDefs (NamedVar (DynamicKey _ :| _) _ _) = Set.empty
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
bindFree :: Binding NExpr -> Set VarName
|
|
|
|
bindFree (Inherit Nothing keys _) = Set.fromList $ mapMaybe staticKey keys
|
|
|
|
bindFree (Inherit (Just scope) _ _) = freeVars scope
|
|
|
|
bindFree (NamedVar path expr _) = pathFree path `Set.union` freeVars expr
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2019-03-17 22:47:38 +01:00
|
|
|
pathFree :: NAttrPath NExpr -> Set VarName
|
|
|
|
pathFree = foldMap (foldMap freeVars)
|
2018-09-11 00:51:28 +02:00
|
|
|
|
2018-04-07 21:02:50 +02:00
|
|
|
|
2018-05-10 10:14:13 +02:00
|
|
|
class ToExpr a where
|
2018-05-10 10:18:28 +02:00
|
|
|
toExpr :: a -> NExprLoc
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
instance ToExpr NExprLoc where
|
2019-03-17 22:47:38 +01:00
|
|
|
toExpr = id
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
instance ToExpr VarName where
|
2019-03-17 22:47:38 +01:00
|
|
|
toExpr = Fix . NSym_ nullSpan
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
instance ToExpr Int where
|
2019-03-17 22:47:38 +01:00
|
|
|
toExpr = Fix . NConstant_ nullSpan . NInt . fromIntegral
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
instance ToExpr Integer where
|
2019-03-17 22:47:38 +01:00
|
|
|
toExpr = Fix . NConstant_ nullSpan . NInt
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
instance ToExpr Float where
|
2019-03-17 22:47:38 +01:00
|
|
|
toExpr = Fix . NConstant_ nullSpan . NFloat
|
2018-05-10 10:14:13 +02:00
|
|
|
|
|
|
|
metaExp :: Set VarName -> NExprLoc -> Maybe ExpQ
|
|
|
|
metaExp fvs (Fix (NSym_ _ x)) | x `Set.member` fvs =
|
2019-03-17 22:47:38 +01:00
|
|
|
Just [| toExpr $(varE (mkName (Text.unpack x))) |]
|
2018-04-07 21:02:50 +02:00
|
|
|
metaExp _ _ = Nothing
|
|
|
|
|
2018-05-10 10:18:28 +02:00
|
|
|
metaPat :: Set VarName -> NExprLoc -> Maybe PatQ
|
|
|
|
metaPat fvs (Fix (NSym_ _ x)) | x `Set.member` fvs =
|
2019-03-17 22:47:38 +01:00
|
|
|
Just (varP (mkName (Text.unpack x)))
|
2018-05-10 10:18:28 +02:00
|
|
|
metaPat _ _ = Nothing
|
|
|
|
|
2018-04-07 21:02:50 +02:00
|
|
|
nix :: QuasiQuoter
|
2019-03-17 22:47:38 +01:00
|
|
|
nix = QuasiQuoter { quoteExp = quoteExprExp, quotePat = quoteExprPat }
|