{------------------------------------------------------------------------------------- - - Preprocess abstract syntax trees, remove backward steps and optimize - Programmer: Leonidas Fegaras - Email: fegaras@cse.uta.edu - Web: http://lambda.uta.edu/ - Creation: 05/01/08, last update: 06/30/08 - - Copyright (c) 2008 by Leonidas Fegaras, the University of Texas at Arlington. All rights reserved. - This material is provided as is, with absolutely no warranty expressed or implied. - Any use is at your own risk. Permission is hereby granted to use or copy this program - for any purpose, provided the above notices are retained on all copies. - --------------------------------------------------------------------------------------} module XML.HXQ.Optimizer(optimize) where --import Debug.Trace import System.IO.Unsafe import Control.Monad import Char(toLower) import Database.HDBC import HXML(AttList) import XML.HXQ.Parser import XML.HXQ.XTree import XML.HXQ.DB import XML.HXQ.DBConnect paths = [ "current_step", "child_step", "descendant_step", "attribute_step", "attribute_descendant_step" ] distinct :: Eq a => [a] -> [a] distinct = foldl (\r a -> if elem a r then r else r++[a]) [] {-# NOINLINE publishXmlDoc #-} -- get an XML document stored in a relational database publishXmlDoc :: FilePath -> String -> Ast publishXmlDoc filepath name = let query = unsafePerformIO (publishWrapper filepath name) [ast] = parse (scan query) in ast where publishWrapper filepath name = do let prefix = map toLower name db <- connect filepath table <- findSchema db prefix let query = publishTable table disconnect db return query -- collect attribute constructions inside element constructions collect_attributes :: Ast -> (Ast,[Ast]) collect_attributes (Ast "attribute_construction" [attr,value]) = (Ast "call" [Avar "empty"],[Ast "pair" [attr,value]]) collect_attributes (Ast "call" [Avar "concatenate",x,y]) = let (cx,ax) = collect_attributes x (cy,ay) = collect_attributes y in (Ast "call" [Avar "concatenate",cx,cy],ax++ay) collect_attributes (Ast "append" es) = let (s,a) = foldr (\e (r,ar) -> let (cx,ax) = collect_attributes e in (cx:r,ax++ar)) ([],[]) es in (Ast "append" s,a) collect_attributes (Ast "step" (e:es)) = let (ce,ae) = collect_attributes e in (Ast "step" (ce:es),ae) collect_attributes e = (e,[]) -- does the expression contain a $var/.. ? parentOfVar :: Ast -> String -> Bool parentOfVar (Ast "step" [Ast "parent_step" [Ast "step" [Avar x]]]) var = x == var parentOfVar (Ast "let" [Avar v,s,_]) var | var == v = parentOfVar s var parentOfVar (Ast "for" [Avar v,Avar i,s,_]) var | var == v || var == i = parentOfVar s var parentOfVar (Ast _ args) var = or (map (\x -> parentOfVar x var) args) parentOfVar _ _ = False -- replace $var/.. with $nvar replaceParentOfVar :: Ast -> String -> String -> Ast replaceParentOfVar (Ast "step" [Ast "parent_step" [Ast "step" [Avar x]]]) var nvar | x == var = Avar nvar replaceParentOfVar (Ast "let" [Avar v,s,b]) var nvar | var == v = Ast "let" [Avar v,replaceParentOfVar s var nvar,b] replaceParentOfVar (Ast "for" [Avar v,Avar i,s,b]) var nvar | var == v || var == i = Ast "for" [Avar v,Avar i,replaceParentOfVar s var nvar,b] replaceParentOfVar (Ast f args) var nvar = Ast f (map (\x -> replaceParentOfVar x var nvar) args) replaceParentOfVar e _ _ = e -- Rules to extract the parent of an XQuery expression -- For every XQuery x and predicates p1 ... pn and for s in [tag,*,@attr]: -- x/s[p1]...[pn]/.. -> x[s[p1]...[pn]] -- x//s[p1]...[pn]/.. -> x//*[s[p1]...[pn]] removeParent :: Ast -> (Ast,Ast,Bool,Ast) removeParent (Ast "predicate" [c,x]) = let (nx,cond,childp,tag) = removeParent x in (Ast "predicate" [c,nx],cond,childp,tag) removeParent (Ast "step" ((Ast "child_step" [tag,x]):preds)) = (Ast "step" ((Ast "child_step" [tag,Avar "."]):preds),x,True,tag) removeParent (Ast "step" ((Ast "descendant_step" [tag,x]):preds)) = (Ast "step" ((Ast "child_step" [tag,Avar "."]):preds), Ast "step" [Ast "descendant_step" [Astring "*",x]],True,tag) removeParent (Ast "step" ((Ast "attribute_step" [tag,x]):preds)) = (Ast "step" ((Ast "attribute_step" [tag,Avar "."]):preds),x,False,tag) removeParent (Ast "step" ((Ast "descendant_attribute_step" [tag,x]):preds)) = (Ast "step" ((Ast "attribute_step" [tag,Avar "."]):preds), Ast "step" ((Ast "descendant_step" [Astring "*",x]):preds),False,tag) removeParent (Ast "step" (x:xs)) = let (nx,cond,childp,tag) = removeParent x in (Ast "step" (nx:xs),cond,childp,tag) removeParent e = error ("Cannot remove this parent step "++(show e)) tagged_children :: String -> Ast -> [Tag] tagged_children context (Ast "step" ((Ast "child_step" [Astring tag,Avar "."]):_)) | context == "." = [tag] tagged_children context (Ast "step" ((Ast "child_step" [Astring tag,Ast "step" ((Avar v):_)]):_)) | v == context = [tag] tagged_children _ (Ast "step" ((Ast "descendant_any" _):_)) = [] tagged_children _ (Ast "step" ((Ast step _):_)) | elem step paths = [] tagged_children context (Ast _ xs) = concatMap (tagged_children context) xs tagged_children _ _ = [] empty = Ast "call" [Avar "empty"] simplify :: Ast -> Ast -- must be done bottom-up: /../.. simplify (Ast "step" [Ast "parent_step" [Ast "step" [Ast "parent_step" x]]]) = let nx = simplify (Ast "step" [Ast "parent_step" x]) in simplify (Ast "step" [Ast "parent_step" [nx]]) -- get rid of a parent step simplify (Ast "step" [Ast "parent_step" [x]]) = let (cond,nx,_,_) = removeParent x in Ast "predicate" [simplify cond,simplify nx] -- remove $var/.. in a let-FLWOR simplify (Ast "let" [Avar var,source,body]) | parentOfVar body var = let (cond,nx,childp,tag) = removeParent source in simplify (Ast "let" [Avar (var++"_parent"),Ast "predicate" [cond,nx], Ast "let" [Avar var, Ast "step" [ Ast (if childp then "child_step" else "attribute_step") [tag,Avar (var++"_parent")] ], replaceParentOfVar body var (var++"_parent")]]) -- remove $var/.. from a for-FLWOR simplify (Ast "for" [Avar var,Avar "$",source,body]) | parentOfVar body var = let (cond,nx,childp,tag) = removeParent source in simplify (Ast "for" [Avar (var++"_parent"),Avar "$",Ast "predicate" [cond,nx], Ast "for" [Avar var,Avar "$", Ast "step" [ Ast (if childp then "child_step" else "attribute_step") [tag,Avar (var++"_parent")] ], replaceParentOfVar body var (var++"_parent")]]) -- pull out attributes from a general element construction simplify (Ast "element_construction" [tag,Ast "attributes" as,content]) = let (nc,attrs) = collect_attributes content in simplify (Ast "construction" [tag,Ast "attributes" (as++attrs),nc]) -- if //* collect all children tagnames to use descendant_any_with_tagged_children simplify (Ast "for" [Avar var,i,Ast "step" [Ast "step" ((Ast "descendant_step" [Astring "*",path]):preds)],body]) | not (null ((tagged_children var body))) || any (not . null . (tagged_children ".")) preds = let ctags = distinct ((tagged_children var body)++(concatMap (tagged_children ".") preds)) tags = map Avar ctags in simplify (Ast "for" [Avar var,i,Ast "step" [Ast "step" ((Ast "descendant_any" (path:tags)):preds)],body]) simplify (Ast "step" ((Ast "child_step" [Astring tag,Ast "step" ((Ast "descendant_step" [Astring "*",path]):preds)]):preds2)) = let ctags = distinct(tag:(concatMap (tagged_children ".") preds)) tags = map Avar ctags in simplify (Ast "step" ((Ast "child_step" [Astring tag,Ast "step" ((Ast "descendant_any" (path:tags)):preds)]):preds2)) simplify (Ast "step" ((Ast "descendant_step" [Astring "*",path]):preds)) | any (not . null . (tagged_children ".")) preds = let ctags = distinct (concatMap (tagged_children ".") preds) tags = map Avar ctags in simplify (Ast "step" ((Ast "descendant_any" (path:tags)):preds)) -- expand the wrapper of a stored document simplify (Ast "call" [Avar "publish",Astring dbpath,Astring name]) = simplify (publishXmlDoc dbpath name) -- default simplify (Ast n args) = Ast n (map simplify args) simplify e = e taggedElement :: [Ast] -> String -> Maybe [Ast] taggedElement (e@(Ast "construction" [Astring ctag,_,x]):xs) tag | ctag == tag || tag == "*" = case taggedElement xs tag of Nothing -> Nothing Just s -> Just (e:s) taggedElement ((Ast "construction" [_,_,_]):xs) tag = taggedElement xs tag taggedElement ((Ast "call" [Avar "concatenate",x,y]):xs) tag = case (taggedElement (x:xs) tag,taggedElement (y:xs) tag) of (Just tx,Just ty) -> Just (tx++ty) _ -> Nothing taggedElement ((Astring _):xs) tag = taggedElement xs tag taggedElement ((Aint _):xs) tag = taggedElement xs tag taggedElement (e:xs) tag = Nothing taggedElement [] _ = Just [] findAttr :: String -> [Ast] -> Ast findAttr tag ((Ast "pair" [Astring a,v]):_) | a==tag || tag=="*" = v findAttr tag (_:xs) = findAttr tag xs findAttr _ [] = empty andAll :: [Ast] -> Ast andAll [x] = x andAll (x:xs) = foldl (\a r -> call "and" [a,r]) x xs occursContext :: Ast -> Int occursContext e = case e of Avar "." -> 1 Ast "let" _ -> 0 Ast "for" _ -> 0 Ast "call" [Avar "SQL",s,f,w] -> occursContext w Ast "descendant_any" (x:tags) -> occursContext x Ast step [tag,x] | elem step paths -> occursContext x Ast n xs -> sum (map occursContext xs) _ -> 0 substContext :: Ast -> Ast -> Ast substContext e b = case b of Avar "." -> e Ast "let" _ -> b Ast "for" _ -> b Ast "call" [Avar "SQL",s,f,w] -> Ast "call" [Avar "SQL",s,f,substContext e w] Ast "descendant_any" (x:tags) -> Ast "descendant_any" ((substContext e x):tags) Ast step [tag,x] | elem step paths -> Ast step [tag,substContext e x] Ast n xs -> Ast n (map (substContext e) xs) _ -> b occurs :: String -> Ast -> Int occurs v e = case e of Avar w | v==w -> 1 Ast "let" [Avar w,_,_] | v==w -> 0 Ast "for" [Avar w,Avar i,_,_] | v==w || v==i -> 0 Ast "call" [Avar "SQL",s,f,w] -> occurs v w Ast n xs -> sum (map (occurs v) xs) _ -> 0 subst :: String -> Ast -> Ast -> Ast subst v e b = case b of Avar w | v==w -> e Ast "let" [Avar w,_,_] | v==w -> b Ast "for" [Avar w,Avar i,_,_] | v==w || v==i -> b Ast "call" [Avar "SQL",s,f,w] -> Ast "call" [Avar "SQL",s,f,subst v e w] Ast n xs -> Ast n (map (subst v e) xs) _ -> b dependsOnPosition :: Bool -> Ast -> Bool dependsOnPosition contextp e = case e of Avar "." -> contextp Ast "call" [Avar "position"] -> True Ast "call" [Avar "last"] -> True Ast "call" ((Avar "step"):x:_) -> dependsOnPosition contextp x Ast _ xs -> any (dependsOnPosition contextp) xs _ -> False wellFormedPredicate :: Bool -> Ast -> Bool wellFormedPredicate contextp e = case e of Ast "call" ((Avar "step"):x:_) -> not (dependsOnPosition contextp x) Ast step xs | elem step paths || step == "descendant_any" -> not (any (dependsOnPosition contextp) xs) Ast "construction" xs -> not (any (dependsOnPosition contextp) xs) Ast "call" [Avar "not",x] -> not (dependsOnPosition contextp x) Ast "call" [Avar cmp,x,y] | any (\(f,_) -> f==cmp) (sqlComparisson++sqlBoolean) -> not (dependsOnPosition contextp x) && not (dependsOnPosition contextp y) _ -> False splitSqlPredicate :: [String] -> Ast -> Maybe(Ast,Ast) splitSqlPredicate tables (Ast "call" [Avar "and",p1,p2]) = case (splitSqlPredicate tables p1,splitSqlPredicate tables p2) of (Nothing,Nothing) -> Nothing (Nothing,Just(pp1,pp2)) -> Just(pp1,Ast "call" [Avar "and",p1,pp2]) (Just(pp1,pp2),Nothing) -> Just(pp1,Ast "call" [Avar "and",p2,pp2]) splitSqlPredicate tables pred | sqlPredicate tables pred = Just(pred,Ast "call" [Avar "true"]) splitSqlPredicate tables pred = Nothing -- Normalization normalize :: Ast -> Bool -> Int -> (Ast,Bool,Int) normalize exp changed count = case exp of Ast "step" [x] -> normalize x True count Ast "step" (x:(Ast "call" [Avar "true"]):xs) -> norm (Ast "step" (x:xs)) Ast "step" (x:(Ast "call" [Avar "false"]):xs) -> (empty,True,count) Ast "for" [v,i,Ast "call" [Avar "empty"],b] -> (empty,True,count) Ast "for" [v,i,s,Ast "call" [Avar "empty"]] -> (empty,True,count) Ast "descendant_any" ((Astring _):_) -> (empty,True,count) Ast "descendant_any" ((Aint _):_) -> (empty,True,count) Ast "descendant_any" ((Afloat _):_) -> (empty,True,count) Ast "descendant_any" ((Ast "call" [Avar "text",_]):_) -> (empty,True,count) Ast "descendant_any" ((Ast "call" [Avar "empty"]):_) -> (empty,True,count) Ast step [_,Astring _] | elem step paths -> (empty,True,count) Ast step [_,Aint _] | elem step paths -> (empty,True,count) Ast step [_,Afloat _] | elem step paths -> (empty,True,count) Ast step [_,Ast "call" [Avar "text",_]] | elem step paths -> (empty,True,count) Ast step [_,Ast "call" [Avar "empty"]] | elem step paths -> (empty,True,count) Ast "call" [Avar "and",Ast "call" [Avar "true"],x] -> norm x Ast "call" [Avar "and",x,Ast "call" [Avar "true"]] -> norm x -- (x,()) -> x Ast "call" [Avar "concatenate",x,Ast "call" [Avar "empty"]] -> norm x -- ((),x) -> x Ast "call" [Avar "concatenate",Ast "call" [Avar "empty"],x] -> norm x -- for $v1 in (for $v2 in s2 return b2) return b1 --> for $v2 in s2, for $v1 in b2 return b1 Ast "for" [v1,i1,Ast "for" [v2,i2,s2,b2],b1] -> norm (Ast "for" [v2,i2,s2,Ast "for" [v1,i1,b2,b1]]) -- (for $v in s return b)/tag --> for $v in s return b/tag --> Ast "descendant_any" ((Ast "for" [v,i,s,b]):tags) -> norm (Ast "for" [v,i,s,Ast "descendant_any" (b:tags)]) Ast step [tag,Ast "for" [v,i,s,b]] | elem step paths -> norm (Ast "for" [v,i,s,Ast step [tag,b]]) -- (x,y)/tag --> (x/tag,y/tag) Ast "descendant_any" ((Ast "call" [Avar "concatenate",x,y]):tags) -> norm (Ast "call" [Avar "concatenate",Ast "descendant_any" (x:tags),Ast "descendant_any" (y:tags)]) Ast step [tag,Ast "call" [Avar "concatenate",x,y]] | elem step paths -> norm (Ast "call" [Avar "concatenate",Ast step [tag,x],Ast step [tag,y]]) -- for $v in (x,y) return b --> (for $v in x return b,for $v in y return b) Ast "for" [v,i@(Avar "$"),Ast "call" [Avar "concatenate",x,y],b] -> norm (Ast "call" [Avar "concatenate",Ast "for" [v,i,x,b],Ast "for" [v,i,y,b]]) -- for $v in ... return b --> b[$v/(...)] Ast "for" [Avar v,Avar i,e,b] | case e of Ast "construction" _ -> True; Ast _ _ -> False; _ -> True -> norm (if i == "$" then subst v e b else subst v e (subst i (Aint 1) b)) --Ast "for" [Avar v,Avar i,Ast "predicate" [pred,e],b] -- -> norm (Ast "for" [Avar v,Avar i,e,Ast "predicate" [pred,b]]) Ast "for" [Avar v,Avar i,Ast "predicate" [pred,e],b] | occurs v pred == 0 && occurs i pred == 0 && occursContext pred == 0 -> norm (Ast "predicate" [pred,Ast "for" [Avar v,Avar i,e,b]]) -- unfold linear let Ast "let" [Avar v,e,b] | occurs v b < 2 -> norm (subst v e b) -- (if c then t else e)/A --> if c then t/A else e/A Ast "descendant_any" ((Ast "predicate" [c,e]):tags) | wellFormedPredicate True c -> norm (Ast "predicate" [c,Ast "descendant_any" (e:tags)]) Ast step [tag,Ast "predicate" [c,e]] | elem step paths && wellFormedPredicate True c -> norm (Ast "predicate" [c,Ast step [tag,e]]) -- if p doesn't depend on context: (e[p])/A --> (e/A)[p] Ast "descendant_any" ((Ast "step" (x:xs@(_:_))):tags) | all (wellFormedPredicate True) xs -> norm (Ast "step" ((Ast "descendant_any" (x:tags)):xs)) Ast step [tag,Ast "step" (x:xs@(_:_))] | elem step paths && all (wellFormedPredicate True) xs -> norm (Ast "step" ((Ast step [tag,x]):xs)) -- normalize predicate Ast "predicate" [pred,x] | occursContext pred > 0 -> let v = "x"++show count in normalize (Ast "for" [Avar v,Avar "$",x,Ast "predicate" [substContext (Avar v) pred,Avar v]]) True (count+1) Ast "step" [x,pred] | occursContext pred > 0 -> let v = "x"++show count in normalize (Ast "for" [Avar v,Avar "$",x,Ast "predicate" [substContext (Avar v) pred,Avar v]]) True (count+1) Ast "predicate" [p1,Ast "predicate" [p2,e]] -> norm (Ast "predicate" [Ast "call" [Avar "and",p1,p2],e]) Ast "predicate" [Ast "call"[Avar "false"],x] -> (empty,True,count) Ast "predicate" [Ast "call"[Avar "true"],x] -> (x,True,count) Ast "predicate" [x,Ast "call"[Avar "empty"]] -> (empty,True,count) Ast "step" ((Ast "call" [Avar "empty"]):xs) -> (empty,True,count) -- promote well-formed predicates; but note: (x,y)[1] <> (x[1],y[1]) Ast "step" ((Ast "call" [Avar "concatenate",x,y]):xs) | all (wellFormedPredicate False) xs -> norm (Ast "call" [Avar "concatenate",Ast "step" (x:xs),Ast "step" (y:xs)]) Ast "predicate" [pred,Ast "for" [v,i,s,b]] | wellFormedPredicate False pred -> norm (Ast "for" [v,i,s,Ast "predicate" [pred,b]]) Ast "step" ((Ast "for" [v,i,s,b]):xs) | all (wellFormedPredicate False) xs -> norm (Ast "for" [v,i,s,Ast "predicate" [andAll xs,b]]) Ast "step" (e@(Ast "construction" [_,_,_]):xs) -> if sum (map occursContext xs) > 0 then norm (Ast "predicate" [andAll (map (substContext e) xs),e]) else let (r,b,c) = foldr (\a (r,b,c) -> let (x,s,i) = normalize a b c in (x:r,s,i)) ([],changed,count) (e:xs) in (Ast "step" r,b,c) Ast "call" [Avar "=",x,y] | x == empty || y == empty -> (Ast "call"[Avar "true"],True,count) -- (.........)/tag --> ......... Ast "child_step" [Astring tag,Ast "construction" [_,_,Ast "append" x]] | taggedElement x tag /= Nothing -> case taggedElement x tag of Just [] -> (empty,True,count) Just s -> norm (concatenateAll s) Ast "child_step" [Astring tag,Ast "construction" [_,_,Ast "append" x]] -> norm (Ast "current_step" [Astring tag,concatenateAll x]) Ast "current_step" [Astring tag1,e@(Ast "construction" [Astring tag2,_,Ast "append" x])] -> if tag1 == tag2 || tag1 == "*" then norm e else (empty,True,count) -- (x)//tag --> (x,x//tag) Ast "descendant_any" (z@(Ast "construction" [Astring ctag,_,Ast "append" x]):tags) -> norm (Ast "call" [Avar "concatenate",z,Ast "descendant_any" ((concatenateAll x):tags)]) Ast "descendant_step" [Astring tag,z@(Ast "construction" [Astring ctag,_,Ast "append" x])] -> norm (if tag == ctag || tag == "*" then Ast "call" [Avar "concatenate",z,Ast "descendant_step" [Astring tag,concatenateAll x]] else Ast "descendant_step" [Astring tag,concatenateAll x]) -- (x)/@A --> s Ast "attribute_step" [Astring tag,Ast "construction" [ctag,Ast "attributes" as,x]] -> (findAttr tag as,True,count) -- (x)//@A --> (s,x//@A) Ast "attribute_descendant_step" [Astring tag,Ast "construction" [ctag,Ast "attributes" as,Ast "append" x]] -> norm (Ast "call" [Avar "concatenate",findAttr tag as, Ast "attribute_descendant_step" [Astring tag,concatenateAll x]]) -- SQL folding Ast "for" [Avar v1,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s1),Ast "call" ((Avar "from"):f1),pred1], Ast "for" [Avar v2,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s2),Ast "call" ((Avar "from"):f2),pred2],b]] | occurs v1 b == 0 -> norm (Ast "for" [Avar v2,Avar "$", Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):(s1++s2)), Ast "call" ((Avar "from"):(f1++f2)),Ast "call" [Avar "and",pred1,pred2]], b]) Ast "for" [Avar v,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s),Ast "call" ((Avar "from"):tables),pred1], Ast "predicate" [pred2,x]] | splitSqlPredicate [ v | Avar v <- tables ] pred2 /= Nothing -> let Just(pred3,pred4) = splitSqlPredicate [ v | Avar v <- tables ] pred2 in norm (Ast "for" [Avar v,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s), Ast "call" ((Avar "from"):tables),Ast "call" [Avar "and",pred1,pred3]], Ast "predicate" [pred4,x]]) Ast "for" [Avar v1,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s1),Ast "call" ((Avar "from"):f1),pred1], Ast "for" [Avar v2,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s2),Ast "call" ((Avar "from"):f2),pred2], Ast "predicate" [predd,b]]] | occurs v1 b == 0 && splitSqlPredicate [ v | Avar v <- f1 ] predd /= Nothing -> let Just(pred3,pred4) = splitSqlPredicate [ v | Avar v <- f1 ] predd in norm (Ast "for" [Avar v1,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s1), Ast "call" ((Avar "from"):f1),Ast "call" [Avar "and",pred1,pred3]], Ast "for" [Avar v2,Avar "$",Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):s2),Ast "call" ((Avar "from"):f2),pred2], Ast "predicate" [pred4,b]]]) -- default Ast n args -> let (r,b,c) = foldr (\a (r,b,c) -> let (x,s,i) = normalize a b c in (x:r,s,i)) ([],changed,count) args in (Ast n r,b,c) _ -> (exp,changed,count) where norm e = normalize e True count --tnorm e = trace ("*** "++show exp++"\n--> "++ppAst e) (normalize e True count) foldSQL :: Ast -> Ast foldSQL e = case e of Ast "call" [Avar "SQL",Ast "call" ((Avar "select"):cols),Ast "call" ((Avar "from"):tables),pred] -> let (sql,args) = makeSQL tables pred cols in Ast "call" [Avar "sql",Astring sql,concatenateAll args] Ast n args -> Ast n (map foldSQL args) _ -> e optimizeLoop :: Ast -> Int -> (Ast,Int) optimizeLoop e c = let (ne,b,c') = normalize e False c in if b then optimizeLoop ne c' else (ne,c) optimize :: Ast -> Ast optimize e = foldSQL (fst (optimizeLoop (simplify e) 0))