packages feed

HXQ-0.5: 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/25/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


systemFunctions :: [(String,Int,[XSeq]->XSeq)]
systemFunctions = $(iFunctions)

type Environment = [(String,XSeq)]

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


findV var env
  = case filter (\(n,_) -> n==var) env of
      (_,b):_ -> b
      _ -> error ("Undefined variable: "++var)


eval :: Ast -> Environment -> Functions -> XSeq
eval e env fncs
  = case e of
      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 "call" [Avar "position"] -> findV "_position" env
      Ast "call" [Avar "last"] -> findV "_last" env
      Ast "call" ((Avar fname):args)
             -> case filter (\(n,_,_) -> n==fname) systemFunctions of
                  [(_,len,f)] -> if (length args) == len
                                    then f (map (\x -> eval x 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 ((zipWith (\p a -> (p,eval a 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" al,body]
             -> let alc = map (\p -> case p of
                                       Ast "pair" [Astring a,Astring v]
                                           -> (a,v)
                                       Ast "pair" [Astring a,v]
                                           -> (a,showXS (text (eval v env fncs)))) al
                in [ XElem tag alc (eval body env fncs) ]
      Ast "predicate" [condition,body]
             |  containsLast condition      -- blocking: use only when last() is used in condition
             -> let bl = eval body env fncs
                    len = length bl
                in foldir (\x i r -> case eval condition ((".",[ x ]):("_position",[ XInt i ])
                                                                 :("_last",[ XInt len ]):env) fncs of
                                       [] -> r
                                       [XInt n] -> if i==n then x:r else r    -- indexing
                                       _ -> x:r) [] bl 1
      Ast "predicate" [condition,body]      -- non-blocking
             -> foldir (\x i r -> case eval condition ((".",[ x ]):("_position",[ XInt i ]):env) fncs of
                                    [] -> r
                                    [XInt n] -> if i==n then x:r else r    -- indexing
                                    _ -> x:r) [] (eval body env fncs) 1
      Ast "let" [Avar var,source,body]
             -> eval body ((var,eval source env fncs):env) fncs
      Ast "for" [Avar var,Avar "$",source,body]      -- a for-loop without an index
             -> foldr (\a r -> (eval body ((var,[a]):env) fncs)++r) [] (eval source env fncs)
      Ast "for" [Avar var,Avar ivar,source,body]     -- a for-loop with an index
             -> foldir (\a i r -> (eval body ((var,[a]):(ivar,[XInt i]):env) fncs)++r)
                       [] (eval source env fncs) 1
      Ast "sortTuple" (exp:orderBys)             -- prepare each FLWOR tuple for sorting
             -> [ XElem "" [] (foldl (\r a -> r++[XElem "" [] (text (eval a env fncs))])
                                     [XElem "" [] (eval exp env fncs)] orderBys) ]
      Ast "sort" (exp:ordList)
             -> let ce = map (\(XElem "" [] xs) -> map (\(XElem "" [] ys) -> ys) xs) (eval exp 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 "define" ((Avar f):b:args) -> (f,map (\(Avar v) -> v) args,optimize b):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) env fncs)


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