-------------------------------------------------------------------------------- --- Library for representation and computation of definitional trees and --- representation of the reduction strategy phi. --- --- @author Jan-Hendrik Matthes --- @version February 2020 --- @category algorithm -------------------------------------------------------------------------------- module Rewriting.DefinitionalTree ( DefTree (..) , dtRoot, dtPattern, hasDefTree, selectDefTrees, fromDefTrees, idtPositions , defTrees, defTreesL, loDefTrees, phiRStrategy, dotifyDefTree, writeDefTree ) where import Function (both, on) import List import Maybe (catMaybes, listToMaybe, mapMaybe) import State import Rewriting.Position (Pos, eps, positions, replaceTerm, (.>), (|>)) import Rewriting.Rules import Rewriting.Strategy (RStrategy) import Rewriting.Substitution (applySubst) import Rewriting.Term import Rewriting.Unification (unifiable, unify) -- ----------------------------------------------------------------------------- -- Representation of definitional trees -- ----------------------------------------------------------------------------- --- Representation of a definitional tree, parameterized over the kind of --- function symbols, e.g., strings. --- --- @cons Leaf r - The leaf with rule `r`. --- @cons Branch pat p dts - The branch with pattern `pat`, inductive position --- `p` and definitional subtrees `dts`. --- @cons Or pat dts - The or node with pattern `pat` and definitional --- subtrees `dts`. data DefTree f = Leaf (Rule f) | Branch (Term f) Pos [DefTree f] | Or (Term f) [DefTree f] -- ----------------------------------------------------------------------------- -- Functions for definitional trees -- ----------------------------------------------------------------------------- --- Returns the root symbol (variable or constructor) of a definitional tree. dtRoot :: DefTree f -> Either VarIdx f dtRoot (Leaf r) = rRoot r dtRoot (Branch pat _ _) = tRoot pat dtRoot (Or pat _) = tRoot pat --- Returns the pattern of a definitional tree. dtPattern :: DefTree f -> Term f dtPattern (Leaf (l, _)) = l dtPattern (Branch pat _ _) = pat dtPattern (Or pat _) = pat --- Checks whether a term has a definitional tree with the same constructor --- pattern in the given list of definitional trees. hasDefTree :: Eq f => [DefTree f] -> Term f -> Bool hasDefTree dts t = any (eqConsPattern t . dtPattern) dts --- Returns a list of definitional trees with the same constructor pattern for a --- term from the given list of definitional trees. selectDefTrees :: Eq f => [DefTree f] -> Term f -> [DefTree f] selectDefTrees dts t = filter (eqConsPattern t . dtPattern) dts --- Returns the definitional tree with the given index from the given list of --- definitional trees or the provided default definitional tree if the given --- index is not in the given list of definitional trees. fromDefTrees :: DefTree f -> Int -> [DefTree f] -> DefTree f fromDefTrees dt _ [] = dt fromDefTrees dt n dts@(_:_) | n >= 0 && n < length dts = dts !! n | otherwise = dt --- Returns a list of all inductive positions in a term rewriting system. idtPositions :: TRS _ -> [Pos] idtPositions [] = [] idtPositions trs@((l, _):_) = case l of TermVar _ -> [] TermCons _ ts -> [[i] | i <- [1 .. length ts], all (isDemandedAt i) trs] --- Returns a list of definitional trees for a term rewriting system. defTrees :: Eq f => TRS f -> [DefTree f] defTrees = concatMap defTreesS . groupBy eqCons . sortBy eqCons where eqCons = on eqConsPattern fst --- Returns a list of definitional trees for a list of term rewriting systems. defTreesL :: Eq f => [TRS f] -> [DefTree f] defTreesL = defTrees . concat --- Returns a list of definitional trees for a term rewriting system, whose --- rules have the same constructor pattern. defTreesS :: Eq f => TRS f -> [DefTree f] defTreesS [] = [] defTreesS trs@((l, _):_) = case l of TermVar _ -> [] TermCons c ts -> let arity = length ts pat = TermCons c (map TermVar [0 .. arity - 1]) pss = permutations (idtPositions trs) in catMaybes [defTreesS' arity trs ps pat | ps <- pss] --- Returns a definitional tree for a term rewriting system, whose rules have --- the same constructor pattern, with the given list of inductive positions, --- the given pattern and the given next possible variable or `Nothing` if no --- such definitional tree exists. defTreesS' :: Eq f => VarIdx -> TRS f -> [Pos] -> Term f -> Maybe (DefTree f) defTreesS' _ [] [] _ = Nothing defTreesS' v [r] [] pat = mkLeaf v pat r defTreesS' v trs@(_:_:_) [] pat = mkOr v pat (partition (isDemandedAt 1) trs) defTreesS' v trs (p:ps) pat = Just (Branch pat p dts) where nls = nub [normalizeTerm (l |> p) | (l, _) <- trs] ts = map (renameTermVars v) nls pats = [replaceTerm pat p t | t <- ts] dts = catMaybes [defTreesS' v' (selectRules v' pat') ps pat' | pat' <- pats, let v' = max v (maybe 0 (+ 1) (maxVarInTerm pat'))] selectRules v' t = [r | r@(l, _) <- renameTRSVars v' trs, unifiable [(l, t)]] --- Returns a definitional tree for the given pattern, the given rule and the --- given next possible variable or `Nothing` if no such definitional tree --- exists. mkLeaf :: Eq f => VarIdx -> Term f -> Rule f -> Maybe (DefTree f) mkLeaf v pat r = case unify [(l, pat)] of Left _ -> Nothing Right sub | pat == applySubst sub l -> Just (Leaf (both (applySubst sub) r')) | otherwise -> let (ip:ips) = [p | p <- positions pat, isVarTerm (pat |> p)] pat' = replaceTerm pat ip (l |> ip) v' = max v (maybe 0 (+ 1) (maxVarInTerm pat')) in Just (Branch pat ip (catMaybes [defTreesS' v' [r] ips pat'])) where r'@(l, _) = renameRuleVars v (normalizeRule r) --- Returns a definitional tree for the given pattern, the given pair of term --- rewriting systems and the given next possible variable or `Nothing` if no --- such definitional tree exists. Only the rules in the first term rewriting --- system of the pair have a demanded first argument position. mkOr :: Eq f => VarIdx -> Term f -> (TRS f, TRS f) -> Maybe (DefTree f) mkOr _ _ ([], []) = Nothing mkOr v pat ([], rs2@(_:_)) = Just (Or pat (mapMaybe (mkLeaf v pat) rs2)) mkOr v pat (rs1@(_:_), []) = case intersect (idtPositions rs1) (varPositions pat) of [] -> Just (Or pat (mapMaybe (mkLeaf v pat) rs1)) ps -> defTreesS' v rs1 ps pat mkOr v pat (rs1@(_:_), rs2@(_:_)) = let vps = varPositions pat mdts = [defTreesS' v rs (intersect (idtPositions rs) vps) pat | rs <- [rs1, rs2]] in Just (Or pat (catMaybes mdts)) --- Returns a list of all variable argument positions in a term. varPositions :: Term _ -> [Pos] varPositions (TermVar _) = [] varPositions (TermCons _ ts) = [[i] | i <- [1 .. length ts], isVarTerm (ts !! (i - 1))] -- ----------------------------------------------------------------------------- -- Functions for the reduction strategy phi -- ----------------------------------------------------------------------------- --- Returns the position and the definitional trees from the given list of --- definitional trees for the leftmost outermost defined constructor in a term --- or `Nothing` if no such pair exists. loDefTrees :: Eq f => [DefTree f] -> Term f -> Maybe (Pos, [DefTree f]) loDefTrees [] _ = Nothing loDefTrees dts@(_:_) t = listToMaybe (loDefTrees' eps t) where loDefTrees' _ (TermVar _) = [] loDefTrees' p c@(TermCons _ ts) | hasDefTree dts c = [(p, selectDefTrees dts c)] | otherwise = [lp | (p', t') <- zip [1..] ts, lp <- loDefTrees' (p .> [p']) t'] --- The reduction strategy phi. It uses the definitional tree with the given --- index for all constructor terms if possible or at least the first one. phiRStrategy :: Eq f => Int -> RStrategy f phiRStrategy n trs t = let dts = defTrees trs in case loDefTrees dts t of Nothing -> [] Just (_, []) -> [] Just (p, dts'@(dt:_)) -> case phiRStrategy' n dts (t |> p) (fromDefTrees dt n dts') of Nothing -> [] Just p' -> [p .> p'] --- Returns the position for the reduction strategy phi where a term should be --- reduced according to the given definitional tree or `Nothing` if no such --- position exists. It uses the definitional tree with the given index for all --- constructor terms if possible or at least the first one. phiRStrategy' :: Eq f => Int -> [DefTree f] -> Term f -> DefTree f -> Maybe Pos phiRStrategy' _ _ t (Leaf (l, _)) | unifiable [(l', t)] = Just eps | otherwise = Nothing where l' = maybe l (\v -> renameTermVars (v + 1) l) (maxVarInTerm t) phiRStrategy' _ _ (TermVar _) (Branch _ _ _) = Nothing phiRStrategy' n dts t@(TermCons _ _) (Branch _ p dts') = case t |> p of TermVar _ -> Nothing tp@(TermCons _ _) -> case selectDefTrees dts tp of [] -> case find (\dt -> eqConsPattern tp (dtPattern dt |> p)) dts' of Nothing -> Nothing Just dt -> phiRStrategy' n dts t dt x@(dt:_) -> case phiRStrategy' n dts tp (fromDefTrees dt n x) of Nothing -> Nothing Just p' -> Just (p .> p') phiRStrategy' _ _ _ (Or _ _) = Nothing -- ----------------------------------------------------------------------------- -- Graphical representation of definitional trees -- ----------------------------------------------------------------------------- --- A node represented as a tuple of an integer, a possible inductive position --- and a term and parameterized over the kind of function symbols, e.g., --- strings. type Node f = (Int, Maybe Pos, Term f) --- An edge represented as a tuple of a boolean to distinguish between branch --- (`False`) and rule (`True`) edges, a start node and an end node and --- parameterized over the kind of function symbols, e.g., strings. type Edge f = (Bool, Node f, Node f) --- A graph represented as a tuple of nodes and edges and parameterized over --- the kind of function symbols, e.g., strings. type Graph f = ([Node f], [Edge f]) --- Transforms a definitional tree into a graph representation. toGraph :: DefTree f -> Graph f toGraph dt = fst (fst (runState (toGraph' dt) 0)) where toGraph' (Leaf (l, r)) = newIdx `bindS` \i -> let n = (i, Nothing, l) in mapS (ruleEdge n) [r] `bindS` addNode n toGraph' (Branch pat p dts) = newIdx `bindS` \i -> let n = (i, Just p, pat) in mapS (branchEdge n) dts `bindS` addNode n toGraph' (Or pat dts) = newIdx `bindS` \i -> let n = (i, Nothing, pat) in mapS (branchEdge n) dts `bindS` addNode n addNode n gs = let (ns, es) = unzip gs in returnS ((n : concat ns, concat es), n) branchEdge n1 dt' = toGraph' dt' `bindS` \((ns, es), n2) -> returnS (ns, (False, n1, n2):es) ruleEdge n1 t = newIdx `bindS` \i -> let n = (i, Nothing, t) in returnS ([n], [(True, n1, n)]) newIdx = getS `bindS` \i -> putS (i + 1) `bindS_` returnS i --- Transforms a term into a string representation and surrounds the subterm at --- the given position with the html `` and `` tags. showTermWithPos :: (f -> String) -> (Maybe Pos, Term f) -> String showTermWithPos s = showTP False where showTerm' _ (TermVar v) = showVarIdx v showTerm' b (TermCons c ts) = case ts of [] -> s c [l, r] -> parensIf b (showTerm' True l ++ " " ++ s c ++ " " ++ showTerm' True r) _ -> s c ++ "(" ++ intercalate "," (map (showTerm' False) ts) ++ ")" showTP b (Nothing, t) = showTerm' b t showTP b (Just [], t) = "" ++ showTerm' b t ++ "" showTP _ (Just (_:_), TermVar v) = showVarIdx v showTP b (Just (p:ps), TermCons c ts) = case [(if i == p then Just ps else Nothing, t) | (i, t) <- zip [1..] ts] of [] -> s c [l, r] -> parensIf b (showTP True l ++ " " ++ s c ++ " " ++ showTP True r) ts' -> s c ++ "(" ++ intercalate "," (map (showTP False) ts') ++ ")" --- Transforms a definitional tree into a graphical representation by using the --- *DOT graph description language*. dotifyDefTree :: (f -> String) -> DefTree f -> String dotifyDefTree s dt = "digraph definitional_tree {\n" ++ " graph [margin=0.0];\n" ++ " node [fontname=\"Menlo\",fontsize=10.0,shape=box];\n" ++ unlines (map showNode ns) ++ " edge [fontname=\"Menlo\",fontsize=7.0,arrowhead=none];\n" ++ unlines (map showEdge es) ++ "}" where (ns, es) = toGraph dt showNode (n, p, t) = " " ++ showVarIdx n ++ " [label=<" ++ showTermWithPos s (p, t) ++ ">];" showEdge (b, (n1, _, _), (n2, _, _)) = let opts = if b then " [arrowhead=normal];" else ";" in " " ++ showVarIdx n1 ++ " -> " ++ showVarIdx n2 ++ opts --- Writes the graphical representation of a definitional tree with the --- *DOT graph description language* to a file with the given filename. writeDefTree :: (f -> String) -> DefTree f -> String -> IO () writeDefTree s dt fn = writeFile fn (dotifyDefTree s dt) -- ----------------------------------------------------------------------------- -- Definition of helper functions -- ----------------------------------------------------------------------------- --- Encloses a string in parentheses if the given condition is true. parensIf :: Bool -> String -> String parensIf b s = if b then "(" ++ s ++ ")" else s