descript-lang-0.2.0.0: src/Descript/BasicInj/Data/Value/Gen.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Descript.BasicInj.Data.Value.Gen
( GenProperty (..)
, GenRecord (..)
, GenValue (..)
, PartProperty
, PartRecord
, GenPropVal (..)
, GenPart (..)
, singletonValue
, isEmpty
, primParts
, mapParts
, foldMapParts
, traverseParts
, recWithHead
, forceRecWithHead
, deleteRecWithHead
, mergeAddRecord
, partHasHead
, recHasHead
, recHasProp
, lookupProp
, forceLookupProp
, mapPropVals
, foldMapPropVals
, traversePropVals
, reconValue
, reValue
, reRecord
, reProperty
) where
import Prelude hiding (head)
import Descript.BasicInj.Data.Atom
import Descript.Misc
import Data.Monoid as M
import Core.Data.Monoid
import Data.Semigroup as S
import Core.Data.Semigroup
import Data.Proxy
import Data.Maybe
import Data.List hiding (head)
import Core.Data.List.Assoc
import qualified Data.List.NonEmpty as NonEmpty
-- | A property whose values are of type @v@.
data GenProperty v an
= Property
{ propertyAnn :: an
, propertyKey :: 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 :: FSymbol 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 (Semigroup1 v, Monoid1 v) => 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
mergeAddPart :: (Semigroup an) => p an -> [p an] -> [p an]
instance (GenPart p, Semigroup an, Monoid an) => Monoid (GenValue p an) where
mempty = Value mempty []
mappend = (S.<>)
mconcat vals
= Value
{ valueAnn = mconcat $ map valueAnn vals
, valueParts = cmergeParts $ map valueParts vals
}
instance (GenPart p, Semigroup an) => Semigroup (GenValue p an) where
Value xAnn xParts <> Value yAnn yParts
= Value (xAnn S.<> yAnn) $ mergeParts xParts yParts
sconcat vals
= Value
{ valueAnn = sconcat $ NonEmpty.map valueAnn vals
, valueParts = cmergeParts $ map valueParts $ NonEmpty.toList vals
}
instance (GenPart p) => GenPropVal (GenValue p) where
doesPrint _ = True
instance (GenPart p) => Monoid1 (GenValue p)
instance (GenPart p) => Semigroup1 (GenValue p)
instance AssocPair (GenProperty v an) where
type Key (GenProperty v an) = Symbol () -- Don't want annotations to distinguish keys.
type Value (GenProperty v an) = v an
getKey = remAnns . propertyKey
getValue = propertyValue
instance (Semigroup an) => SemAssocPair (GenProperty v an) where
aappend1 vapp (Property xAnn xKey xVal) (Property yAnn yKey yVal)
= Property (xAnn S.<> yAnn) (xKey `eappend` yKey) (xVal `vapp` yVal)
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) M.<> propsPrinted
where propsPrinted = "[" M.<> pintercal ", " propPrinteds M.<> "]"
propPrinteds = map sub $ properties record
instance (FwdPrintable v, GenPropVal v) => Printable (GenProperty v) where
aprintRec sub (Property _ key val)
| doesPrint val = pimp (keyPrinted M.<> ": ") M.<> valPrinted
| otherwise = keyPrinted
where keyPrinted = sub key
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)
=> FSymbol 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)
=> FSymbol an1
-> GenValue p an2
-> PartRecord p an2
forceRecWithHead head' value
= case recWithHead head' value of
Nothing
-> error
$ "Value doesn't contain record with head: "
++ show (remAnns 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)
=> FSymbol an
-> GenValue p an
-> GenValue p an
deleteRecWithHead head' (Value ann parts)
= Value ann $ filter (partHasHead head') parts
cmergeParts :: (GenPart p, Semigroup an)
=> [[p an]]
-> [p an]
cmergeParts = foldr mergeParts []
mergeParts :: (GenPart p, Semigroup an)
=> [p an]
-> [p an]
-> [p an]
mergeParts = foldr mergeAddPart
mergeAddRecord :: (GenPart p, Semigroup an)
=> PartRecord p an
-> [p an]
-> [p an]
mergeAddRecord record [] = [recToPart Proxy record]
mergeAddRecord record (x : xs)
= case tryMergeRecordWithPart record x of
Failure () -> x : mergeAddRecord record xs
Success newRecord -> recToPart Proxy newRecord : xs
tryMergeRecordWithPart :: (GenPart p, Semigroup an)
=> PartRecord p an
-> p an
-> UResult (PartRecord p an)
tryMergeRecordWithPart xRecord y
= case partToRec y of
Nothing -> Failure ()
Just yRecord -> tryMergeRecords xRecord yRecord
tryMergeRecords :: (Semigroup1 v, Semigroup an)
=> GenRecord v an
-> GenRecord v an
-> UResult (GenRecord v an)
tryMergeRecords (Record xAnn xHead xProps) (Record yAnn yHead yProps)
| xHead /@= yHead = Failure ()
| otherwise = Success Record
{ recordAnn = xAnn S.<> yAnn
, head = xHead `eappend` yHead
, properties = assocUnionBy sappend1 xProps yProps
}
-- | Whether the part's a record with the given head.
partHasHead :: (GenPart p) => FSymbol 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 :: FSymbol an1 -> GenRecord v an2 -> Bool
recHasHead head' record = head' =@= head record
-- | Does the record have the given property key?
recHasProp :: Symbol an1 -> GenRecord v an2 -> Bool
recHasProp key = assocMember (remAnns key) . properties
-- | Gets the property with the given key.
-- Returns 'Nothing' if the property doesn't exist.
lookupProp :: Symbol an1 -> GenRecord v an2 -> Maybe (v an2)
lookupProp key = glookup (remAnns key) . properties
-- | Gets the property with the given key.
-- Raises an error if the property doesn't exist.
forceLookupProp :: Symbol an1 -> GenRecord v an2 -> v an2
forceLookupProp key record
= case lookupProp key record of
Nothing
-> error
$ "No property with the given key: "
++ show (remAnns key)
Just x -> x
-- | 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
-- | Uses the old parts to taint the value's annotation if necessary --
-- specifically, the value is tainted if the number of parts changes.
reconValue :: (GenPart p, TaintAnn an)
=> an
-> [p an]
-> [GenValue p an]
-> GenValue p an
reconValue ann oldParts = reValue ann oldParts . cmergeParts . map valueParts
-- | Uses the old parts to taint the value's annotation if necessary --
-- specifically, the value is tainted if the number of parts changes.
reValue :: (TaintAnn an) => an -> [p an] -> [p an] -> GenValue p an
reValue ann oldParts newParts = Value ann' newParts
where ann'
| length oldParts /= length newParts = taint ann
| otherwise = ann
-- | Uses the old properties to taint the record's annotation if
-- necessary -- specifically, the value is tainted if the number of
-- properties changes.
reRecord :: (TaintAnn an)
=> an
-> FSymbol an
-> [GenProperty v an]
-> [GenProperty v an]
-> GenRecord v an
reRecord ann head' oldProps newProps = Record ann' head' newProps
where ann'
| length oldProps /= length newProps = taint ann
| otherwise = ann
-- | Uses the old property value to taint the property's annotation if
-- necessary -- specifically, the property is tainted if the old value
-- has a print and the new one doesn't, or vice versa.
reProperty :: (GenPropVal v, TaintAnn an)
=> an
-> Symbol an
-> v an
-> v an
-> GenProperty v an
reProperty ann key old new = Property ann' key new
where ann'
| doesPrint old /= doesPrint new = taint ann
| otherwise = ann