blob: 5f74a5748146e89e9bd3c05b774b22c5776332a7 [file] [log] [blame] [edit]
-- |
-- Module : Minigent.Environment
-- Copyright : (c) Data61 2018-2019
-- Commonwealth Science and Research Organisation (CSIRO)
-- ABN 41 687 119 230
-- License : BSD3
--
-- Definitions for global environments and local type contexts.
--
-- May be used qualified or unqualified.
{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveFunctor #-}
module Minigent.Environment
( -- * Global Environments
GlobalEnvironments (..)
, emptyGlobalEnvironments
, -- * Contexts
Context
, push
, pop
, use
, topUsed
, unused
, factor
, alter
, reconcile
) where
import Minigent.Syntax
import Data.Maybe (mapMaybe)
import qualified Data.Map as M
import qualified Data.List as L
-- | Global environments are for top-level functions, both definitions and types.
data GlobalEnvironments
= GlobalEnvs
{ defns :: M.Map FunName (VarName, Expr)
, types :: M.Map FunName PolyType
} deriving (Show)
emptyGlobalEnvironments = GlobalEnvs M.empty M.empty
-- | A value of type @Context a@ maps variable names to values of type @a@,
-- keeping track of the amount of times the variable is "used". It can be
-- thought of as a /stack/ of bindings, with below 'push' and 'pop' functions.
newtype Context a = Ctx [(VarName, Int, a)] deriving (Semigroup, Monoid, Functor, Show)
-- | Add a new binding for the given variable to the context, shadowing existing bindings.
push :: (VarName, a) -> Context a -> Context a
push (v, x) (Ctx m) = Ctx ( (v, 0,x) : m)
-- | Returns true if the topmost bound variable in the context has been "used".
topUsed :: Context a -> Bool
topUsed (Ctx ((_,u,_):_)) = u > 0
topUsed _ = error "topUsed called on empty context"
-- | Removes the topmost binding from the context. If the context is empty, does nothing.
pop :: Context a -> Context a
pop (Ctx xs) = Ctx (drop 1 xs)
-- | Given a variable name, look it up in the context, returning its associated value.
-- Also returns a new context where the variable's usage count has been increased,
-- and a boolean that is true if the variable had already been used before.
use :: (Show a) => VarName -> Context a -> (a, Bool, Context a)
use v (Ctx ls) = let (a, r, ls') = go ls in (a, r, Ctx ls')
where
go [] = error "use called on an out of scope variable"
go (x@(var, usage, a) : xs)
| v == var = (a, usage > 0, (var, usage + 1, a) : xs)
| v /= var = let (a', i, xs') = go xs
in (a', i, x:xs')
-- | Returns all elements in the context that have been left unused.
unused :: Context a -> [a]
unused (Ctx ls) = mapMaybe (\(v,u,a) -> if u == 0 then Just a else Nothing) ls
-- | Extract specific variables from a context. Given a list of variable names
-- and an input context, the output will contain only those variables in
-- the input context mentioned in the list.
--
-- This is used in let! expressions to extract observer variables from the
-- rest of the context.
factor :: [VarName] -> Context a -> Context a
factor vs (Ctx xs) = let as = go vs xs in Ctx as
where
go vs [] = []
go vs ((v,u,a):xs)
| v `elem` vs = (v,u,a): go (L.delete v vs) xs
| otherwise = go vs xs
-- | Alter specific bindings using the provided function.
--
-- This is used in let! expressions to modify observer variable bindings.
alter :: [VarName] -> ((a, Int) -> (a, Int)) -> Context a -> Context a
alter vs f (Ctx xs) = Ctx (map go xs)
where
go (v,u,a)
| v `elem` vs = let (a',u') = f (a,u) in (v,u',a')
| otherwise = (v,u,a)
-- | Given two contexts that are assumed to have the same variables
-- and elements, but may
-- differ in their usage counts, return a combined context where
-- the usage count is the maximum of the inputs, along with any elements
-- where the usage counts differed.
--
-- This is used in situations like if and case expressions, where
-- control flow branches, and the same linear variables must be used in both
-- branches.
reconcile :: Context a -> Context a -> ([a], Context a)
reconcile (Ctx xs) (Ctx ys) = let (as, zs) = go xs ys in (as, Ctx zs)
where
go [] [] = ([], [])
go ((v,u,a):xs) ((_,u',_):ys)
= let (as, zs) = go xs ys
z = (v,max u u',a)
as' = if u /= u' then a:as else as
in (as', z:zs)