packages feed

HXQ-0.6: XQueryInterpreter.hs

{-------------------------------------------------------------------------------------
-
- The XQuery Interpreter
- Programmer: Leonidas Fegaras
- Email: fegaras@cse.uta.edu
- Web: http://lambda.uta.edu/
- Creation: 03/22/08, last update: 03/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.
-
--------------------------------------------------------------------------------------}


{-# OPTIONS_GHC -fth #-}

module XQueryInterpreter where

import List(sortBy)
import XMLParse(parseDocument)
import XQueryParser
import XQueryCompiler


-- system functions (=, concat, etc)
systemFunctions :: [(String,Int,[XSeq]->XSeq)]
systemFunctions = $(iFunctions)

-- XPath step functions (child, descendant, etc)
pathFunctions :: [(String,Tag->XTree->XSeq)]
pathFunctions = $(pFunctions)

-- run-time bindings of FLOWR variables
type Environment = [(String,XSeq)]

-- a user-defined function is (fname,parameters,body)
type Functions = [(String,[String],Ast)]


-- Each XPath predicate must calculate position() and last() from its input XSeq
-- if last() is used, then the evaluation is blocking (need to store the whole input XSeq)
applyPredicates :: [Ast] -> XSeq -> Environment -> Functions -> XSeq
applyPredicates [] xs _ _ = xs
applyPredicates (pred:preds) xs env fncs
    | containsLast pred         -- blocking: use only when last() is used in the predicate
    = let last = length xs
      in applyPredicates preds
             (foldir (\x i r -> if case eval pred [x] i last env fncs of
                                     [XInt k] -> k == i               -- indexing
                                     b -> conditionTest b
                                then x:r else r) [] xs 1) env fncs
applyPredicates (pred:preds) xs env fncs
    = applyPredicates preds
          (foldir (\x i r -> if case eval pred [x] i 0 env fncs of
                                  [XInt k] -> k == i               -- indexing
                                  b -> conditionTest b
                             then x:r else r) [] xs 1) env fncs


-- The XQuery interpreter
-- context: context node (XPath .)
-- position: the element position in the parent sequence (XPath position())
-- last: the length of the parent sequence (XPath last())
-- env: contains FLOWR variable bindings
-- fncs: user-defined functions
eval :: Ast -> XSeq -> Int -> Int -> Environment -> Functions -> XSeq
eval e context position last env fncs
  = case e of
      Avar "." -> context
      Avar v -> findV v env
      Aint n -> [ XInt n ]
      Afloat n -> [ XFloat n ]
      Astring s -> [ XText s ]
      Ast "doc" [Aint n] -> findV ("_doc"++(show n)) env
      Ast "context" [v,body] -> context
      Ast "call" [Avar "position"] -> [XInt position]
      Ast "call" [Avar "last"] -> [XInt last]
      Ast "step" [Ast path_step [Astring tag,body]]
          |  memV path_step pathFunctions
          -> foldr (\x r -> ((findV path_step pathFunctions) tag x)++r)
                   [] (eval body context position last env fncs)
      Ast "step" ((Ast path_step [Astring tag,body]):predicates)
          |  memV path_step pathFunctions
          -> foldr (\x r -> (applyPredicates predicates ((findV path_step pathFunctions) tag x) env fncs)++r)
                   [] (eval body context position last env fncs)
      Ast "step" [exp]
          -> eval exp context position last env fncs
      Ast "step" (exp:predicates)
          -> applyPredicates predicates (eval exp context position last env fncs) env fncs
      Ast "predicate" [condition,body]
          -> applyPredicates [condition] (eval body context position last env fncs) env fncs
      Ast "call" ((Avar fname):args)
          -> case filter (\(n,_,_) -> n == fname || ("fn:"++n) == fname) systemFunctions of
               [(_,len,f)] -> if (length args) == len
                              then f (map (\x -> eval x context position last env fncs) args)
                              else error ("Wrong number of arguments in system call: "++fname)
               _ -> case filter (\(n,_,_) -> n == fname) fncs of
                      [(_,params,body)] -> if (length params) == (length args)
                                           then eval body context 0 0 
                                                    ((zipWith (\p a -> (p,eval a context position last env fncs))
                                                              params args)++env) fncs
                                           else error ("Wrong number of arguments in function call: "++fname)
                      _ -> error ("Undefined function: "++fname)
      Ast "construction" [Astring tag,Ast "attributes" [],body]
          -> [ XElem tag [] 0 (eval body context position last env fncs) ]
      Ast "construction" [tag,Ast "attributes" al,body]
             -> let alc = map (\(Ast "pair" [a,v])
                                     -> let ac = eval a context position last env fncs
                                            vc = eval v context position last env fncs
                                        in (qName ac,showXS (text vc))) al
                    ct = eval tag context position last env fncs
                    bc = eval body context position last env fncs
                in [ XElem (qName ct) alc 0 bc ]
      Ast "let" [Avar var,source,body]
          -> eval body context position last ((var,eval source context position last env fncs):env) fncs
      Ast "for" [Avar var,Avar "$",source,body]      -- a for-loop without an index
          -> foldr (\a r -> (eval body [a] 0 0 ((var,[a]):env) fncs)++r)
                   [] (eval source context position last env fncs)
      Ast "for" [Avar var,Avar ivar,source,body]     -- a for-loop with an index
          -> foldir (\a i r -> (eval body [a] i 0 ((var,[a]):(ivar,[XInt i]):env) fncs)++r)
                    [] (eval source context position last env fncs) 1
      Ast "sortTuple" (exp:orderBys)             -- prepare each FLWOR tuple for sorting
          -> [ XElem "" [] 0 (foldl (\r a -> r++[XElem "" [] 0 (text (eval a context position last env fncs))])
                                    [XElem "" [] 0 (eval exp context position last env fncs)] orderBys) ]
      Ast "sort" (exp:ordList)
          -> let ce = map (\(XElem _ _ _ xs) -> map (\(XElem _ _ _ ys) -> ys) xs)
                          (eval exp context position last env fncs)
                 ordering = foldr (\(Avar ord) r (x:xs) (y:ys)
                                       -> case compareXSeqs (ord == "ascending") x y of
                                            EQ -> r xs ys
                                            o -> o)
                                  (\xs ys -> EQ) ordList
             in concatMap head (sortBy (\(_:xs) (_:ys) -> ordering xs ys) ce)
      _ -> error ("Illegal XQuery: "++(show e))


-- the XQuery interpreter
xquery :: String -> IO XSeq
xquery query
    = do let asts = parse (scan query)
             fncs = foldr (\e r -> case e of
                                     Ast "function" ((Avar f):b:args) -> (f,map (\(Avar v) -> v) args,optimize b):r
                                     _ -> r) [] asts
             vars = foldl (\r e -> case e of
                                     Ast "variable" [Avar v,u] -> (v,eval (optimize u) [] 0 0 r fncs):r
                                     _ -> r) [] asts
             (ast,_,ns) = getDocs (last asts) 0
         env <- foldr (\(n,Astring file) r -> do doc <- readFile file
                                                 env <- r
                                                 return (("_doc"++(show n),[materialize (parseDocument doc)]):env))
                      (return []) ns
         return (eval (optimize ast) [] 0 0 (env++vars) fncs)


-- Read an XQuery fom a file and run it
xfile :: String -> IO XSeq
xfile file = do query <- readFile file
                xquery query