hnix/Nix/Builtins.hs
2018-03-27 22:21:17 -07:00

123 lines
3.6 KiB
Haskell

module Nix.Builtins (baseEnv, builtins, evalTopLevelExpr) where
import Data.Fix
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as Text
import Nix.Atoms
import Nix.Eval
import Nix.Expr (NExpr)
import Nix.Parser
import System.IO.Unsafe
-- | Evaluate a nix expression in the default context
evalTopLevelExpr :: NExpr -> NValue
evalTopLevelExpr val = evalExpr val baseEnv
baseEnv :: ValueSet
baseEnv =
Map.fromList $ [ ("builtins", Fix $ NVSet builtins) ] ++ topLevelBuiltins
where
topLevelBuiltins = map mapping $ filter isTopLevel builtinsList
-- builtins = Map.fromList $ map mapping $ builtinsList
builtins :: ValueSet
builtins = Map.fromList $ map mapping $ builtinsList
data BuiltinType = Normal | TopLevel
data Builtin = Builtin
{ kind :: BuiltinType
, mapping :: (Text, NValue)
}
isTopLevel :: Builtin -> Bool
isTopLevel b = case kind b of
Normal -> False
TopLevel -> True
builtinsList :: [ Builtin ]
builtinsList =
[ topLevel ("toString", prim_toString)
, topLevel ("import", prim_import)
, basic ("hasAttr" , prim_hasAttr)
, basic ("getAttr" , prim_getAttr)
, basic ("any" , prim_any )
, basic ("all" , prim_all )
]
where
basic = Builtin Normal
topLevel = Builtin TopLevel
-- Helpers
mkBool :: Bool -> NValue
mkBool = Fix . NVConstant . NBool
extractBool :: NValue -> Bool
extractBool (Fix (NVConstant (NBool b))) = b
extractBool _ = error "Not a bool constant"
evalPred :: NValue -> NValue -> NValue
evalPred (Fix (NVFunction params pred)) = pred . buildArgument params
evalPred pred = error $ "Trying to call a " ++ show pred
-- Primops
prim_toString :: NValue
prim_toString = builtin "toString" $ toString
toString :: NValue -> NValue
toString s = Fix $ uncurry NVStr $ valueText s
prim_import :: NValue
prim_import = builtin "import" $ import_
import_ :: NValue -> NValue
import_ (Fix (NVLiteralPath path)) = doImport path
import_ (Fix (NVEnvPath path)) = doImport path -- jww (2018-03-27): ?
import_ val = error $ "Invalid argument type for import: " ++ show val
-- Since the files we are importing can be considered immutable during parsing
-- and evaluation, this use of unsafePerformIO should be fine.
doImport :: FilePath -> NValue
doImport path = case unsafePerformIO (parseNixFile path) of
Failure err ->
error $ "Import of " ++ path ++ " failed: " ++ show err
Success expr -> evalExpr expr baseEnv
prim_hasAttr :: NValue
prim_hasAttr = builtin2 "hasAttr" hasAttr
hasAttr :: NValue -> NValue -> NValue
hasAttr (Fix (NVStr key _)) (Fix (NVSet aset)) =
Fix $ NVConstant $ NBool $ Map.member key aset
hasAttr key aset =
error $ "Invalid types for builtin.hasAttr: " ++ show (key, aset)
prim_getAttr :: NValue
prim_getAttr = builtin2 "getAttr" getAttr
getAttr :: NValue -> NValue -> NValue
getAttr (Fix (NVStr key _)) (Fix (NVSet aset)) =
Map.findWithDefault _err key aset
where
_err = error ("Field does not exist " ++ Text.unpack key)
getAttr key aset =
error $ "Invalid types for builtin.getAttr: " ++ show (key, aset)
prim_any :: NValue
prim_any = builtin2 "any" _any
_any :: NValue -> NValue -> NValue
_any pred (Fix (NVList l)) = mkBool . any extractBool $ map (evalPred pred) l
_any _ list =
error $ "builtins.any takes a list as second argument, not a " ++ show list
prim_all :: NValue
prim_all = builtin2 "all" _all
_all :: NValue -> NValue -> NValue
_all pred (Fix (NVList l)) = mkBool . all extractBool $ map (evalPred pred) l
_all _ list =
error $ "builtins.all takes a list as second argument, not a " ++ show list