packages feed

hic-0.0.0.1: src/Language/Cimple/Hic/Ast.hs

{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE DeriveTraversable     #-}
{-# LANGUAGE DerivingVia           #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE Strict                #-}
{-# LANGUAGE TemplateHaskell       #-}
module Language.Cimple.Hic.Ast
    ( Node, NodeF (..)
    , HicNode (..)
    , TaggedUnionMember (..)
    , MatchCase (..)
    , CleanupAction (..)
    , ReturnIntent (..)
    ) where

import           Data.Aeson                   (FromJSON, FromJSON1, ToJSON,
                                               ToJSON1)
import           Data.Aeson.TH                (defaultOptions, deriveJSON1)
import           Data.Bifunctor               (Bifunctor (..))
import           Data.Fix                     (Fix (..), foldFix)
import           Data.Foldable                (fold)
import           Data.Functor.Classes         (Eq1, Ord1, Read1, Show1)
import           Data.Functor.Classes.Generic (FunctorClassesDefault (..))
import           Data.Hashable                (Hashable (..))
import           Data.Hashable.Lifted         (Hashable1)
import           Data.Text                    (Text)
import qualified Data.Text                    as Text
import           GHC.Generics                 (Generic, Generic1)
import qualified Language.Cimple              as C

-- | The High-level Cimple (Hic) AST.
-- It wraps the base Cimple AST and adds a 'HicNode' constructor for lifted constructs.
data NodeF lexeme a
    = CimpleNode (C.NodeF lexeme a)
    | HicNode (HicNode lexeme a)
    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault (NodeF lexeme)

instance Bifunctor NodeF where
    bimap f g (CimpleNode cn) = CimpleNode (bimap f g cn)
    bimap f g (HicNode hn)    = HicNode (bimap f g hn)

type Node lexeme = Fix (NodeF lexeme)

instance C.Concats (NodeF lexeme [lexeme]) lexeme where
    concats (CimpleNode f) = C.concats f
    concats (HicNode h)    = C.concats h

instance C.Concats (HicNode lexeme [lexeme]) lexeme where
    concats (Scoped r b c)    = r ++ b ++ concatMap C.concats c
    concats (Raise o v r)     = fold o ++ v ++ C.concats r
    concats (Transition f t)  = f ++ t
    concats (TaggedUnion n tt tf ut uf m) =
        [n] ++ tt ++ [tf] ++ ut ++ [uf] ++ concatMap C.concats m
    concats (TaggedUnionGet _ p o _isPtr tf tv uf m e) = p ++ o ++ [tf] ++ tv ++ [uf] ++ [m] ++ e
    concats (Match o _ tf c d) = o ++ [tf] ++ concatMap C.concats c ++ fold d
    concats (TaggedUnionMemberAccess o uf m) = o ++ [uf] ++ [m]
    concats (TaggedUnionGetTag _ p o _isPtr tf) = p ++ o ++ [tf]
    concats (TaggedUnionConstruct o _isPtr ty tf tv uf m d) = o ++ [ty] ++ [tf] ++ tv ++ [uf] ++ [m] ++ d
    concats (ForEach is in_ c s cons b _hi) = is ++ in_ ++ c ++ s ++ concat cons ++ b
    concats (Find i in_ c s con p f m) = [i] ++ in_ ++ c ++ s ++ con ++ p ++ f ++ fold m
    concats (IterationElement i c) = i : c
    concats (IterationIndex i) = [i]

instance C.Concats (TaggedUnionMember lexeme [lexeme]) lexeme where
    concats (TaggedUnionMember e m t) = [e, m] ++ t

instance C.Concats (MatchCase lexeme [lexeme]) lexeme where
    concats (MatchCase v b) = v ++ b

instance C.Concats (CleanupAction [lexeme]) lexeme where
    concats (CleanupAction l b) = fold l ++ b

instance C.Concats (ReturnIntent [lexeme]) lexeme where
    concats (ReturnValue v) = v
    concats (ReturnError e) = e
    concats ReturnVoid      = []

instance C.HasLocation lexeme => C.HasLocation (Node lexeme) where
    sloc file (n :: Node lexeme) =
        case foldFix (C.concats :: NodeF lexeme [lexeme] -> [lexeme]) n of
            []  -> Text.pack file <> ":0:0"
            l:_ -> C.sloc file (l :: lexeme)

-- | Generic high-level language constructs inferred from C.
data HicNode lexeme a
    -- | A scoped block with mandatory cleanup.
    -- Inferred from: { resource = alloc(); ... if (err) goto CLEANUP; ... CLEANUP: free(resource); }
    = Scoped
        { scopedResource :: a
        , scopedBody     :: a
        , scopedCleanup  :: [CleanupAction a]
        }

    -- | Explicit error propagation.
    | Raise
        { raiseOutParam :: Maybe a
        , raiseValue    :: a
        , raiseReturn   :: ReturnIntent a
        }

    -- | A structured protocol/state-machine transition.
    | Transition
        { transitionFrom :: a
        , transitionTo   :: a
        }

    -- | A tagged union (sum type).
    -- Inferred from: struct { Enum tag; union { ... } data; }
    | TaggedUnion
        { tuName       :: lexeme
        , tuTagType    :: a
        , tuTagField   :: lexeme
        , tuUnionType  :: a
        , tuUnionField :: lexeme
        , tuMembers    :: [TaggedUnionMember lexeme a]
        }

    -- | A type-safe getter for a tagged union member.
    -- Inferred from: Member* get(TaggedUnion *u) { return u->tag == VAL ? u->data.member : NULL; }
    | TaggedUnionGet
        { tugScope      :: C.Scope
        , tugProto      :: a
        , tugObject     :: a
        , tugIsPointer  :: Bool
        , tugTagField   :: lexeme
        , tugTagValue   :: a
        , tugUnionField :: lexeme
        , tugMember     :: lexeme
        , tugElse       :: a
        }

    -- | A pattern match over a tagged union.
    -- Inferred from: switch (u->tag) { case VAL: ... u->data.member ... }
    | Match
        { matchObject   :: a
        , matchIsPtr    :: Bool
        , matchTagField :: lexeme
        , matchCases    :: [MatchCase lexeme a]
        , matchDefault  :: Maybe a
        }

    -- | A high-level access to a member of a tagged union.
    -- Inferred from: u->data.member
    | TaggedUnionMemberAccess
        { tumaObject     :: a
        , tumaUnionField :: lexeme
        , tumaMember     :: lexeme
        }

    -- | Safe access to the tag of a tagged union.
    | TaggedUnionGetTag
        { tugtScope     :: C.Scope
        , tugtProto     :: a
        , tugtObject    :: a
        , tugtIsPointer :: Bool
        , tugtTagField  :: lexeme
        }

    -- | Atomic construction of a tagged union.
    -- Inferred from: *u = (TaggedUnion) { tag, data };
    -- Or coalesced from sequential assignments: u.tag = val; u.data.mem = val;
    | TaggedUnionConstruct
        { tucObject     :: a
        , tucIsPointer  :: Bool
        , tucType       :: lexeme
        , tucTagField   :: lexeme
        , tucTagValue   :: a
        , tucUnionField :: lexeme
        , tucMember     :: lexeme
        , tucDataValue  :: a
        }

    -- | A high-level iteration over one or more collections (zipped).
    -- Inferred from: for (init; cond; step) { ... c1[i] ... c2[i] ... }
    | ForEach
        { feIterators  :: [lexeme]
        , feInit       :: a
        , feCond       :: a
        , feStep       :: a
        , feContainers :: [a]
        , feBody       :: a
        , feHasIndex   :: Bool
        }

    -- | A high-level search operation.
    -- Inferred from: for (init; cond; step) { if (pred) foundAction; } missingAction;
    | Find
        { fIterator  :: lexeme
        , fInit      :: a
        , fCond      :: a
        , fStep      :: a
        , fContainer :: a
        , fPredicate :: a
        , fOnFound   :: a
        , fOnMissing :: Maybe a
        }

    -- | A high-level access to the current element in an iteration.
    | IterationElement
        { ieIterator  :: lexeme
        , ieContainer :: a
        }

    -- | A high-level access to the current index in an iteration.
    | IterationIndex
        { iiIterator :: lexeme
        }

    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault (HicNode lexeme)

instance Bifunctor HicNode where
    bimap _ g (Scoped r b c) = Scoped (g r) (g b) (map (fmap g) c)
    bimap _ g (Raise o v r) = Raise (fmap g o) (g v) (fmap g r)
    bimap _ g (Transition fr to) = Transition (g fr) (g to)
    bimap f g (TaggedUnion n tt tf ut uf m) =
        TaggedUnion (f n) (g tt) (f tf) (g ut) (f uf) (map (bimap f g) m)
    bimap f g (TaggedUnionGet sc p o isPtr tf tv uf m e) =
        TaggedUnionGet sc (g p) (g o) isPtr (f tf) (g tv) (f uf) (f m) (g e)
    bimap f g (Match o isPtr tf c d) = Match (g o) isPtr (f tf) (map (bimap f g) c) (fmap g d)
    bimap f g (TaggedUnionMemberAccess o uf m) = TaggedUnionMemberAccess (g o) (f uf) (f m)
    bimap f g (TaggedUnionGetTag sc p o isPtr tf) = TaggedUnionGetTag sc (g p) (g o) isPtr (f tf)
    bimap f g (TaggedUnionConstruct o isPtr ty tf tv uf m d) =
        TaggedUnionConstruct (g o) isPtr (f ty) (f tf) (g tv) (f uf) (f m) (g d)
    bimap f g (ForEach is in_ c s cons b hi) = ForEach (map f is) (g in_) (g c) (g s) (map g cons) (g b) hi
    bimap f g (Find i in_ c s con p found missing) = Find (f i) (g in_) (g c) (g s) (g con) (g p) (g found) (fmap g missing)
    bimap f g (IterationElement i c) = IterationElement (f i) (g c)
    bimap f _ (IterationIndex i) = IterationIndex (f i)

data TaggedUnionMember lexeme a = TaggedUnionMember
    { tumEnumVal :: lexeme
    , tumMember  :: lexeme
    , tumType    :: a
    }
    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault (TaggedUnionMember lexeme)

instance Bifunctor TaggedUnionMember where
    bimap f g (TaggedUnionMember e m t) = TaggedUnionMember (f e) (f m) (g t)

data MatchCase lexeme a = MatchCase
    { mcValue :: a
    , mcBody  :: a
    }
    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault (MatchCase lexeme)

instance Bifunctor MatchCase where
    bimap _ g (MatchCase v b) = MatchCase (g v) (g b)

data CleanupAction a
    = CleanupAction
        { cleanupLabel :: Maybe a
        , cleanupBody  :: a
        }
    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault CleanupAction

data ReturnIntent a
    = ReturnVoid
    | ReturnValue a
    | ReturnError a -- The "sentinel" return value like -1 or nullptr
    deriving (Show, Read, Eq, Ord, Generic, Generic1, Functor, Foldable, Traversable)
    deriving (Show1, Read1, Eq1, Ord1) via FunctorClassesDefault ReturnIntent

instance (Hashable lexeme, Hashable a) => Hashable (NodeF lexeme a)
instance (Hashable lexeme, Hashable a) => Hashable (HicNode lexeme a)
instance (Hashable lexeme, Hashable a) => Hashable (TaggedUnionMember lexeme a)
instance (Hashable lexeme, Hashable a) => Hashable (MatchCase lexeme a)
instance Hashable a => Hashable (CleanupAction a)
instance Hashable a => Hashable (ReturnIntent a)

instance Hashable lexeme => Hashable1 (NodeF lexeme)
instance Hashable lexeme => Hashable1 (HicNode lexeme)
instance Hashable lexeme => Hashable1 (TaggedUnionMember lexeme)
instance Hashable lexeme => Hashable1 (MatchCase lexeme)
instance Hashable1 CleanupAction
instance Hashable1 ReturnIntent

deriveJSON1 defaultOptions ''CleanupAction
deriveJSON1 defaultOptions ''ReturnIntent
deriveJSON1 defaultOptions ''MatchCase
deriveJSON1 defaultOptions ''TaggedUnionMember
deriveJSON1 defaultOptions ''HicNode
deriveJSON1 defaultOptions ''NodeF