represent pattern matrices and typed occurrences
This commit is contained in:
2 files changed
+403
No files matched your search
@@ -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
|
||||||
@@ -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
|
||||||
Reference in new issue
Block a user