represent pattern matrices and typed occurrences

This commit is contained in:
milner committed 2020-01-31 12:00:00 +00:00
1 parent 4fadf4bc01
commit d805be2117
2 files changed
+403

No files matched your search

+269
View File
@@ -0,0 +1,269 @@
module Tessera.Matrix where
import Data.List (foldl', sortBy)
import Data.Maybe (mapMaybe)
import Data.Ord (comparing)
import qualified Data.Map.Strict as Map
import Tessera.Env
import Tessera.Syntax
data Key
= KeyCon String
| KeyInt Integer
| KeyBool Bool
| KeyTuple Int
deriving (Eq, Show)
data Signature
= FiniteSignature [(Key, [Type])]
| IntegerSignature
deriving (Eq, Show)
data Outcome
= NotCovered [Pattern]
| Covered
| SearchExhausted
deriving (Eq, Show)
signature :: Env -> Type -> Signature
signature env ty = case ty of
TName "Int" [] -> IntegerSignature
TName "Bool" [] -> FiniteSignature [(KeyBool True, []), (KeyBool False, [])]
TName name arguments -> case lookupType env name of
Just definition
| not (Map.findWithDefault True name (finiteInhabitants env)) -> FiniteSignature []
| otherwise ->
FiniteSignature
[ (KeyCon (constructorName c), instantiate (typeDefinitionParams definition) arguments (constructorArgs c))
| c <- typeDefinitionConstructors definition
]
Nothing -> FiniteSignature []
TTuple elements -> FiniteSignature [(KeyTuple (length elements), elements)]
TVar _ -> FiniteSignature []
headKey :: Pattern -> Maybe Key
headKey pattern' = case pattern' of
PCon name _ -> Just (KeyCon name)
PInt n -> Just (KeyInt n)
PBool b -> Just (KeyBool b)
PTuple elements -> Just (KeyTuple (length elements))
PVar _ -> Nothing
PWild -> Nothing
isVariable :: Pattern -> Bool
isVariable pattern' = case pattern' of
PVar _ -> True
PWild -> True
_ -> False
patternArguments :: Pattern -> [Pattern]
patternArguments pattern' = case pattern' of
PCon _ arguments -> arguments
PTuple elements -> elements
_ -> []
keyArity :: Env -> Key -> Int
keyArity env key = case key of
KeyCon name -> maybe 0 id (constructorArity env name)
KeyBool _ -> 0
KeyInt _ -> 0
KeyTuple n -> n
buildKey :: Key -> [Pattern] -> Pattern
buildKey key arguments = case key of
KeyCon name -> PCon name arguments
KeyBool b -> PBool b
KeyInt n -> PInt n
KeyTuple n -> PTuple (take n arguments)
specialize :: Env -> Key -> [[Pattern]] -> [[Pattern]]
specialize env key = concatMap step
where
arity = keyArity env key
step row = case row of
[] -> []
(p : rest)
| isVariable p -> [replicate arity PWild ++ rest]
| headKey p == Just key -> [patternArguments p ++ rest]
| otherwise -> []
defaultMatrix :: [[Pattern]] -> [[Pattern]]
defaultMatrix = mapMaybe step
where
step row = case row of
(p : rest) | isVariable p -> Just rest
_ -> Nothing
allConstructorsPresent :: [(Key, [Type])] -> [Pattern] -> Bool
allConstructorsPresent constructors column =
all (\(key, _) -> key `elem` mapMaybe headKey column) constructors
inhabitedType :: Env -> [String] -> Type -> Bool
inhabitedType env visited ty = case ty of
TVar _ -> True
TTuple elements -> all (inhabitedType env visited) elements
TName "Int" _ -> True
TName "Bool" _ -> True
TName name arguments
| name `elem` visited -> False
| otherwise -> case lookupType env name of
Just definition ->
any (constructorInhabited (name : visited)) (typeDefinitionConstructors definition)
where
constructorInhabited seen c =
all (inhabitedType env seen) (instantiate (typeDefinitionParams definition) arguments (constructorArgs c))
Nothing -> False
sample :: Env -> Int -> Type -> Pattern
sample env limit ty
| limit <= 0 = PWild
| otherwise = case ty of
TVar _ -> PWild
TName "Int" _ -> PInt 0
TName "Bool" _ -> PBool False
TTuple elements -> PTuple (map (sample env (limit - 1)) elements)
TName name arguments -> case lookupType env name of
Just definition ->
let candidates =
[ (c, instantiate (typeDefinitionParams definition) arguments (constructorArgs c))
| c <- typeDefinitionConstructors definition
]
usable =
[ (c, argumentTypes)
| (c, argumentTypes) <- candidates
, all (inhabitedType env []) argumentTypes
]
ordered = sortBy (comparing (length . snd)) usable
in case ordered of
((c, argumentTypes) : _) ->
PCon (constructorName c) (map (sample env (limit - 1)) argumentTypes)
[] -> PWild
Nothing -> PWild
sampleRow :: Env -> Int -> [Type] -> [Pattern]
sampleRow env limit = map (sample env limit)
finiteInhabitants :: Env -> Map.Map String Bool
finiteInhabitants env = iterateUntilEqual Map.empty
where
names = Map.keys (envTypes env)
iterateUntilEqual known =
let next = Map.fromList [(name, hasInhabitant known name) | name <- names]
in if next == known then known else iterateUntilEqual next
hasInhabitant known name
| name `elem` ["Int", "Bool"] = True
| otherwise = case lookupType env name of
Just definition -> any (all (argumentInhabited known) . constructorArgs) (typeDefinitionConstructors definition)
Nothing -> False
argumentInhabited known ty = case ty of
TVar _ -> True
TName name _ -> Map.findWithDefault False name known
TTuple elements -> all (argumentInhabited known) elements
freshInteger :: [Integer] -> Integer
freshInteger used = head [n | n <- [0 ..], n `notElem` used]
firstJust :: [Maybe a] -> Maybe a
firstJust [] = Nothing
firstJust (Just x : _) = Just x
firstJust (Nothing : rest) = firstJust rest
witness :: Env -> Int -> [Type] -> [[Pattern]] -> Outcome
witness env limit types matrix
| limit <= 0 = SearchExhausted
| null types = if null matrix then NotCovered [] else Covered
| otherwise = case signature env (head types) of
IntegerSignature -> integerWitness env (limit - 1) types matrix
FiniteSignature constructors ->
let column = map head matrix
complete = allConstructorsPresent constructors column
in if complete
then combine (map (branch constructors matrix types) constructors)
else case witness env (limit - 1) (tail types) (defaultMatrix matrix) of
NotCovered rest ->
case [ (key, argumentTypes) | (key, argumentTypes) <- constructors, key `notElem` mapMaybe headKey column ] of
((key, argumentTypes) : _) ->
NotCovered (buildKey key (sampleRow env (limit - 1) argumentTypes) : rest)
[] -> Covered
Covered -> Covered
SearchExhausted -> SearchExhausted
where
branch constructors' matrix' types' (key, argumentTypes) =
case witness env (limit - 1) (argumentTypes ++ tail types') (specialize env key matrix') of
NotCovered result ->
let arity = length argumentTypes
in NotCovered (buildKey key (take arity result) : drop arity result)
Covered -> Covered
SearchExhausted -> SearchExhausted
combine outcomes = case [rest | NotCovered rest <- outcomes] of
(rest : _) -> NotCovered rest
[] -> if SearchExhausted `elem` outcomes then SearchExhausted else Covered
integerWitness :: Env -> Int -> [Type] -> [[Pattern]] -> Outcome
integerWitness env limit types matrix =
let column = map head matrix
hasVariable = any isVariable column
literals = [n | PInt n <- column]
in if hasVariable
then case witness env limit (tail types) (defaultMatrix matrix) of
NotCovered rest -> NotCovered (PInt (freshInteger literals) : rest)
Covered -> Covered
SearchExhausted -> SearchExhausted
else NotCovered (PInt (freshInteger literals) : sampleRow env limit (tail types))
useful :: Env -> [Type] -> [[Pattern]] -> [Pattern] -> Bool
useful env types matrix query
| null query = null matrix
| otherwise = case query of
(p : rest) -> case headKey p of
Just key ->
useful env (keyArgumentTypes env (head types) key ++ tail types) (specialize env key matrix) (patternArguments p ++ rest)
Nothing -> case signature env (head types) of
IntegerSignature -> useful env (tail types) (defaultMatrix matrix) rest
FiniteSignature constructors ->
if allConstructorsPresent constructors (map head matrix)
then any (branch constructors matrix types rest) constructors
else useful env (tail types) (defaultMatrix matrix) rest
where
branch constructors' matrix' types' rest (key, argumentTypes) =
useful env (argumentTypes ++ tail types') (specialize env key matrix') (replicate (length argumentTypes) PWild ++ rest)
keyArgumentTypes :: Env -> Type -> Key -> [Type]
keyArgumentTypes env ty key = case signature env ty of
FiniteSignature constructors -> maybe [] id (lookup key constructors)
IntegerSignature -> []
usefulWith :: Env -> [Type] -> [[Pattern]] -> [Pattern] -> Outcome
usefulWith env types matrix query
| null query = if null matrix then NotCovered [] else Covered
| otherwise = case query of
(p : rest) -> case headKey p of
Just key -> case witness env 200 (keyArgumentTypes env (head types) key ++ tail types) (specialize env key matrix) of
NotCovered result ->
let arity = length (patternArguments p)
in NotCovered (buildKey key (take arity result) : drop arity result)
Covered -> Covered
SearchExhausted -> SearchExhausted
Nothing -> case signature env (head types) of
IntegerSignature -> usefulWith env (tail types) (defaultMatrix matrix) rest
FiniteSignature constructors ->
if allConstructorsPresent constructors (map head matrix)
then combine [ branch constructors matrix types rest c | c <- constructors ]
else usefulWith env (tail types) (defaultMatrix matrix) rest
where
branch constructors' matrix' types' rest (key, argumentTypes) =
usefulWith env (argumentTypes ++ tail types') (specialize env key matrix') (replicate (length argumentTypes) PWild ++ rest)
combine outcomes = case [result | NotCovered result <- outcomes] of
(result : _) -> NotCovered result
[] -> if SearchExhausted `elem` outcomes then SearchExhausted else Covered
matrixRows :: [[Pattern]] -> Int
matrixRows = length
coverCheck :: Env -> [Type] -> [[Pattern]] -> Pattern -> Bool
coverCheck env types matrix value =
let row = [value]
in useful env types matrix row
+134
View File
@@ -0,0 +1,134 @@
module Tessera.Tree where
import Data.List (foldl')
import Data.Maybe (mapMaybe)
import Tessera.Env
import Tessera.Matrix
import Tessera.Syntax
data Occurrence = Occurrence [Int] deriving (Eq, Show)
data Tree
= Success Int
| Failure
| Switch Occurrence [(Key, Tree)] (Maybe Tree)
| Bind Occurrence String Tree
deriving (Eq, Show)
data Strategy = Leftmost | Heuristic deriving (Eq, Show)
strategyName :: Strategy -> String
strategyName Leftmost = "leftmost"
strategyName Heuristic = "heuristic"
removeAt :: Int -> [a] -> [a]
removeAt index items = take index items ++ drop (index + 1) items
type Row = ([Pattern], Int)
specializeRows :: Env -> Key -> [Row] -> [Row]
specializeRows env key = concatMap step
where
arity = keyArity env key
step (row, index) = case row of
[] -> []
(p : rest)
| isVariable p -> [(replicate arity PWild ++ rest, index)]
| headKey p == Just key -> [(patternArguments p ++ rest, index)]
| otherwise -> []
defaultRows :: [Row] -> [Row]
defaultRows = mapMaybe step
where
step (row, index) = case row of
(p : rest) | isVariable p -> Just (rest, index)
_ -> Nothing
compileMatch :: Env -> Strategy -> [Type] -> [Row] -> [Occurrence] -> Tree
compileMatch env strategy types rows occurrences
| null rows = Failure
| all isVariable (fst (head rows)) =
let (row, index) = head rows
in foldr bindVariable (Success index) (zip row occurrences)
| otherwise =
let patterns = map fst rows
column = chooseColumn strategy patterns
columnType = types !! column
occurrence = occurrences !! column
restTypes = removeAt column types
restOccurrences = removeAt column occurrences
columnPatterns = map (!! column) patterns
appearing = mapMaybe headKey columnPatterns
path = occurrencePath occurrence
in case signature env columnType of
IntegerSignature ->
let literals = [n | KeyInt n <- appearing]
branches =
[ (KeyInt n, compileMatch env strategy restTypes (specializeRows env (KeyInt n) rows) restOccurrences)
| n <- literals
]
defaultBranch =
if any isVariable columnPatterns
then Just (compileMatch env strategy restTypes (defaultRows rows) restOccurrences)
else Nothing
in Switch occurrence branches defaultBranch
FiniteSignature constructors ->
let keys = map fst constructors
complete = allConstructorsPresent constructors columnPatterns
branchKeys = if complete then keys else [k | k <- keys, k `elem` appearing]
branches =
[ ( key
, compileMatch env strategy (argumentTypes ++ restTypes) (specializeRows env key rows) (argumentOccurrences ++ restOccurrences)
)
| key <- branchKeys
, let argumentTypes = maybe [] id (lookup key constructors)
, let argumentOccurrences = [Occurrence (path ++ [j]) | j <- [0 .. length argumentTypes - 1]]
]
defaultBranch =
if not complete && not (null (defaultRows rows))
then Just (compileMatch env strategy restTypes (defaultRows rows) restOccurrences)
else Nothing
in Switch occurrence branches defaultBranch
where
bindVariable (pattern', occurrence) inner = case pattern' of
PVar name -> Bind occurrence name inner
_ -> inner
occurrencePath :: Occurrence -> [Int]
occurrencePath (Occurrence path) = path
chooseColumn :: Strategy -> [[Pattern]] -> Int
chooseColumn Leftmost matrix = head candidates
where candidates = [i | i <- [0 .. width - 1], not (isVariable (head matrix !! i))]
width = length (head matrix)
chooseColumn Heuristic matrix = foldl' better (head candidates) (tail candidates)
where
width = length (head matrix)
candidates = [i | i <- [0 .. width - 1], not (isVariable (head matrix !! i))]
better best candidate =
let score i = (distinctKeys i, negate (constructorCount i), i)
in if score candidate < score best then candidate else best
distinctKeys i = length (unique [k | Just k <- map (headKey . (!! i)) matrix])
constructorCount i = length [() | row <- matrix, not (isVariable (row !! i))]
unique = foldl' (\acc x -> if x `elem` acc then acc else acc ++ [x]) []
initialOccurrences :: Int -> [Occurrence]
initialOccurrences n = [Occurrence [i] | i <- [0 .. n - 1]]
treeSize :: Tree -> Int
treeSize tree = case tree of
Success _ -> 1
Failure -> 1
Bind _ _ inner -> 1 + treeSize inner
Switch _ branches fallback -> 1 + sum (map (treeSize . snd) branches) + maybe 0 treeSize fallback
treeDepth :: Tree -> Int
treeDepth tree = case tree of
Success _ -> 1
Failure -> 1
Bind _ _ inner -> 1 + treeDepth inner
Switch _ branches fallback ->
let branchDepth = maximum (0 : map (treeDepth . snd) branches)
fallbackDepth = maybe 0 treeDepth fallback
in 1 + max branchDepth fallbackDepth