{-# LANGUAGE DeriveFunctor #-}

-- | This is a utility module that consolidates all `Context`-related operations

module Dhall.Context (
    -- * Context
      Context
    , empty
    , insert
    , match
    , lookup
    , toList
    ) where

import Data.Text (Text)
import Prelude   hiding (lookup)

{-| A @(Context a)@ associates `Data.Text.Text` labels with values of type @a@.
    Each `Data.Text.Text` label can correspond to multiple values of type @a@

    The `Context` is used for type-checking when @(a = Expr X)@

    * You create a `Context` using `empty` and `insert`
    * You transform a `Context` using `fmap`
    * You consume a `Context` using `lookup` and `toList`

    The difference between a `Context` and a `Data.Map.Map` is that a `Context`
    lets you have multiple ordered occurrences of the same key and you can
    query for the @n@th occurrence of a given key.
-}
newtype Context a = Context { forall a. Context a -> [(Text, a)]
getContext :: [(Text, a)] }
    deriving (forall a b. a -> Context b -> Context a
forall a b. (a -> b) -> Context a -> Context b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Context b -> Context a
$c<$ :: forall a b. a -> Context b -> Context a
fmap :: forall a b. (a -> b) -> Context a -> Context b
$cfmap :: forall a b. (a -> b) -> Context a -> Context b
Functor)

-- | An empty context with no key-value pairs
empty :: Context a
empty :: forall a. Context a
empty = forall a. [(Text, a)] -> Context a
Context []

-- | Add a key-value pair to the `Context`
insert :: Text -> a -> Context a -> Context a
insert :: forall a. Text -> a -> Context a -> Context a
insert Text
k a
v (Context [(Text, a)]
kvs) = forall a. [(Text, a)] -> Context a
Context ((Text
k, a
v) forall a. a -> [a] -> [a]
: [(Text, a)]
kvs)
{-# INLINABLE insert #-}

{-| \"Pattern match\" on a `Context`

> match (insert k v ctx) = Just (k, v, ctx)
> match  empty           = Nothing
-}
match :: Context a -> Maybe (Text, a, Context a)
match :: forall a. Context a -> Maybe (Text, a, Context a)
match (Context ((Text
k, a
v) : [(Text, a)]
kvs)) = forall a. a -> Maybe a
Just (Text
k, a
v, forall a. [(Text, a)] -> Context a
Context [(Text, a)]
kvs)
match (Context           []  ) = forall a. Maybe a
Nothing
{-# INLINABLE match #-}

{-| Look up a key by name and index

> lookup _ _         empty  = Nothing
> lookup k 0 (insert k v c) = Just v
> lookup k n (insert k v c) = lookup k (n - 1) c
> lookup k n (insert j v c) = lookup k  n      c  -- k /= j
-}
lookup :: Text -> Int -> Context a -> Maybe a
lookup :: forall a. Text -> Int -> Context a -> Maybe a
lookup Text
_ Int
_ (Context         []  ) =
    forall a. Maybe a
Nothing
lookup Text
x Int
n (Context ((Text
k, a
v):[(Text, a)]
kvs)) =
    if Text
x forall a. Eq a => a -> a -> Bool
== Text
k
    then if Int
n forall a. Eq a => a -> a -> Bool
== Int
0
         then forall a. a -> Maybe a
Just a
v
         else forall a. Text -> Int -> Context a -> Maybe a
lookup Text
x (Int
n forall a. Num a => a -> a -> a
- Int
1) (forall a. [(Text, a)] -> Context a
Context [(Text, a)]
kvs)
    else forall a. Text -> Int -> Context a -> Maybe a
lookup Text
x Int
n (forall a. [(Text, a)] -> Context a
Context [(Text, a)]
kvs)
{-# INLINABLE lookup #-}

{-| Return all key-value associations as a list

> toList           empty  = []
> toList (insert k v ctx) = (k, v) : toList ctx
-}
toList :: Context a -> [(Text, a)]
toList :: forall a. Context a -> [(Text, a)]
toList = forall a. Context a -> [(Text, a)]
getContext
{-# INLINABLE toList #-}