270 lines
10 KiB
Haskell
270 lines
10 KiB
Haskell
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE DeriveTraversable #-}
|
|
{-# LANGUAGE DeriveFoldable #-}
|
|
{-# LANGUAGE DeriveGeneric #-}
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
|
{-# LANGUAGE DeriveFunctor #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE LambdaCase #-}
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
-- | The nix expression type and supporting types.
|
|
module Nix.Expr.Types where
|
|
|
|
import Data.Data
|
|
import Data.Eq.Deriving
|
|
import Data.Fix
|
|
import Data.Functor.Classes
|
|
import Data.Map (Map, toList)
|
|
import Data.Text (Text, pack)
|
|
import Data.Traversable
|
|
import GHC.Exts
|
|
import GHC.Generics
|
|
import Nix.Atoms
|
|
import Text.Show.Deriving
|
|
|
|
-- | 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
|
|
-- ^ Constants: ints, bools, URIs, and null.
|
|
| NStr !(NString r)
|
|
-- ^ A string, with interpolated expressions.
|
|
| NSym !Text
|
|
-- ^ A variable. For example, in the expression @f a@, @f@ is represented
|
|
-- as @NSym "f"@ and @a@ as @NSym "a"@.
|
|
| NList ![r]
|
|
-- ^ A list literal.
|
|
| NSet ![Binding r]
|
|
-- ^ An attribute set literal, not recursive.
|
|
| NRecSet ![Binding r]
|
|
-- ^ An attribute set literal, recursive.
|
|
| NLiteralPath !FilePath
|
|
-- ^ 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
|
|
-- ^ A path which refers to something in the Nix search path (the NIX_PATH
|
|
-- environment variable. For example, @<nixpkgs/pkgs>@.
|
|
| NUnary !NUnaryOp !r
|
|
-- ^ Application of a unary operator to an expression.
|
|
| NBinary !NBinaryOp !r !r
|
|
-- ^ Application of a binary operator to two expressions.
|
|
| NSelect !r !(NAttrPath r) !(Maybe r)
|
|
-- ^ Dot-reference into an attribute set, optionally providing an
|
|
-- alternative if the key doesn't exist.
|
|
| NHasAttr !r !(NAttrPath r)
|
|
-- ^ Ask if a set contains a given attribute path.
|
|
| NAbs !(Params r) !r
|
|
-- ^ A function literal (lambda abstraction).
|
|
| NApp !r !r
|
|
-- ^ Apply a function to an argument.
|
|
| NLet ![Binding r] !r
|
|
-- ^ Evaluate the second argument after introducing the bindings.
|
|
| NIf !r !r !r
|
|
-- ^ If-then-else statement.
|
|
| NWith !r !r
|
|
-- ^ Evaluate an attribute set, bring its bindings into scope, and
|
|
-- evaluate the second argument.
|
|
| NAssert !r !r
|
|
-- ^ Assert that the first returns true before evaluating the second.
|
|
deriving (Ord, Eq, Generic, Typeable, Data, Functor,
|
|
Foldable, Traversable, Show)
|
|
|
|
-- | 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
|
|
|
|
|
|
-- | 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
|
|
-- ^ An explicit naming, such as @x = y@ or @x.y = z@.
|
|
| Inherit !(Maybe r) ![NKeyName r]
|
|
-- ^ 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;@.
|
|
deriving (Typeable, Data, Ord, Eq, Functor, Foldable, Traversable, Show)
|
|
|
|
-- | @Params@ represents all the ways the formal parameters to a
|
|
-- function can be represented.
|
|
data Params r
|
|
= Param !Text
|
|
-- ^ For functions with a single named argument, such as @x: x + 1@.
|
|
| ParamSet !(ParamSet r) !(Maybe Text)
|
|
-- ^ Explicit parameters (argument must be a set). Might specify a name
|
|
-- to bind to the set in the function body.
|
|
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show,
|
|
Foldable, Traversable)
|
|
|
|
instance IsString (Params r) where
|
|
fromString = Param . fromString
|
|
|
|
-- | An explicit parameter set; provides a shorthand for unpacking arguments.
|
|
data ParamSet r
|
|
= FixedParamSet !(Map Text (Maybe r))
|
|
-- ^ A fixed set, where no arguments beyond what is specified in the map
|
|
-- may be given. The map might contain defaults for arguments not passed.
|
|
| VariadicParamSet !(Map Text (Maybe r))
|
|
-- ^ Same as the 'FixedParamSet', but extra arguments are allowed.
|
|
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Show,
|
|
Foldable, Traversable)
|
|
|
|
instance Eq1 ParamSet where
|
|
liftEq eq (FixedParamSet a) (FixedParamSet b) =
|
|
liftEq (liftEq (liftEq eq)) (Data.Map.toList a) (Data.Map.toList b)
|
|
liftEq eq (VariadicParamSet a) (VariadicParamSet b) =
|
|
liftEq (liftEq (liftEq eq)) (Data.Map.toList a) (Data.Map.toList b)
|
|
liftEq _ _ _ = False
|
|
|
|
-- It's not possible to derive this automatically as there is no Show1 instance
|
|
-- for Map. We define one locally here.
|
|
instance Show1 ParamSet where
|
|
liftShowsPrec sp sl p =
|
|
let liftShowsPrecMap :: (Int -> a -> ShowS)
|
|
-> ([a] -> ShowS)
|
|
-> Int
|
|
-> Map Text a
|
|
-> ShowS
|
|
liftShowsPrecMap spMap slMap pMap m =
|
|
showsUnaryWith (liftShowsPrec (liftShowsPrec spMap slMap)
|
|
(liftShowList spMap slMap))
|
|
"fromList" pMap (Data.Map.toList m)
|
|
showNamedMap s =
|
|
showsUnaryWith (liftShowsPrecMap (liftShowsPrec sp sl)
|
|
(liftShowList sp sl))
|
|
s p
|
|
in \case
|
|
FixedParamSet m -> showNamedMap "FixedParamSet" m
|
|
VariadicParamSet m -> showNamedMap "VariadicParamSet" m
|
|
|
|
-- | 'Antiquoted' represents an expression that is either
|
|
-- antiquoted (surrounded by ${...}) or plain (not antiquoted).
|
|
data Antiquoted v r = Plain !v | Antiquoted !r
|
|
deriving (Ord, Eq, Generic, Typeable, Data, Functor, Foldable, Traversable, Show)
|
|
|
|
-- | 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]
|
|
-- ^ Strings wrapped with double-quotes (") are not allowed to contain
|
|
-- literal newline characters.
|
|
| Indented ![Antiquoted Text r]
|
|
-- ^ Strings wrapped with two single quotes ('') can contain newlines,
|
|
-- and their indentation will be stripped.
|
|
deriving (Eq, Ord, Generic, Typeable, Data, Functor, Foldable, Traversable, Show)
|
|
|
|
-- | 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 !Text
|
|
deriving (Eq, Ord, Generic, Typeable, Data, Show)
|
|
|
|
-- | Most key names are just static text, so this instance is convenient.
|
|
instance IsString (NKeyName r) where
|
|
fromString = StaticKey . 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 -> pure (StaticKey key)
|
|
|
|
-- | 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
|
|
deriving (Eq, Ord, Generic, Typeable, Data, Show)
|
|
|
|
-- | 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 (++)
|
|
deriving (Eq, Ord, Generic, Typeable, Data, Show)
|
|
|
|
-- | Get the name out of the parameter (there might be none).
|
|
paramName :: Params r -> Maybe Text
|
|
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)
|