descript-lang-0.2.0.0: src/Descript/Sugar/Data/Value/Gen.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Descript.Sugar.Data.Value.Gen
( GenProperty (..)
, GenRecord (..)
, GenValue (..)
, PartProperty
, PartRecord
, GenPropVal (..)
, GenPart (..)
, singletonValue
, isEmpty
, primParts
, mapParts
, foldMapParts
, traverseParts
, recWithHead
, forceRecWithHead
, deleteRecWithHead
, partHasHead
, recHasHead
, mapPropVals
, foldMapPropVals
, traversePropVals
) where
import Prelude hiding (head)
import Descript.Sugar.Data.Atom
import Descript.Misc
import Data.Monoid
import Data.Proxy
import Data.Maybe
import Data.List hiding (head)
-- | A property whose values are of type @v@.
data GenProperty v an
= Property
{ propertyAnn :: an
, propertyKey :: Maybe (Symbol an)
, propertyValue :: v an
} deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- | A record whose property values are of type @v@ - a record in a
-- value of type @v@.
--
-- Records are the main data of Descript. A record with no properties
-- encodes a singleton value or leaf. A record with properties encodes a
-- structure, or a tree where the properties are branches. Records are
-- similar to products, but their fields are named and they can be
-- differentiated by thier heads.
data GenRecord v an
= Record
{ recordAnn :: an
, head :: Symbol an
, properties :: [GenProperty v an]
} deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- | A value whose parts are of type 'p'.
data GenValue p an
= Value
{ valueAnn :: an
, valueParts :: [p an]
} deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable)
-- | A property for a particular part.
type PartProperty p an = GenProperty (PartPropVal p) an
-- | A record for a particular part.
type PartRecord p an = GenRecord (PartPropVal p) an
class GenPropVal v where
-- | Whether this prints in a property definition.
-- If true, it would print @key: ...@. If false, it would print @key@.
doesPrint :: v an -> Bool
-- | A part of a value.
class (Ann p, GenPropVal (PartPropVal p), Eq (p ())) => GenPart p where
type PartPropVal p :: * -> *
partToPrim :: p an -> Maybe (Prim an)
partToRec :: p an -> Maybe (PartRecord p an)
primToPart :: Proxy p -> Prim an -> p an
recToPart :: Proxy p -> PartRecord p an -> p an
instance (GenPart p) => GenPropVal (GenValue p) where
doesPrint _ = True
instance (Functor p, Foldable p, Traversable p) => SAnn (GenValue p) where
mapSAnn f (Value ann parts) = Value (f ann) parts
instance (Functor p, Foldable p, Traversable p) => Ann (GenValue p) where
getAnn = valueAnn
instance (Functor v, Foldable v, Traversable v) => Ann (GenRecord v) where
getAnn = recordAnn
instance (Functor v, Foldable v, Traversable v) => Ann (GenProperty v) where
getAnn = propertyAnn
instance (Printable p) => Printable (GenValue p) where
aprintRec sub (Value _ parts) = pintercal " | " $ map sub parts
instance (Printable p) => FwdPrintable (GenValue p) where
afprintRec = id
instance (FwdPrintable v, GenPropVal v) => Printable (GenRecord v) where
aprintRec sub record = sub (head record) <> propsPrinted
where propsPrinted = "[" <> pintercal ", " propPrinteds <> "]"
propPrinteds = map sub $ properties record
instance (FwdPrintable v, GenPropVal v) => Printable (GenProperty v) where
aprintRec sub (Property _ key val)
= case key of
Nothing -> valPrinted
Just key'
| doesPrint val -> keyPrinted <> ": " <> valPrinted
| otherwise -> keyPrinted
where keyPrinted = sub key'
where valPrinted = afprintRec sub val
instance (Show an, Printable p, Show (p an)) => Summary (GenValue p an) where
summaryRec = pprintSummaryRec
instance (Show an, FwdPrintable v, GenPropVal v, Show (v an)) => Summary (GenRecord v an) where
summaryRec = pprintSummaryRec
instance (Show an, FwdPrintable v, GenPropVal v, Show (v an)) => Summary (GenProperty v an) where
summaryRec = pprintSummaryRec
-- | Creates a value with just the given part.
singletonValue :: (Ann p) => p an -> GenValue p an
singletonValue part = Value (getAnn part) [part]
-- | Whether the value has any parts.
isEmpty :: GenValue p an -> Bool
isEmpty (Value _ parts) = null parts
-- | All primitives in the value.
primParts :: (GenPart p) => GenValue p an -> [Prim an]
primParts = mapMaybe partToPrim . valueParts
-- | Maps the parts.
mapParts :: (p1 an -> p2 an) -> GenValue p1 an -> GenValue p2 an
mapParts f (Value ann parts) = Value ann $ map f parts
-- | Maps then folds the parts.
foldMapParts :: (Monoid r) => (p an -> r) -> GenValue p an -> r
foldMapParts f (Value _ parts) = foldMap f parts
-- | Traverses the parts.
traverseParts :: (Applicative w)
=> (p1 an -> w (p2 an))
-> GenValue p1 an
-> w (GenValue p2 an)
traverseParts f (Value ann parts) = Value ann <$> traverse f parts
-- | Gets the record in the value with the given head.
recWithHead :: (GenPart p)
=> Symbol an1
-> GenValue p an2
-> Maybe (PartRecord p an2)
recWithHead head' (Value _ parts)
= find (recHasHead head') $ mapMaybe partToRec parts
-- | Gets the record in the value with the given head.
-- Returns an error if the value isn't present
forceRecWithHead :: (GenPart p)
=> Symbol an1
-> GenValue p an2
-> PartRecord p an2
forceRecWithHead head' value
= case recWithHead head' value of
Nothing -> error "Value doesn't contain record with head"
Just record -> record
-- | Removes the record in the value with the given head.
-- If this record doesn't exist, does nothing.
deleteRecWithHead :: (GenPart p)
=> Symbol an
-> GenValue p an
-> GenValue p an
deleteRecWithHead head' (Value ann parts)
= Value ann $ filter (partHasHead head') parts
-- | Whether the part's a record with the given head.
partHasHead :: (GenPart p) => Symbol an -> p an -> Bool
partHasHead head' part
= case partToRec part of
Just record -> recHasHead head' record
Nothing -> False
-- | Does the record have the given head?
recHasHead :: Symbol an1 -> GenRecord v an2 -> Bool
recHasHead head' record = remAnns head' == remAnns (head record)
-- | Maps the record's property values.
mapPropVals :: (v an -> v2 an) -> GenRecord v an -> GenRecord v2 an
mapPropVals f record
= Record
{ recordAnn = recordAnn record
, head = head record
, properties = map (mapPropVal f) $ properties record
}
-- | Maps then folds the record's property values.
foldMapPropVals :: (Monoid r) => (v an -> r) -> GenRecord v an -> r
foldMapPropVals f = foldMap (f . propertyValue) . properties
-- | Traverses the record's property values.
traversePropVals :: (Applicative w) => (v an -> w (v2 an)) -> GenRecord v an -> w (GenRecord v2 an)
traversePropVals f record
= Record (recordAnn record) (head record)
<$> traverse (traversePropVal f) (properties record)
mapPropVal :: (v an -> v2 an) -> GenProperty v an -> GenProperty v2 an
mapPropVal f (Property ann key val) = Property ann key $ f val
traversePropVal :: (Applicative w) => (v an -> w (v2 an)) -> GenProperty v an -> w (GenProperty v2 an)
traversePropVal f (Property ann key val) = Property ann key <$> f val