packages feed

ede-0.3.4.0: lib/Text/EDE/Internal/AST.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}

-- |
-- Module      : Text.EDE.Internal.AST
-- Copyright   : (c) 2013-2022 Brendan Hay <brendan.g.hay@gmail.com>
-- License     : This Source Code Form is subject to the terms of
--               the Mozilla Public License, v. 2.0.
--               A copy of the MPL can be found in the LICENSE file or
--               you can obtain it at http://mozilla.org/MPL/2.0/.
-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--
-- /Warning/: this is an internal module, and does not have a stable
-- API or name. Functions in this module may not check or enforce
-- preconditions expected by public modules. Use at your own risk!
--
-- AST smart constructors.
module Text.EDE.Internal.AST where

import qualified Control.Comonad as Comonad
import Control.Comonad.Cofree (Cofree ((:<)))
import Data.Aeson.Types (Value (..))
import qualified Data.Foldable as Foldable
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Maybe as Maybe
import Text.EDE.Internal.Types

newtype Fix f = Fix (f (Fix f))

cofreeFix :: Functor f => a -> Fix f -> Cofree f a
cofreeFix x = go
  where
    go (Fix f) = x :< fmap go f
{-# INLINEABLE cofreeFix #-}

var :: Id -> Var
var = Var . (:| [])
{-# INLINEABLE var #-}

eapp :: a -> [Exp a] -> Exp a
eapp x [] = cofreeFix x blank
eapp _ [e] = e
eapp _ (e : es) = Foldable.foldl' (\x y -> Comonad.extract x :< EApp x y) e es
{-# INLINEABLE eapp #-}

efun :: Id -> Exp a -> Exp a
efun i e = let x = Comonad.extract e in x :< EApp (x :< EFun i) e
{-# INLINEABLE efun #-}

efilter :: Exp a -> (Id, [Exp a]) -> Exp a
efilter e (i, ps) = let x = Comonad.extract e in eapp x ((x :< EFun i) : e : ps)
{-# INLINEABLE efilter #-}

elet :: Maybe (Id, Exp a) -> Exp a -> Exp a
elet m e = maybe e (\(i, b) -> Comonad.extract b :< ELet i b e) m
{-# INLINEABLE elet #-}

eset :: Id -> Exp a -> Exp a -> Exp a
eset i e k =
  elet (Just (i, e)) k
{-# INLINEABLE eset #-}

eblock :: Id -> Exp a -> Exp a
eblock i e =
  Comonad.extract e :< EBlock i e
{-# INLINEABLE eblock #-}

eOverrideBlock :: Id -> Exp a -> Exp a -> Exp a
eOverrideBlock i b e =
  Comonad.extract b :< EOverrideBlock i b e
{-# INLINEABLE eOverrideBlock #-}

ecase ::
  Exp a ->
  [Alt (Exp a)] ->
  Maybe (Exp a) ->
  Exp a
ecase p ws f = Comonad.extract p :< ECase p (ws ++ maybe [] ((: []) . wild) f)
{-# INLINEABLE ecase #-}

eif ::
  (Exp a, Exp a) ->
  [(Exp a, Exp a)] ->
  Maybe (Exp a) ->
  Exp a
eif t ts f =
  Foldable.foldr'
    c
    (Maybe.fromMaybe (Comonad.extract (fst t) `cofreeFix` blank) f)
    (t : ts)
  where
    c (p, w) e = Comonad.extract p :< ECase p [true w, false e]
{-# INLINEABLE eif #-}

eempty :: Exp a -> Exp a -> Maybe (Exp a) -> Exp a
eempty v e = maybe e (eif (efun "!" (efun "empty" v), e) [] . Just)
{-# INLINEABLE eempty #-}

true, false, wild :: Exp a -> Alt (Exp a)
true = (PLit (Bool True),)
false = (PLit (Bool False),)
wild = (PWild,)
{-# INLINEABLE true #-}
{-# INLINEABLE false #-}
{-# INLINEABLE wild #-}

blank :: Fix ExpF
blank = Fix (ELit (String mempty))
{-# INLINEABLE blank #-}