packages feed

jsonnet-0.4.0.0: src/Language/Jsonnet/Value.hs

-- |
-- Module                  : Language.Jsonnet.Value
-- Copyright               : (c) 2020-2021 Alexandre Moreno
-- SPDX-License-Identifier : BSD-3-Clause OR Apache-2.0
-- Maintainer              : Alexandre Moreno <alexmorenocano@gmail.com>
-- Stability               : experimental
-- Portability             : non-portable
module Language.Jsonnet.Value where

import Control.Lens (view)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.HashMap.Lazy (HashMap)
import Data.IORef
import Data.Map.Strict (Map)
import Data.Scientific
import Data.Text (Text)
import Data.Vector (Vector)
import GHC.Generics
import Language.Jsonnet.Common
import Language.Jsonnet.Core hiding (mkField)
import Language.Jsonnet.Eval.Monad
import Language.Jsonnet.Pretty ()
import Data.Aeson (FromJSON (..))
import qualified Data.Aeson as JSON
import qualified Data.Aeson.KeyMap as KeyMap
import qualified Data.HashMap.Lazy as H

type Eval = EvalM Value

type Env = Ctx Value

newtype ExtVars = ExtVars (Map Text Value)
  deriving newtype (Semigroup, Monoid)

data Value
  = VNull
  | VBool !Bool
  | VStr !Text
  | VNum !Scientific
  | VObj !Object -- !Object
  | VArr !(Vector Value)
  | VThunk !Core !Env
  | VIndir !Ref
  | VPrim !Prim
  | VClos !Lam !Env
  | VFun !Fun

instance FromJSON Value where
  parseJSON = \case
    JSON.Null -> pure VNull
    JSON.Bool b -> pure $ VBool b
    JSON.Number n -> pure $ VNum n
    JSON.String s -> pure $ VStr s
    JSON.Array a -> VArr <$> traverse parseJSON a
    JSON.Object o -> VObj . f <$> traverse parseJSON (KeyMap.toHashMapText o)
    where
      f :: HashMap Text Value -> Object
      f o =
        H.fromList
          [ mkField k v
            | (k, v) <- H.toList o
          ]
      mkField k v = (k, VField (VStr k) v v Visible)

data VField = VField
  { -- |
    fieldKey :: Value,
    -- |
    fieldValWHNF :: Value,
    -- |
    fieldVal :: Value,
    -- |
    fieldVis :: Visibility
  }
  deriving (Generic)

type Fun = Value -> Eval Value

type Object = HashMap Text VField

data Cell = Cell {cellVal :: Value, cellIsWHNF :: Bool}
  deriving (Generic)

type Ref = IORef Cell

instance HasVisibility VField where
  visible VField {..} = fieldVis == Visible
  forced VField {..} = fieldVis == Forced
  hidden VField {..} = fieldVis == Hidden

class HasValue a where
  inj :: a -> Value
  proj :: Value -> Eval a

instance HasValue Value where
  inj = id
  proj = pure

mkCell :: Value -> Cell
mkCell v = Cell v False

mkIndirV :: MonadIO m => Value -> m Value
mkIndirV v = VIndir <$> allocate v

mkThunk :: Core -> Eval Value
mkThunk c = VThunk c <$> view ctx

allocate :: MonadIO m => Value -> m (IORef Cell)
allocate = liftIO . newIORef . mkCell