descript-lang-0.2.0.0: src/Descript/Sugar/Refine.hs
{-# LANGUAGE ApplicativeDo #-}
module Descript.Sugar.Refine
( refineDDepd
, refine
, refineInputValIn
, refineOutputValIn
) where
import qualified Descript.Sugar.Data.Value.Reg as Reg
import qualified Descript.Sugar.Data.Value.In as In
import qualified Descript.Sugar.Data.Value.Out as Out
import Descript.Sugar.Data
import qualified Descript.BasicInj.Data.Value.Reg as BasicInj.Reg
import qualified Descript.BasicInj.Data.Value.In as BasicInj.In
import qualified Descript.BasicInj.Data.Value.Out as BasicInj.Out
import qualified Descript.BasicInj.Data.Value.Gen as BasicInj.Record
import qualified Descript.BasicInj.Data.Type as BasicInj.RecordType
import qualified Descript.BasicInj.Data as BasicInj
import Descript.Misc
import Data.Monoid
import Data.Foldable
import Data.Maybe
import Core.Data.List
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import Control.Monad
import Prelude hiding (mod)
-- | Expands syntactic sugar in the source using the dependency.
-- Passes dependency failures along.
refineDDepd :: (TaintAnn an)
=> BasicInj.DirtyDepd Source an
-> BasicInj.DirtyDepd BasicInj.Source an
refineDDepd (Depd dextra x) = Depd dextra $ refine (dirtyVal dextra) x
-- | Expands syntactic sugar in the source.
refine :: (TaintAnn an)
=> BasicInj.Dep
-> Source an
-> BasicInj.Source an
refine extra (SourceModule mod)
= BasicInj.SourceModule $ refineBModule extra mod
refine extra (SourceProgram prog)
= BasicInj.SourceProgram $ refineProgram extra prog
refineProgram :: (TaintAnn an)
=> BasicInj.Dep
-> Program an
-> BasicInj.Program an
refineProgram extra (Program ann mod query')
= BasicInj.Program ann mod' $ refineQueryIn scope ctx query'
where scope = BasicInj.moduleScope mod'
ctx = recordCtx_ <> extraCtx
extraCtx = BasicInj.recordCtx extra
recordCtx_ = remAnns $ BasicInj.recordCtx $ BasicInj.amodule mod'
mod' = refineBModule extra mod
refineQueryIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> Query an
-> BasicInj.Query an
refineQueryIn scope ctx (Query ann val)
= BasicInj.Query ann $ refineRegValueIn scope ctx val
refineBModule :: (TaintAnn an)
=> BasicInj.Dep
-> BModule an
-> BasicInj.BModule an
refineBModule extra (BModule ann ictx amod)
= BasicInj.BModule ann ictx $ refineAModuleIn scope ctx amod
where scope = moduleDeclScope $ moduleDecl ictx
ctx = BasicInj.recordCtx extra
refineAModuleIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> AModule an
-> BasicInj.AModule an
refineAModuleIn scope extraCtx (AModule ann recordCtx' reduceCtx')
= BasicInj.AModule ann recordCtx'' reduceCtx''
where recordCtx'' = refineRecordCtxIn scope recordCtx'
reduceCtx'' = refineReduceCtxIn scope ctx reduceCtx'
ctx = recordCtx_ <> extraCtx
recordCtx_ = remAnns recordCtx''
refineRecordCtxIn :: (TaintAnn an)
=> AbsScope
-> RecordCtx an
-> BasicInj.RecordCtx an
refineRecordCtxIn scope (RecordCtx ann decls)
= BasicInj.RecordCtx ann $ map (refineRecordDeclIn scope) decls
refineRecordDeclIn :: (TaintAnn an)
=> AbsScope
-> RecordDecl an
-> BasicInj.RecordDecl an
refineRecordDeclIn scope (RecordDecl ann rtype)
= BasicInj.RecordDecl ann $ refineRecordTypeIn scope rtype
refineRecordTypeIn :: (TaintAnn an)
=> AbsScope
-> RecordType an
-> BasicInj.RecordType an
refineRecordTypeIn scope (RecordType ann head' props)
= BasicInj.RecordType ann (FSymbol scope head') props
refineReduceCtxIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> ReduceCtx an
-> BasicInj.ReduceCtx an
refineReduceCtxIn scope ctx (ReduceCtx ann (p :| ps))
= BasicInj.ReduceCtx ann topPhase
$ NonEmpty.map (refinePhaseCtxIn scope ctx) $ p :| ps
where topPhase = BasicInj.PhaseCtx (preInsertAnn $ getAnn p) []
refinePhaseCtxIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> PhaseCtx an
-> BasicInj.PhaseCtx an
refinePhaseCtxIn scope ctx (PhaseCtx ann reducers)
= BasicInj.PhaseCtx ann $ map (refineReducerIn scope ctx) reducers
refineReducerIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> Reducer an
-> BasicInj.Reducer an
refineReducerIn scope ctx (Reducer ann input' output')
= BasicInj.Reducer ann input'' (refineOutputValIn scope ctx input_ output')
where input_ = remAnns input''
input'' = refineInputValIn scope ctx input'
refineRegValueIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> Reg.Value an
-> BasicInj.Reg.Value an
refineRegValueIn scope ctx (Value ann parts)
= BasicInj.Value ann $ map (refineRegPartIn scope ctx) parts
refineInputValIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> In.Value an
-> BasicInj.In.Value an
refineInputValIn scope ctx (Value ann parts)
= BasicInj.Value ann $ map (refineInPartIn scope ctx) parts
refineOutputValIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> Out.Value an
-> BasicInj.Out.Value an
refineOutputValIn scope ctx in' (Value ann parts)
= BasicInj.Value ann $ map (refineOutPartIn scope ctx in') parts
refineRegPartIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> Reg.Part an
-> BasicInj.Reg.Part an
refineRegPartIn _ _ (Reg.PartPrim prim) = BasicInj.Reg.PartPrim prim
refineRegPartIn scope ctx (Reg.PartRecord record)
= BasicInj.Reg.PartRecord $ refineRegRecordIn scope ctx record
refineInPartIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> In.Part an
-> BasicInj.In.Part an
refineInPartIn _ _ (In.PartPrim prim) = BasicInj.In.PartPrim prim
refineInPartIn _ _ (In.PartPrimType primType) = BasicInj.In.PartPrimType primType
refineInPartIn scope ctx (In.PartRecord record)
= BasicInj.In.PartRecord $ refineInRecordIn scope ctx record
refineOutPartIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> Out.Part an
-> BasicInj.Out.Part an
refineOutPartIn _ _ _ (Out.PartPrim prim) = BasicInj.Out.PartPrim prim
refineOutPartIn scope ctx in' (Out.PartRecord record)
= BasicInj.Out.PartRecord $ refineOutRecordIn scope ctx in' record
refineOutPartIn scope _ in' (Out.PartPropPath path)
= BasicInj.Out.PartPropPath $ refinePropPathIn scope in' path
refineOutPartIn scope ctx in' (Out.PartInjApp app)
= BasicInj.Out.PartInjApp $ refineInjAppIn scope ctx in' app
refineRegRecordIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> Reg.Record an
-> BasicInj.Reg.Record an
refineRegRecordIn scope ctx (Record ann head' props)
= BasicInj.Record ann head''
$ zipWith (refineRegPropIn scope ctx head_) [0..] props
where head_ = remAnns head''
head'' = FSymbol scope head'
refineInRecordIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> In.Record an
-> BasicInj.In.Record an
refineInRecordIn scope ctx (Record ann head' props)
= BasicInj.Record ann head''
$ zipWith (refineInPropIn scope ctx head_) [0..] props
where head_ = remAnns head''
head'' = FSymbol scope head'
refineOutRecordIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> Out.Record an
-> BasicInj.Out.Record an
refineOutRecordIn scope ctx in' (Record ann head' props)
= BasicInj.Record ann head''
$ zipWith (refineOutPropIn scope ctx in' head_) [0..] props
where head_ = remAnns head''
head'' = FSymbol scope head'
refineRegPropIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.FSymbol ()
-> Int
-> Reg.Property an
-> BasicInj.Reg.Property an
refineRegPropIn scope ctx head' idx (Property ann key val)
= BasicInj.Property ann key' $ refineRegValueIn scope ctx val
where key' = resolvePropKey ctx head' idx keyAnn `fromMaybe` key
keyAnn = preInsertAnn ann
refineInPropIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.FSymbol ()
-> Int
-> In.Property an
-> BasicInj.In.Property an
refineInPropIn scope ctx head' idx (Property ann key val)
= BasicInj.Property ann key' $ refineInOptValueIn scope ctx val
where key' = resolvePropKey ctx head' idx keyAnn `fromMaybe` key
keyAnn = preInsertAnn ann
refineOutPropIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> BasicInj.FSymbol ()
-> Int
-> Out.Property an
-> BasicInj.Out.Property an
refineOutPropIn scope ctx in' head' idx (Property ann key val)
= BasicInj.Property ann key' $ refineOutputValIn scope ctx in' val
where key' = resolvePropKey ctx head' idx keyAnn `fromMaybe` key
keyAnn = preInsertAnn ann
refineInOptValueIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> In.OptValue an
-> BasicInj.In.OptValue an
refineInOptValueIn _ _ In.NothingValue = BasicInj.In.NothingValue
refineInOptValueIn scope ctx (In.JustValue val)
= BasicInj.In.JustValue $ refineInputValIn scope ctx val
refinePropPathIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.In.Value ()
-> PropPath an
-> BasicInj.PropPath an
refinePropPathIn scope in' (PropPath ann (x :| xs))
= BasicInj.PropPath ann $ x' :| refineSubPathIn scope inSub xs
where inSub = inputInElem x_ in'
x_ = remAnns x'
x' = refinePathElemIn scope in' x
refineSubPathIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.In.Value ()
-> SubPropPath an
-> BasicInj.SubPropPath an
refineSubPathIn _ _ [] = []
refineSubPathIn scope in' (x : xs)
= x' : refineSubPathIn scope inSub xs
where inSub = inputInElem x_ in'
x_ = remAnns x'
x' = refinePathElemIn scope in' x
refinePathElemIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.In.Value ()
-> PathElem an
-> BasicInj.PathElem an
refinePathElemIn _ in' (PathElemImp key) = BasicInj.PathElem ann key head'
where head' = resolvePathHead in' key
ann = getAnn key
refinePathElemIn scope _ (PathElemExp ann key head')
= BasicInj.PathElem ann key $ FSymbol scope head'
refineInjAppIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> Out.InjApp an
-> BasicInj.Out.InjApp an
refineInjAppIn scope ctx in' (Out.InjApp ann funcId' params')
= BasicInj.Out.InjApp ann funcId'
$ map (refineOutParamIn scope ctx in') params'
refineOutParamIn :: (TaintAnn an)
=> AbsScope
-> BasicInj.RecordCtx ()
-> BasicInj.In.Value ()
-> Out.InjParam an
-> BasicInj.Out.InjParam an
refineOutParamIn scope ctx in' (Out.InjParam ann val)
= BasicInj.Out.InjParam ann $ refineOutputValIn scope ctx in' val
inputInElem :: BasicInj.PathElem ()
-> BasicInj.In.Value ()
-> BasicInj.In.Value ()
inputInElem (BasicInj.PathElem () key head')
= fold
. ( BasicInj.In.optValToMaybeVal
<=< BasicInj.lookupProp key
<=< BasicInj.recWithHead head' )
resolvePropKey :: (TaintAnn an)
=> BasicInj.RecordCtx ()
-> BasicInj.FSymbol ()
-> Int
-> an
-> BasicInj.Symbol an
resolvePropKey ctx head' idx ann
= ann <$ (undefinedSym `fromMaybe` resolvePropKey_ ctx head' idx)
resolvePropKey_ :: BasicInj.RecordCtx ()
-> BasicInj.FSymbol ()
-> Int
-> Maybe (BasicInj.Symbol ())
resolvePropKey_ ctx head' idx
= (!? idx) . BasicInj.RecordType.properties
=<< BasicInj.lookupRecordType head' ctx
resolvePathHead :: (TaintAnn an)
=> BasicInj.In.Value ()
-> Symbol an
-> FSymbol an
resolvePathHead in' key
= ann' <$ (BasicInj.undefinedFSym `fromMaybe` resolvePathHead_ in' key_)
where key_ = remAnns key
ann' = postInsertAnn $ getAnn key
resolvePathHead_ :: BasicInj.In.Value ()
-> Symbol ()
-> Maybe (FSymbol ())
resolvePathHead_ (BasicInj.Value () parts) key
= case filter (BasicInj.recHasProp key) $ mapMaybe BasicInj.partToRec parts of
[record] -> Just $ BasicInj.Record.head record
_ -> Nothing