hnix/src/Nix/Expr/Types.hs

316 lines
12 KiB
Haskell
Raw Normal View History

2018-04-09 11:07:40 +02:00
{-# LANGUAGE BangPatterns #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE ConstraintKinds #-}
2018-04-09 11:07:40 +02:00
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE FlexibleContexts #-}
2018-04-07 21:02:50 +02:00
{-# LANGUAGE FlexibleInstances #-}
2018-04-08 00:34:54 +02:00
{-# LANGUAGE GADTs #-}
2018-04-09 11:07:40 +02:00
{-# LANGUAGE KindSignatures #-}
2018-04-07 21:02:50 +02:00
{-# LANGUAGE LambdaCase #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE MultiParamTypeClasses #-}
2018-04-09 11:07:40 +02:00
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
2018-04-07 21:02:50 +02:00
{-# LANGUAGE TemplateHaskell #-}
2018-04-08 00:34:54 +02:00
{-# LANGUAGE TypeApplications #-}
2018-04-09 11:07:40 +02:00
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-orphans #-}
2018-04-09 09:52:10 +02:00
2018-04-07 21:02:50 +02:00
-- | The nix expression type and supporting types.
module Nix.Expr.Types where
2018-04-09 11:07:40 +02:00
import Control.DeepSeq
import Data.Data
import Data.Eq.Deriving
import Data.Fix
import Data.Functor.Classes
import qualified Data.HashMap.Strict.InsOrd as InsOrd
import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
import Data.Text (Text, pack, unpack)
import Data.Traversable
import GHC.Exts
import GHC.Generics
import Language.Haskell.TH.Syntax
import Nix.Atoms
import Nix.Parser.Library (Delta(..))
import Nix.Utils
import Text.Show.Deriving
import Type.Reflection (eqTypeRep)
2018-04-08 00:34:54 +02:00
import qualified Type.Reflection as Reflection
2018-04-07 21:02:50 +02:00
type VarName = Text
-- | The main nix expression type. This is polymorphic so that it can be made
-- a functor, which allows us to traverse expressions and map functions over
-- them. The actual 'NExpr' type is a fixed point of this functor, defined
-- below.
data NExprF r
= NConstant NAtom
2018-04-07 21:02:50 +02:00
-- ^ Constants: ints, bools, URIs, and null.
| NStr (NString r)
2018-04-07 21:02:50 +02:00
-- ^ A string, with interpolated expressions.
| NSym VarName
2018-04-07 21:02:50 +02:00
-- ^ A variable. For example, in the expression @f a@, @f@ is represented
-- as @NSym "f"@ and @a@ as @NSym "a"@.
| NList [r]
2018-04-07 21:02:50 +02:00
-- ^ A list literal.
| NSet [Binding r]
2018-04-07 21:02:50 +02:00
-- ^ An attribute set literal, not recursive.
| NRecSet [Binding r]
2018-04-07 21:02:50 +02:00
-- ^ An attribute set literal, recursive.
| NLiteralPath FilePath
2018-04-07 21:02:50 +02:00
-- ^ A path expression, which is evaluated to a store path. The path here
-- can be relative, in which case it's evaluated relative to the file in
-- which it appears.
| NEnvPath FilePath
2018-04-07 21:02:50 +02:00
-- ^ A path which refers to something in the Nix search path (the NIX_PATH
-- environment variable. For example, @<nixpkgs/pkgs>@.
| NUnary NUnaryOp r
2018-04-07 21:02:50 +02:00
-- ^ Application of a unary operator to an expression.
| NBinary NBinaryOp r r
2018-04-07 21:02:50 +02:00
-- ^ Application of a binary operator to two expressions.
| NSelect r (NAttrPath r) (Maybe r)
2018-04-07 21:02:50 +02:00
-- ^ Dot-reference into an attribute set, optionally providing an
-- alternative if the key doesn't exist.
| NHasAttr r (NAttrPath r)
2018-04-07 21:02:50 +02:00
-- ^ Ask if a set contains a given attribute path.
| NAbs (Params r) r
2018-04-07 21:02:50 +02:00
-- ^ A function literal (lambda abstraction).
| NApp r r
2018-04-07 21:02:50 +02:00
-- ^ Apply a function to an argument.
| NLet [Binding r] r
2018-04-07 21:02:50 +02:00
-- ^ Evaluate the second argument after introducing the bindings.
| NIf r r r
2018-04-07 21:02:50 +02:00
-- ^ If-then-else statement.
| NWith r r
2018-04-07 21:02:50 +02:00
-- ^ Evaluate an attribute set, bring its bindings into scope, and
-- evaluate the second argument.
| NAssert r r
2018-04-07 21:02:50 +02:00
-- ^ Assert that the first returns true before evaluating the second.
2018-04-09 11:07:40 +02:00
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, NFData1)
2018-04-07 21:02:50 +02:00
-- | We make an `IsString` for expressions, where the string is interpreted
-- as an identifier. This is the most common use-case...
instance IsString NExpr where
fromString = Fix . NSym . fromString
2018-04-08 00:34:54 +02:00
instance Lift (Fix NExprF) where
lift = dataToExpQ $ \b ->
case Reflection.typeOf b `eqTypeRep` Reflection.typeRep @Text of
Just HRefl -> Just [| pack $(liftString $ unpack b) |]
Nothing -> Nothing
2018-04-07 21:02:50 +02:00
-- | The monomorphic expression type is a fixed point of the polymorphic one.
type NExpr = Fix NExprF
-- | A single line of the bindings section of a let expression or of a set.
data Binding r
= NamedVar (NAttrPath r) r
2018-04-07 21:02:50 +02:00
-- ^ An explicit naming, such as @x = y@ or @x.y = z@.
| Inherit (Maybe r) (NAttrPath r)
2018-04-07 21:02:50 +02:00
-- ^ Using a name already in scope, such as @inherit x;@ which is shorthand
-- for @x = x;@ or @inherit (x) y;@ which means @y = x.y;@.
2018-04-09 11:07:40 +02:00
deriving (Generic, Generic1, Typeable, Data, Ord, Eq, Functor,
Foldable, Traversable, Show, NFData, NFData1)
2018-04-07 21:02:50 +02:00
-- | @Params@ represents all the ways the formal parameters to a
-- function can be represented.
data Params r
= Param VarName
2018-04-07 21:02:50 +02:00
-- ^ For functions with a single named argument, such as @x: x + 1@.
| ParamSet (ParamSet r) Bool (Maybe VarName)
2018-04-07 21:02:50 +02:00
-- ^ Explicit parameters (argument must be a set). Might specify a name to
-- bind to the set in the function body. The bool indicates whether it is
-- variadic or not.
2018-04-09 11:07:40 +02:00
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, Show,
2018-04-07 21:02:50 +02:00
Foldable, Traversable)
2018-04-09 11:07:40 +02:00
instance NFData a => NFData (Params a) where
rnf (Param !_) = ()
rnf (ParamSet !s !_ !_) = InsOrd.size s `seq` ()
instance NFData1 Params where
liftRnf _ (Param !_) = ()
liftRnf _ (ParamSet !s !_ !_) = InsOrd.size s `seq` ()
2018-04-07 21:02:50 +02:00
-- This uses InsOrdHashMap because nix XML serialization preserves the order of
-- the param set.
type ParamSet r = InsOrdHashMap VarName (Maybe r)
instance IsString (Params r) where
fromString = Param . fromString
-- | 'Antiquoted' represents an expression that is either
-- antiquoted (surrounded by ${...}) or plain (not antiquoted).
2018-04-09 11:07:40 +02:00
data Antiquoted (v :: *) (r :: *) = Plain v | Antiquoted r
deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, NFData1)
2018-04-07 21:02:50 +02:00
-- | An 'NString' is a list of things that are either a plain string
-- or an antiquoted expression. After the antiquotes have been evaluated,
-- the final string is constructed by concating all the parts.
data NString r
= DoubleQuoted [Antiquoted Text r]
2018-04-07 21:02:50 +02:00
-- ^ Strings wrapped with double-quotes (") can contain literal newline
-- characters, but the newlines are preserved and no indentation is stripped.
| Indented [Antiquoted Text r]
2018-04-07 21:02:50 +02:00
-- ^ Strings wrapped with two single quotes ('') can contain newlines,
-- and their indentation will be stripped.
2018-04-09 11:07:40 +02:00
deriving (Eq, Ord, Generic, Generic1, Typeable, Data, Functor,
Foldable, Traversable, Show, NFData, NFData1)
2018-04-07 21:02:50 +02:00
-- | For the the 'IsString' instance, we use a plain doublequoted string.
instance IsString (NString r) where
fromString "" = DoubleQuoted []
fromString string = DoubleQuoted [Plain $ pack string]
-- | A 'KeyName' is something that can appear at the right side of an
-- equals sign. For example, @a@ is a 'KeyName' in @{ a = 3; }@, @let a = 3;
-- in ...@, @{}.a@ or @{} ? a@.
--
-- Nix supports both static keynames (just an identifier) and dynamic
-- identifiers. Dynamic identifiers can be either a string (e.g.:
-- @{ "a" = 3; }@) or an antiquotation (e.g.: @let a = "example";
-- in { ${a} = 3; }.example@).
--
-- Note: There are some places where a dynamic keyname is not allowed.
-- In particular, those include:
--
-- * The RHS of a @binding@ inside @let@: @let ${"a"} = 3; in ...@
-- produces a syntax error.
-- * The attribute names of an 'inherit': @inherit ${"a"};@ is forbidden.
--
-- Note: In Nix, a simple string without antiquotes such as @"foo"@ is
-- allowed even if the context requires a static keyname, but the
-- parser still considers it a 'DynamicKey' for simplicity.
data NKeyName r
= DynamicKey (Antiquoted (NString r) r)
| StaticKey VarName (Maybe Delta)
2018-04-09 11:07:40 +02:00
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
instance Generic1 NKeyName where
type Rep1 NKeyName = NKeyName -- jww (2018-04-09): wrong
from1 = id
to1 = id
instance NFData1 NKeyName where
liftRnf _ (StaticKey !_ !_) = ()
liftRnf _ (DynamicKey (Plain !_)) = ()
liftRnf k (DynamicKey (Antiquoted r)) = k r
instance NFData Delta
2018-04-07 21:02:50 +02:00
-- | Most key names are just static text, so this instance is convenient.
instance IsString (NKeyName r) where
fromString = flip StaticKey Nothing . fromString
instance Eq1 NKeyName where
liftEq eq (DynamicKey a) (DynamicKey b) = liftEq2 (liftEq eq) eq a b
liftEq _ (StaticKey a _) (StaticKey b _) = a == b
liftEq _ _ _ = False
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Show1 NKeyName where
liftShowsPrec sp sl p = \case
DynamicKey a -> showsUnaryWith (liftShowsPrec2 (liftShowsPrec sp sl) (liftShowList sp sl) sp sl) "DynamicKey" p a
StaticKey t _ -> showsUnaryWith showsPrec "StaticKey" p t
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Functor NKeyName where
fmap = fmapDefault
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Foldable NKeyName where
foldMap = foldMapDefault
-- Deriving this instance automatically is not possible because @r@
-- occurs not only as last argument in @Antiquoted (NString r) r@
instance Traversable NKeyName where
traverse f = \case
DynamicKey (Plain str) -> DynamicKey . Plain <$> traverse f str
DynamicKey (Antiquoted e) -> DynamicKey . Antiquoted <$> f e
StaticKey key pos -> pure (StaticKey key pos)
-- | A selector (for example in a @let@ or an attribute set) is made up
-- of strung-together key names.
type NAttrPath r = [NKeyName r]
-- | There are two unary operations: logical not and integer negation.
data NUnaryOp = NNeg | NNot
2018-04-09 11:07:40 +02:00
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
2018-04-07 21:02:50 +02:00
-- | Binary operators expressible in the nix language.
data NBinaryOp
= NEq -- ^ Equality (==)
| NNEq -- ^ Inequality (!=)
| NLt -- ^ Less than (<)
| NLte -- ^ Less than or equal (<=)
| NGt -- ^ Greater than (>)
| NGte -- ^ Greater than or equal (>=)
| NAnd -- ^ Logical and (&&)
| NOr -- ^ Logical or (||)
| NImpl -- ^ Logical implication (->)
| NUpdate -- ^ Joining two attribut sets (//)
| NPlus -- ^ Addition (+)
| NMinus -- ^ Subtraction (-)
| NMult -- ^ Multiplication (*)
| NDiv -- ^ Division (/)
| NConcat -- ^ List concatenation (++)
2018-04-09 11:07:40 +02:00
deriving (Eq, Ord, Generic, Typeable, Data, Show, NFData)
2018-04-07 21:02:50 +02:00
-- | Get the name out of the parameter (there might be none).
paramName :: Params r -> Maybe VarName
paramName (Param n) = Just n
paramName (ParamSet _ _ n) = n
$(deriveEq1 ''NExprF)
$(deriveEq1 ''NString)
$(deriveEq1 ''Binding)
$(deriveEq1 ''Params)
$(deriveEq1 ''Antiquoted)
$(deriveEq2 ''Antiquoted)
$(deriveShow1 ''NExprF)
$(deriveShow1 ''NString)
$(deriveShow1 ''Params)
$(deriveShow1 ''Binding)
$(deriveShow1 ''Antiquoted)
$(deriveShow2 ''Antiquoted)
stripPositionInfo :: NExpr -> NExpr
stripPositionInfo = transport phi
where
phi (NSet binds) = NSet (map go binds)
phi (NRecSet binds) = NRecSet (map go binds)
phi (NLet binds body) = NLet (map go binds) body
phi (NSelect s attr alt) = NSelect s (map clear attr) alt
phi x = x
go (NamedVar path r) = NamedVar (map clear path) r
go (Inherit ms names) = Inherit ms (map clear names)
clear (StaticKey name _) = StaticKey name Nothing
clear k = k
2018-04-09 09:52:10 +02:00
class ConvertValue v a where
ofVal :: a -> v
wantVal :: v -> Maybe a
type Convertible v t =
(ConvertValue v Bool,
ConvertValue v Int,
ConvertValue v Integer,
ConvertValue v Float,
ConvertValue v Text,
ConvertValue v (Maybe Text), -- text or null
ConvertValue v [t],
ConvertValue v (AttrSet t, AttrSet Delta),
ConvertValue v (AttrSet t))