packages feed

fir-0.1.0.0: src/FIR/AST.hs

{-# LANGUAGE AllowAmbiguousTypes      #-}
{-# LANGUAGE DataKinds                #-}
{-# LANGUAGE DeriveAnyClass           #-}
{-# LANGUAGE DeriveGeneric            #-}
{-# LANGUAGE DerivingVia              #-}
{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances        #-}
{-# LANGUAGE GADTs                    #-}
{-# LANGUAGE InstanceSigs             #-}
{-# LANGUAGE MultiParamTypeClasses    #-}
{-# LANGUAGE PatternSynonyms          #-}
{-# LANGUAGE PolyKinds                #-}
{-# LANGUAGE ScopedTypeVariables      #-}
{-# LANGUAGE StandaloneDeriving       #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications         #-}
{-# LANGUAGE TypeFamilies             #-}
{-# LANGUAGE TypeOperators            #-}
{-# LANGUAGE UndecidableInstances     #-}

{-# OPTIONS_GHC -O0 #-}

{-|
Module: FIR.AST

Representation of programs using abstract syntax trees.

The user interface to the AST is through type class overloading,
see "FIR.Syntax.AST" and "FIR.Syntax.Program".

This is an extensible AST represented by an open sum type (variant type),
with a higher-order abstract syntax representation (HOAS),
as seen in the paper /Combining Deep and Shallow Embeddings of Domain-Specific Languages/
by Josef Svenningsson and Emil Axelsson.
-}

module FIR.AST
  ( -- * Main AST data type
    AST, Code
    -- * Syntactic type class
  , Syntactic(Internal, toAST, fromAST)
  , SyntacticVal, InternalType
    -- * Primitive operations
  , primOp
  , HasUndefined(undefined)
    -- * Displaying ASTs graphically
  , toTree

    -- * Re-exports
  , module FIR.AST.ControlFlow
  , module FIR.AST.Effs
  , module FIR.AST.Exts
  , module FIR.AST.Images
  , module FIR.AST.Optics
  , module FIR.AST.Prim

  )
  where

-- base
import Prelude
  hiding ( undefined )
import qualified Prelude
import Data.Typeable
import Data.Kind
  ( Type, Constraint )

-- tree-view
import Data.Tree.View
  ( showTree )

-- variant
import Data.Variant.EGADT
  ( EGADT )

-- fir
import FIR.AST.ControlFlow
import FIR.AST.Display
  ( toTree )
import FIR.AST.Effs
import FIR.AST.Exts
import FIR.AST.Images
import FIR.AST.Optics
import FIR.AST.Prim
import FIR.AST.Type

import FIR.Prim.Op
  ( PrimOp(PrimOpAugType) )
import {-# SOURCE #-} FIR.Prim.Types
  ( PrimTy )

------------------------------------------------------------
-- main AST data type: extensible GADT

type AllOpsF
  = '[ LamF, AppF, LitF, MkIDF, ValueF, UnsafeCoerceF
     , IxMonadF
     , PrimOpF, UndefinedF, GradedMappendF
     , ApplicativeF
     , MkVectorF, MatF, StructF, ArrayF, ArrayLengthF, HListF
     , SelectionF, LoopF
     , LetF, DefF, FunDefF, FunCallF, DefEntryPointF, StateF
     , DebugPrintfF, RayF
     , ImgOpsF, ImgQueryF
     , OpticF
     ]

type AST    = EGADT AllOpsF
type Code a = AST (Val a)

------------------------------------------------

-- | Syntactic type class (Axelsson, Svenningsson).
class Syntactic a where
  type Internal a :: AugType
  toAST :: a -> AST (Internal a)
  fromAST :: AST (Internal a) -> a

type family SyntacticVal a :: Constraint where
  SyntacticVal a = ( Syntactic a, Internal a ~ Val (InternalType a) )
type InternalType a = UnderlyingType (Internal a)

instance Syntactic (AST a) where
  type Internal (AST a) = a
  toAST   = id
  fromAST = id

instance (SyntacticVal a, Syntactic b) => Syntactic (a -> b) where
  type Internal (a -> b) = Internal a :--> Internal b
  toAST        f    = Lam @( Val (InternalType a) :--> Internal b )
                      ( toAST . f . fromAST )
  fromAST (Lam f) a = fromAST ( f  $ toAST a )
  fromAST      f  a = fromAST ( f :$ toAST a )

------------------------------------------------

-- | Utility function for defining primops.
primOp :: forall a op r
       .  ( PrimOp op a
          , Syntactic r
          , Internal r ~ PrimOpAugType op a
          )
       => r
primOp = fromAST $ PrimOp ( Proxy @a ) ( Proxy @op )

-- | Types at which we can define "undefined."
class HasUndefined (a :: Type) where
  undefined :: a

instance PrimTy a => HasUndefined (Code a) where
  undefined = Undefined

instance {-# OVERLAPPABLE #-} HasUndefined a where
  undefined = Prelude.undefined

------------------------------------------------
-- display AST for viewing

instance Show (AST ast) where
  show = showTree . toTree