purenix-1.0: src/PureNix/Expr.hs
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
-- | Nix expression types and auxiliary functions.
-- Since 'Expr' is actually a fixpoint over the 'ExprF' base functor, this
-- module also exposes auxiliary functions that serve as constructors.
module PureNix.Expr where
import Data.List.NonEmpty (NonEmpty (..))
import PureNix.Identifiers
import PureNix.Prelude
-- | The fixpoint over 'ExprF', see haddocks there for more information.
newtype Expr = Expr {unExpr :: ExprF Expr}
-- Explicitly using the @newtype@ strategy here makes the 'Show' instance
-- much nicer, since it makes it so you don't get all the @Expr@
-- constructors.
deriving newtype (Show)
-- | Base functor for a Nix expression.
-- We don't aim to be able to represent every valid Nix expression, just the
-- ones that are relevant for PureNix.
--
-- 'ExprF' is the base functor for the 'Expr' fixpoint.
-- This allows us to easily annotate and consume it during pretty-printing.
--
-- Note that 'String', unlike 'Key' and 'Var', is a raw representation of the intended string, completely unquoted and unescaped.
-- That means that it might consist of, for example, a single '"'.
-- It is the job of the printer to figure out how to correctly escape those.
data ExprF f
= Var Var
| Lam Var f
| App f f
| Attrs [Var] [(f, [Key])] [(Key, f)]
| Cond f f f
| List [f]
| Bin Op f f
| Not f
| Sel f Key
| Let (NonEmpty (Var, f)) f
| Int Integer
| Double Double
| String Text
| Path Text
deriving stock (Functor, Foldable, Traversable, Show)
data Op = Update | Equals | And
deriving (Eq, Show)
foldExpr :: (ExprF r -> r) -> Expr -> r
foldExpr f = go where go = f . fmap go . unExpr
var :: Var -> Expr
var = Expr . Var
lam :: Var -> Expr -> Expr
lam arg body = Expr $ Lam arg body
app :: Expr -> Expr -> Expr
app f x = Expr $ App f x
cond :: Expr -> Expr -> Expr -> Expr
cond c true false = Expr $ Cond c true false
attrs ::
[Var] ->
[(Expr, [Key])] ->
[(Key, Expr)] ->
Expr
attrs inherits inheritFroms binds = Expr $ Attrs inherits inheritFroms binds
sel :: Expr -> Key -> Expr
sel e s = Expr $ Sel e s
let' :: [(Var, Expr)] -> Expr -> Expr
let' [] body = body
let' (h : t) body = Expr $ Let (h :| t) body
int :: Integer -> Expr
int = Expr . Int
double :: Double -> Expr
double = Expr . Double
string :: Text -> Expr
string = Expr . String
list :: [Expr] -> Expr
list = Expr . List
bin :: Op -> Expr -> Expr -> Expr
bin op a b = Expr $ Bin op a b
path :: Text -> Expr
path = Expr . Path
constructorFieldNames :: [Var]
constructorFieldNames = numberedVars "__field"
not' :: Expr -> Expr
not' = Expr . Not
-- | Convenience constructor for builtins.
-- Takes a Key, and gives you @builtins.key@
builtin :: Key -> Expr
builtin = sel (var "builtins")
-- Just
-- becomes
-- (a: { __tag = "Just"; __field0 = a; })
constructor :: Text -> [Var] -> Expr
constructor conName fields =
foldr
lam
( attrs
[]
[]
(("__tag", string conName) : zipWith (\arg (UnsafeVar name) -> (UnsafeKey name, var arg)) fields constructorFieldNames)
)
fields