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

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BangPatterns #-}


module Text.XML.HXQ.Functions where

import HXML(AttList)
import Data.List
import Char
import Text.XML.HXQ.XTree
import Text.XML.HXQ.Types
import Language.Haskell.TH(Q,Exp)
import Text.Regex
import Text.Regex.Base.RegexLike
import qualified GHC.Arr as A
import Debug.Trace


{--------------- XPath Steps ---------------------------------------------------------}


-- XPath step self /.
self_step :: QName -> XTree -> XSeq
self_step tag x
    = case x of
        XElem t _ _ _ _
            -> if matchQName t tag then [x] else []
        _ -> []


-- XPath step /tag or / *
child_step :: QName -> XTree -> XSeq
child_step tag x
    = case x of
        XElem _ _ _ _ bs
            -> foldr (\b s -> case b of
                                 XElem t _ _ _ _
                                     | matchQName t tag
                                     -> b:s
                                 _ -> s) [] bs
        _ -> []


-- XPath step descendant-or-self
descendant_or_self_step :: QName -> XTree -> XSeq
descendant_or_self_step tag (x@(XElem t _ _ _ cs))
    | matchQName t tag
    = x:(concatMap (descendant_or_self_step tag) cs)
descendant_or_self_step tag (XElem t _ _ _ cs)
    = concatMap (descendant_or_self_step tag) cs
descendant_or_self_step _ _ = []


-- XPath step //tag or // *
descendant_step :: QName -> XTree -> XSeq
descendant_step tag (XElem t _ _ _ cs)
    = concatMap (descendant_or_self_step tag) cs
descendant_step _ _ = []


-- It's like //* but has tagged children, which are derived statically
-- After examining 100 children it gives up: this avoids space leaks
descendant_any_with_tagged_children :: [QName] -> XTree -> XSeq
descendant_any_with_tagged_children tags (XElem t _ _ _ cs)
    = concatMap (ca tags) cs
      where ca tags (x@(XElem t _ _ _ cs))
                | all (\tag -> foldr (\b s -> case b of
                                                (XElem k _ _ _ _) -> s || k == tag
                                                _ -> s) False cs100) tags
                = x:(concatMap (ca tags) cs)
                where cs100 = take 100 cs
            ca tags (XElem t _ _ _ cs)
                = concatMap (ca tags) cs
            ca tags _ = []
descendant_any_with_tagged_children tags _ = []


-- XPath step /@attr or /@*
attribute_step :: QName -> XTree -> XSeq
attribute_step attr x
    = case x of
        XElem _ al _ _ _
            -> foldr (\(a,v) s -> if matchQName a attr
                                  then (XAttr a v):s
                                  else s) [] al
        _ -> []


-- XPath step //@attr or //@*
attribute_descendant_step :: QName -> XTree -> XSeq
attribute_descendant_step attr (x@(XElem _ al _ _ cs))
    = foldr (\(a,v) s -> if matchQName a attr
                         then (XAttr a v):s
                         else s)
            (concatMap (attribute_descendant_step attr) cs) al
attribute_descendant_step _ _ = []


-- XPath step parent /..
parent_step :: QName -> XTree -> XSeq
parent_step tag (XElem _ _ _ p _)
 = case p of
     XElem t _ _ _ _
         | matchQName t tag
         -> [p]
     _ -> []
parent_step _ _ = []


-- XPath step ancestor
ancestor_step :: QName -> XTree -> XSeq
ancestor_step tag (XElem _ _ _ p _)
 = case p of
     XElem t _ _ _ _
         -> if matchQName t tag
            then p:(ancestor_step tag p)
            else ancestor_step tag p
     _ -> []
ancestor_step _ _ = []


-- XPath step ancestor-or-self
ancestor_or_self_step :: QName -> XTree -> XSeq
ancestor_or_self_step tag e
 = case e of
     XElem t _ _ _ _
         -> if matchQName t tag
            then e:(ancestor_step tag e)
            else ancestor_step tag e
     _ -> []


-- XPath step following-sibling
following_sibling_step :: QName -> XTree -> XSeq
following_sibling_step tag (XElem _ _ order (XElem _ _ _ _ cs) _)
 = concatMap (self_step tag)
             (tail (dropWhile filter cs))
   where filter (XElem _ _ o _ _) = o /= order
         filter _ = True
following_sibling_step _ _ = []


-- XPath step following
following_step :: QName -> XTree -> XSeq
following_step tag (XElem _ _ order p _)
 = case p of
     XElem _ _ _ _ cs
         -> (concatMap (descendant_or_self_step tag)
                       (tail (dropWhile filter cs)))
            ++(following_step tag p)
            where filter (XElem _ _ o _ _) = o /= order
                  filter _ = True
     _ -> []
following_step _ _ = []


-- XPath step preceding-sibling
preceding_sibling_step :: QName -> XTree -> XSeq
preceding_sibling_step tag (XElem _ _ order (XElem _ _ _ _ cs) _)
 = concatMap (self_step tag)
             (takeWhile filter cs)
   where filter (XElem _ _ o _ _) = o /= order
         filter _ = True
preceding_sibling_step _ _ = []


-- XPath step preceding
preceding_step :: QName -> XTree -> XSeq
preceding_step tag (XElem _ _ order p _)
 = case p of
     XElem t _ _ _ cs
         -> (concatMap (descendant_or_self_step tag)
                       (takeWhile filter cs))
            ++(preceding_step tag p)
            where filter (XElem _ _ o _ _) = o /= order
                  filter _ = True
     _ -> []
preceding_step _ _ = []


-- XPath steps
paths :: [(String,Q Exp)]
paths = [ ( "child", [| child_step |] ),
          ( "descendant", [| descendant_step |] ),
          ( "attribute", [| attribute_step |] ),
          ( "self", [| self_step |] ),
          ( "descendant-or-self", [| descendant_or_self_step |] ),
          ( "attribute-descendant", [| attribute_descendant_step |] ),
          ( "following-sibling", [| following_sibling_step |] ),
          ( "following", [| following_step |] ),
          ( "parent", [| parent_step |] ),
          ( "ancestor", [| ancestor_step |] ),
          ( "preceding-sibling", [| preceding_sibling_step |] ),
          ( "preceding", [| preceding_step |] ),
          ( "ancestor-or-self", [| ancestor_or_self_step |] ) ]


-- XPath steps to be used by the interpreter
pathFunctions :: [(String,QName->XTree->XSeq)]
pathFunctions
    = [ ( "child", child_step ),
        ( "descendant", descendant_step ),
        ( "attribute", attribute_step ),
        ( "self", self_step ),
        ( "descendant-or-self", descendant_or_self_step ),
        ( "attribute-descendant", attribute_descendant_step ),
        ( "following-sibling", following_sibling_step ),
        ( "following", following_step ),
        ( "parent", parent_step ),
        ( "ancestor", ancestor_step ),
        ( "preceding-sibling", preceding_sibling_step ),
        ( "preceding", preceding_step ),
        ( "ancestor-or-self", ancestor_or_self_step ) ]


{------------ Functions --------------------------------------------------------------}


-- find the value of a variable in an association list
findV var ((n,b):_) | n==var = b
findV var (_:xs) = findV var xs
findV var _ = error ("Undefined variable: "++var)


-- is the variable defined in the association list?
memV var ((n,_):_) | n==var = True
memV var (_:xs) = memV var xs
memV _ _ = False


-- lazy: like foldr but with an index
foldir :: (a -> Int -> b -> b) -> b -> [a] -> Int -> b
{-# INLINE foldir #-}
{-# SPECIALIZE foldir :: (XTree -> Int -> XSeq -> XSeq) -> XSeq -> XSeq -> Int -> XSeq #-}
foldir c n [] i = n
foldir c n (x:xs) i = c x i (foldir c n xs $! (i+1))


-- strict: like foldl' but with an index
foldil :: (a -> Int -> b -> b) -> b -> [a] -> Int -> b
foldil c n [] i = n
foldil c n (x:xs) i = (foldil c $! (c x i n)) xs $! (i+1)


trueXT = XBool True
falseXT = XBool False


toBoolean :: Bool -> XSeq
toBoolean b = if b then [trueXT] else [falseXT]


-- data()
toData :: XSeq -> XSeq
toData xs
    = foldr (\x r -> case x of
                       XElem _ _ _ _ zs
                           -> (filter (\a -> case a of XText _ -> True; XInt _ -> True; XNull -> True; XAttr _ _ -> True;
                                                       XFloat _ -> True; XBool _ -> True; _ -> False) zs)++r
                       XAttr a v -> (XText v):r
                       XText _ -> x:r
                       XInt _ -> x:r
                       XFloat _ -> x:r
                       XBool _ -> x:r
                       _ -> r) [] xs


-- string()
string :: XSeq -> XSeq
string xs = foldr (\x r -> case x of
                             XElem _ _ _ _ zs -> (string zs)++r
                             XAttr _ v -> (XText v):r
                             XText _ -> x:r
                             XInt _ -> x:r
                             XFloat _ -> x:r
                             XBool _ -> x:r
                             _ -> r) [] xs


strings :: XSeq -> [String]
strings xs = map toString xs


getNames :: XSeq -> XSeq
getNames xs = foldr (\x r -> case x of 
                               XElem tag _ _ _ _ -> (XText $ show tag):r
                               XAttr tag _ -> (XText $ show tag):r
                               _ -> r) [] xs


-- concatenate text with no padding (for element content)
appendText :: [XSeq] -> XSeq
appendText [] = []
appendText [x] = x
appendText (x:xs) = x++(XNoPad:(appendText xs))


substring_before :: String -> String -> String
substring_before xs ys
    = s xs ys []
      where s xs ys c | isPrefixOf ys xs = c
            s (x:xs) ys c = s xs ys (c++[x])
            s _ _ _ = []


substring_after :: String -> String -> String
substring_after xs ys
    = s xs ys
      where s xs ys | isPrefixOf ys xs = drop (length ys) xs
            s (_:xs) ys = s xs ys
            s _ _ = []


replaceString :: String -> String -> [(Int,Int)] -> String
replaceString from with indexes
    = rs from indexes 0 ""
      where rs "" _ _ s = s
            rs fs [] _ s = s++fs
            rs fs ((i,l):is) j s
                | i==j
                = rs (drop l fs) is (j+l) (s++with)
            rs (f:fs) is j s
                = rs fs is (j+1) (s++[f])


translate_string :: String -> String -> String -> String
translate_string xs from to
    = foldr (\c r -> case elemIndex c from of
                       Just i -> if i < length to
                                 then (to !! i):r
                                 else r
                       _ -> c:r) "" xs


toNums :: XSeq -> XSeq
toNums xs = case mapM toNum xs of Just x -> x; _ -> []


getFloat :: XTree -> Double
getFloat x = case toFloat x of
               Just (XFloat n) -> n
               _ -> error("Cannot convert to a double: "++show x)


-- strict: average value
mean :: [Double] -> Double
mean = uncurry (/) . foldl' (\(!s, !n) x -> (s+x, n+1)) (0,0.0)


sumSeq :: XSeq -> XSeq
sumSeq xs
    = case xs of
        XInt _:_ -> [ XInt (foldl' (+) 0 [ n | XInt n <- xs ]) ]
        XFloat _:_ -> [ XFloat (foldl' (+) 0.0 [ n | XFloat n <- xs ]) ]
        _ -> []


maxSeq :: XSeq -> XSeq
maxSeq [] = []
maxSeq xs = [ maximumBy compareXTrees xs ]


minSeq :: XSeq -> XSeq
minSeq [] = []
minSeq xs = [ minimumBy compareXTrees xs ]


contains :: String -> String -> Bool
contains text word = isInfixOf word text


-- lazy: remove duplicates
distinct :: Eq a => [a] -> [a]
distinct xs
    = rd xs []
      where rd (x:xs) r = if elem x r then rd xs r else x:(rd xs $! (x:r))
            rd [] _ = []


modulo :: Double -> Double -> Double
modulo x y = x - fromIntegral(floor(x/y))*y


arithmetic :: (Double -> Double -> Double) -> XTree -> XTree -> XTree
arithmetic op (XInt n) (XInt m) = XInt (floor (op (fromIntegral n) (fromIntegral m)))
arithmetic op (XFloat n) (XFloat m) = XFloat (op n m)
arithmetic op (XFloat n) (XInt m) = XFloat (op n (fromIntegral m))
arithmetic op (XInt n) (XFloat m) = XFloat (op (fromIntegral n) m)


compareXTrees :: XTree -> XTree -> Ordering
compareXTrees (XElem _ _ _ _ _) _ = EQ
compareXTrees _ (XElem _ _ _ _ _) = EQ
compareXTrees (XInt n) (XInt m) = compare n m
compareXTrees (XFloat n) (XInt m) = compare n (fromIntegral m)
compareXTrees (XInt n) (XFloat m) = compare (fromIntegral n) m
compareXTrees (XFloat n) (XFloat m) = compare n m
compareXTrees (XText n) (XText m) = compare n m
compareXTrees (XBool n) (XBool m) = compare n m
compareXTrees x y = compare (toString x) (toString y)


strictCompareOne [XInt n] [XInt m] = compare n m
strictCompareOne [XFloat n] [XFloat m] = compare n m
strictCompareOne [XFloat n] [XInt m] = compare n (fromIntegral m)
strictCompareOne [XInt n] [XFloat m] = compare (fromIntegral n) m
strictCompareOne [XText n] [XText m] = compare n m
strictCompareOne [XBool n] [XBool m] = compare n m
strictCompareOne x y = error ("Illegal operands in strict comparison: "++(show x)++" "++(show y))

strictCompare :: XSeq -> XSeq -> Ordering
strictCompare [XElem _ _ _ _ x] [XElem _ _ _ _ y] = strictCompareOne x y
strictCompare x [XElem _ _ _ _ y] = strictCompareOne x y
strictCompare [XElem _ _ _ _ x] y = strictCompareOne x y
strictCompare x y = strictCompareOne x y


compareXSeqs :: Bool -> XSeq -> XSeq -> Ordering
compareXSeqs ord xs ys
    = let comps = [ compareXTrees x y | x <- xs, y <- ys ]
      in if ord
            then if all (\x -> x == LT) comps
                    then LT
                 else if all (\x -> x == GT) comps
                    then GT
                 else EQ
         else if all (\x -> x == LT) comps
                 then GT
              else if all (\x -> x == GT) comps
                 then LT
              else EQ


deep_equal :: XTree -> XTree -> Bool
deep_equal (XElem t1 a1 _ _ xs1) (XElem t2 a2 _ _ xs2)
    = t1 == t2 && sort a1 == a2 && (and $ zipWith deep_equal xs1 xs2)
deep_equal (XElem _ _ _ _ _) _ = False
deep_equal _ (XElem _ _ _ _ _) = False
deep_equal x y = x == y


conditionTest :: XSeq -> Bool
conditionTest [] = False
conditionTest [XText ""] = False
conditionTest [XInt 0] = False
conditionTest [XBool False] = False
conditionTest _ = True


-- lazy indexing (similar to !!)
index :: [a] -> Int -> [a]
index [] n = []
index (x:xs) 0 = [x]
index (_:xs) n = index xs $! (n-1)


system_functions :: [String]
system_functions = map (\(nm,_,_,_,_) -> nm) systemFunctions


-- System functions
systemFunctions :: [(String,                -- function name
                     Int,                   -- arity (-1 means any number of arguments)
                     [Type],                -- output_type:input_types
                     [XSeq]->XSeq,          -- function to be used by the interpreter
                     [Q Exp] -> Q Exp)]     -- function to be used by the compiler
systemFunctions
    = [
--  general comparisons
        ( "=", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ compareXTrees x y == EQ | x <- toData xs, y <- toData ys ],
          \[xs,ys] -> [| toBoolean $ or [ compareXTrees x y == EQ | x <- toData $xs, y <- toData $ys ] |] ),
        ( "!=", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ strictCompare xs ys /= EQ,
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys /= EQ |] ),
        ( ">", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ compareXTrees x y == GT | x <- toData xs, y <- toData ys ],
          \[xs,ys] -> [| toBoolean $ or [ compareXTrees x y == GT | x <- toData $xs, y <- toData $ys ] |] ),
        ( "<", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ compareXTrees x y == LT | x <- toData xs, y <- toData ys ],
          \[xs,ys] -> [| toBoolean $ or [ compareXTrees x y == LT | x <- toData $xs, y <- toData $ys ] |] ),
        ( ">=", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ compareXTrees x y `elem` [GT,EQ] | x <- toData xs, y <- toData ys ],
          \[xs,ys] -> [| toBoolean $ or [ compareXTrees x y `elem` [GT,EQ] | x <- toData $xs, y <- toData $ys ] |] ),
        ( "<=", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ compareXTrees x y `elem` [LT,EQ] | x <- toData xs, y <- toData ys ],
          \[xs,ys] -> [| toBoolean $ or [ compareXTrees x y `elem` [LT,EQ] | x <- toData $xs, y <- toData $ys ] |] ),
        ( "deep-equal", 2, [tBool,tStar TAny,tStar TAny],
          \[xs,ys] -> toBoolean $ or [ deep_equal x y | x <- xs, y <- ys ],
          \[xs,ys] -> [| toBoolean $ or [ deep_equal x y | x <- $xs, y <- $ys ] |] ),
--  strict comparisons
        ( "eq", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys == EQ,
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys == EQ |] ),
        ( "neq", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys /= EQ,
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys /= EQ |] ),
        ( "lt", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys == LT,
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys == LT |] ),
        ( "gt", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys == GT,
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys == GT |] ),
        ( "le", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys `elem` [LT,EQ],
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys `elem` [LT,EQ] |] ),
        ( "ge", 2, [tBool,tAtomic,tAtomic],
          \[xs,ys] -> toBoolean $ strictCompare xs ys `elem` [GT,EQ],
          \[xs,ys] -> [| toBoolean $ strictCompare $xs $ys `elem` [GT,EQ] |] ),
-- document order comparisons
        ( "<<", 2, [tBool,tNode,tNode],
          \[xs,ys] -> toBoolean $ or [ ox < oy | XElem _ _ ox _ _ <- xs, XElem _ _ oy _ _ <- ys ],
          \[xs,ys] -> [| toBoolean $ or [ ox < oy | XElem _ _ ox _ _ <- $xs, XElem _ _ oy _ _ <- $ys ] |] ),
        ( ">>", 2, [tBool,tNode,tNode],
          \[xs,ys] -> toBoolean $ or [ ox > oy | XElem _ _ ox _ _ <- xs, XElem _ _ oy _ _ <- ys ],
          \[xs,ys] -> [| toBoolean $ or [ ox > oy | XElem _ _ ox _ _ <- $xs, XElem _ _ oy _ _ <- $ys ] |] ),
        ( "is", 2, [tBool,tNode,tNode],
          \[xs,ys] -> toBoolean $ or [ ox == oy | XElem _ _ ox _ _ <- xs, XElem _ _ oy _ _ <- ys ],
          \[xs,ys] -> [| toBoolean $ or [ ox == oy | XElem _ _ ox _ _ <- $xs, XElem _ _ oy _ _ <- $ys ] |] ),
-- arithmetic operations
        ( "+", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ arithmetic (+) x y | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ arithmetic (+) x y | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "-", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ arithmetic (-) x y | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ arithmetic (-) x y | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "*", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ arithmetic (*) x y | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ arithmetic (*) x y | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "div", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ arithmetic (/) x y | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ arithmetic (/) x y | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "idiv", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ case arithmetic (/) x y of XFloat n -> XInt (floor n); x -> x
                      | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ case arithmetic (/) x y of XFloat n -> XInt (floor n); x -> x
                         | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "mod", 2, [tNumeric,tNumeric,tNumeric],
          \[xs,ys] -> [ arithmetic modulo x y | x <- toNums xs, y <- toNums ys ],
          \[xs,ys] -> [| [ arithmetic modulo x y | x <- toNums $xs, y <- toNums $ys ] |] ),
        ( "uplus", 1, [tNumeric,tNumeric],
          \[xs] -> [ x | x <- toNums xs ],
          \[xs] -> [| [ x | x <- toNums $xs ] |] ),
        ( "uminus", 1, [tNumeric,tNumeric],
          \[xs] -> [ case x of XInt n -> XInt (-n); XFloat n -> XFloat (-n) | x <- toNums xs ],
          \[xs] -> [| [ case x of XInt n -> XInt (-n); XFloat n -> XFloat (-n) | x <- toNums $xs ] |] ),
        ( "abs", 1, [tNumeric,tNumeric],
          \[xs] -> [ case x of XInt n -> XInt (abs n); XFloat n -> XFloat (abs n) | x <- toNums xs ],
          \[xs] -> [| [ case x of XInt n -> XInt (abs n); XFloat n -> XFloat (abs n) | x <- toNums $xs ] |] ),
        ( "ceiling", 1, [tNumeric,tNumeric],
          \[xs] -> [ case x of XInt n -> XInt n; XFloat n -> XInt (ceiling n) | x <- toNums xs ],
          \[xs] -> [| [ case x of XInt n -> XInt n; XFloat n -> XInt (ceiling n) | x <- toNums $xs ] |] ),
        ( "round", 1, [tNumeric,tNumeric],
          \[xs] -> [ case x of XInt n -> XInt n; XFloat n -> XInt (round n) | x <- toNums xs ],
          \[xs] -> [| [ case x of XInt n -> XInt n; XFloat n -> XInt (round n) | x <- toNums $xs ] |] ),
        ( "floor", 1, [tNumeric,tNumeric],
          \[xs] -> [ case x of XInt n -> XInt n; XFloat n -> XInt (floor n) | x <- toNums xs ],
          \[xs] -> [| [ case x of XInt n -> XInt n; XFloat n -> XInt (floor n) | x <- toNums $xs ] |] ),
-- boolean operations
        ( "true", 0, [tBool],
          \[] -> [trueXT],
          \[] -> [| [trueXT] |] ),
        ( "false", 0, [tBool],
          \[] -> [falseXT],
          \[] -> [| [falseXT] |] ),
        ( "if", 3, [tBool,TAny,TAny],
          \[cs,xs,ys] -> if conditionTest cs then xs else ys,
          \[cs,xs,ys] -> [| if conditionTest $cs then $xs else $ys |] ),
        ( "and", 2, [tBool,tBool,tBool],
          \[xs,ys] -> toBoolean $ (conditionTest xs) && (conditionTest ys),
          \[xs,ys] -> [| toBoolean $ (conditionTest $xs) && (conditionTest $ys) |] ),
        ( "or", 2, [tBool,tBool,tBool],
          \[xs,ys] -> toBoolean $ (conditionTest xs) || (conditionTest ys),
          \[xs,ys] -> [| toBoolean $ (conditionTest $xs) || (conditionTest $ys) |] ),
        ( "not", 1, [tBool,tBool],
          \[xs] -> toBoolean $ not $ conditionTest xs,
          \[xs] -> [| toBoolean $ not $ conditionTest $xs |] ),
        ( "some", 1, [tBool,tStar TAny],
          \[xs] -> toBoolean $ or [ conditionTest [x] | x <- xs ],
          \[xs] -> [| toBoolean $ or [ conditionTest [x] | x <- $xs ] |] ),
-- aggregations
        ( "count", 1, [tInt,tStar TAny],
          \[xs] -> [ XInt (length xs) ],
          \[xs] -> [| [ XInt (length $xs) ] |] ),
        ( "sum", 1, [tNumeric,tStar tNumeric],
          \[xs] -> sumSeq xs,
          \[xs] -> [| sumSeq $xs |] ),
        ( "avg", 1, [tNumeric,tStar tNumeric],
          \[xs] -> [ XFloat (mean [ getFloat x | x <- toNums xs ]) ],
          \[xs] -> [| [ XFloat (mean [ getFloat x | x <- toNums $xs ]) ] |] ),
        ( "min", 1, [tNumeric,tStar tNumeric],
          \[xs] -> minSeq xs,
          \[xs] -> [| minSeq $xs |] ),
        ( "max", 1, [tNumeric,tStar tNumeric],
          \[xs] -> maxSeq xs,
          \[xs] -> [| maxSeq $xs |] ),
-- string operations
        ( "concat", -1, [tString],
          \ss -> [ XText $ foldr (\s r -> concat [ x | x <- strings s ] ++ r) "" ss ],
          \ss -> [| [ XText $ $(foldr (\s r -> [| concat [ x | x <- strings $s ] ++ $r |]) [| "" |] ss) ] |] ),
        ( "substring", 3, [tString,tString,tNumeric,tNumeric],
          \[xs,n1,n2] -> [ XText (take m2 (drop (m1-1) x))
                         | x <- strings xs, XInt m1 <- toNums n1, XInt m2 <- toNums n2 ],
          \[xs,n1,n2] -> [| [ XText (take m2 (drop (m1-1) x))
                            | x <- strings $xs, XInt m1 <- toNums $n1, XInt m2 <- toNums $n2 ] |] ),
        ( "substring", 2, [tString,tString,tNumeric],
          \[xs,n] -> [ XText (drop (m-1) x) | x <- strings xs, XInt m <- toNums n ],
          \[xs,n] -> [| [ XText (drop (m-1) x) | x <- strings $xs, XInt m <- toNums $n ] |] ),
        ( "substring-before", 2, [tString,tString,tString],
          \[xs,ys] -> [ XText (substring_before x y)  | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| [ XText (substring_before x y)  | x <- strings $xs, y <- strings $ys ] |] ),
        ( "substring-after", 2, [tString,tString,tString],
          \[xs,ys] -> [ XText (substring_after x y)  | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| [ XText (substring_after x y)  | x <- strings $xs, y <- strings $ys ] |] ),
        ( "starts-with", 2, [tString,tString,tString],
          \[xs,ys] -> toBoolean $ or [ x == "" || isPrefixOf y x  | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| toBoolean $ or [ x == "" || isPrefixOf y x  | x <- strings $xs, y <- strings $ys ] |] ),
        ( "ends-with", 2, [tString,tString,tString],
          \[xs,ys] -> toBoolean $ or [ x == "" || isSuffixOf y x  | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| toBoolean $ or [ x == "" || isSuffixOf y x  | x <- strings $xs, y <- strings $ys ] |] ),
        ( "string-join", 2, [tString,tString,tString],
          \[xs,ys] -> [ XText $ intercalate y (strings xs) | y <- strings ys ],
          \[xs,ys] -> [| [ XText $ intercalate y (strings $xs) | y <- strings $ys ] |] ),
        ( "string-length", 1, [tInt,tString],
          \[xs] -> [ XInt $ length x | x <- strings xs ],
          \[xs] -> [| [ XInt $ length x | x <- strings $xs ] |] ),
        ( "translate", 3, [tString,tString,tString,tString],
          \[xs,ys,zs] -> [ XText $ translate_string x y z  | x <- strings xs, y <- strings ys, z <- strings zs ],
          \[xs,ys,zs] -> [| [ XText $ translate_string x y z  | x <- strings $xs, y <- strings $ys, z <- strings $zs ] |] ),
        ( "matches", 2, [tBool,tString,tString],
          \[xs,ys] -> toBoolean $ or [ matchTest (mkRegex y) x | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| toBoolean $ or [ matchTest (mkRegex y) x | x <- strings $xs, y <- strings $ys ] |] ),
        ( "replace", 3, [tString,tString,tString,tString],
          \[xs,ys,zs] -> [ XText $ replaceString x z (map snd $ concatMap A.elems $ matchAllText (mkRegex y) x)
                         | x <- strings xs, y <- strings ys, z <- strings zs ],
          \[xs,ys,zs] -> [| [ XText $ replaceString x z (map snd $ concatMap A.elems $ matchAllText (mkRegex y) x)
                            | x <- strings $xs, y <- strings $ys, z <- strings $zs ] |] ),
        ( "contains", 2, [tBool,tString,tString],
          \[xs,text] -> toBoolean $ or [ contains x t | x <- strings xs, t <- strings text ],
          \[xs,text] -> [| toBoolean $ or [ contains x t | x <- strings $xs, t <- strings $text ] |] ),
        ( "compare", 2, [tInt,tString,tString],
          \[xs,ys] -> [ XInt $ case compare x y of EQ -> 0; LT -> -1; GT -> 1 | x <- strings xs, y <- strings ys ],
          \[xs,ys] -> [| [ XInt $ case compare x y of EQ -> 0; LT -> -1; GT -> 1 | x <- strings $xs, y <- strings $ys ] |] ),
        ( "upper-case", 1, [tString,tString],
          \[xs] -> [ XText $ map toUpper x | x <- strings xs ],
          \[xs] -> [| [ XText $ map toUpper x | x <- strings $xs ] |] ),
        ( "lower-case", 1, [tString,tString],
          \[xs] -> [ XText $ map toLower x | x <- strings xs ],
          \[xs] -> [| [ XText $ map toLower x | x <- strings $xs ] |] ),
        ( "normalize-space", 1, [tString,tString],
          \[xs] -> [ XText $ unwords $ words x  | x <- strings xs ],
          \[xs] -> [| [ XText $ unwords $ words x  | x <- strings $xs ] |] ),
-- sequence operations
        ( "empty", 0, [TAny],
          \[] -> [],
          \[] -> [| [] |] ),
        ( "empty", 1, [tBool,tStar TAny],
          \[xs] -> toBoolean $ null xs,
          \[xs] -> [| toBoolean $ null $xs |] ),
        ( "exists", 1, [tBool,tStar TAny],
          \[xs] -> toBoolean $ (not (null xs)),
          \[xs] -> [| toBoolean $ (not (null $xs)) |] ),
        ( "to", 2, [tStar tInt,tInt,tInt],
          \[xs,ys] -> [ XInt i | XInt n <- toNums xs, XInt m <- toNums ys, i <- [n..m] ],
          \[xs,ys] -> [| [ XInt i | XInt n <- toNums $xs, XInt m <- toNums $ys, i <- [n..m] ] |] ),
        ( "concatenate", 2, [tStar TAny,tStar TAny,tStar TAny],
          \[xs,ys] -> xs ++ ys,
          \[xs,ys] -> [| $xs ++ $ys |] ),
        ( "distinct-values", 1, [tStar TAny,tStar TAny],
          \[xs] -> distinct xs,
          \[xs] -> [| distinct $xs |] ),
        ( "union", 2, [tStar TAny,tStar TAny,tStar TAny],
          \[xs,ys] -> distinct (xs ++ ys),
          \[xs,ys] -> [| distinct ($xs ++ $ys) |] ),
        ( "intersect", 2, [tStar TAny,tStar TAny,tStar TAny],
          \[xs,ys] -> intersect xs ys,
          \[xs,ys] -> [| intersect $xs $ys |] ),
        ( "except", 2, [tStar TAny,tStar TAny,tStar TAny],
          \[xs,ys] -> xs \\ ys,
          \[xs,ys] -> [| $xs \\ $ys |] ),
        ( "reverse", 1, [tStar TAny,tStar TAny],
          \[xs] -> reverse xs,
          \[xs] -> [| reverse $xs |] ),
        ( "subsequence", 2, [tStar TAny,tStar TAny,tInt],
          \[xs,n] -> concat [ drop (m-1) xs | XInt m <- toNums n ],
          \[xs,n] -> [| concat [ drop (m-1) $xs | XInt m <- toNums $n ] |] ),
        ( "subsequence", 3, [tStar TAny,tStar TAny,tInt,tInt],
          \[xs,n1,n2] -> concat [ take m2 (drop (m1-1) xs) | XInt m1 <- toNums n1, XInt m2 <- toNums n2 ],
          \[xs,n1,n2] -> [| concat [ take m2 (drop (m1-1) $xs) | XInt m1 <- toNums $n1, XInt m2 <- toNums $n2 ] |] ),
        ( "insert-before", 3, [tStar TAny,tStar TAny,tStar TAny,tInt],
          \[xs,n,ys] -> concat [ (take (i-1) xs)++ys++(drop (i-1) xs) | XInt i <- toNums n ],
          \[xs,n,ys] -> [| let x = $xs in concat [ (take (i-1) x) ++ $ys ++ (drop (i-1) x) | XInt i <- toNums $n ] |] ),
        ( "index-of", 2, [tInt,tStar TAny,TAny,tInt],
          \[xs,ys] -> [ XInt (i+1) | y <- ys, i <- elemIndices y xs ],
          \[xs,ys] -> [| [ XInt (i+1) | y <- $ys, i <- elemIndices y $xs ] |] ),
        ( "remove", 2, [tStar TAny,tStar TAny,tInt],
          \[xs,ys] -> concat [ (take (i-1) xs)++(drop i xs) | XInt i <- toNums ys ],
          \[xs,ys] -> [| let x = $xs in concat [ (take (i-1) x)++(drop i x) | XInt i <- toNums $ys ] |] ),
-- type testing and casting
        ( "data", 1, [TAny,TAny],
          \[xs] -> toData xs,
          \[xs] -> [| toData $xs |] ),
        ( "text", 1, [tString,tNode],
          \[xs] -> [ w | XElem _ _ _ _ ts <- xs, w <- toData ts ],
          \[xs] -> [| [ w | XElem _ _ _ _ ts <- $xs, w <- toData ts ] |] ),
        ( "string", 1, [tString,TAny],
          \[xs] -> string xs,
          \[xs] -> [| string $xs |] ),
        ( "name", 1, [tString,TAny],
          \[xs] -> getNames xs,
          \[xs] -> [| getNames $xs |] ),
        ( "local-name", 1, [tString,TAny],
          \[xs] -> getNames xs,
          \[xs] -> [| getNames $xs |] ),
        ( "number", 1, [tNumeric,TAny],
          \[xs] -> toNums xs,
          \[xs] -> [| toNums $xs |] ),
        ( "boolean", 1, [tBool,TAny],
          \[xs] -> toBoolean $ or [ conditionTest [x] | x <- xs ],
          \[xs] -> [| toBoolean $ or [ conditionTest [x] | x <- $xs ] |] ),
        ( "node", 1, [tNode,TAny],
          \[xs] -> [ w | XElem _ _ _ _ ts <- xs, w <- ts ],
          \[xs] -> [| [ w | XElem _ _ _ _ ts <- $xs, w <- ts ] |] ),
        ( "instance-of", 2, [tBool,TAny,TAny],
          \[e,[XType tp]] -> [ XBool $ instanceOf e tp ],
          \[e,tp] -> [| case $tp of [XType t] -> [ XBool $ instanceOf $e t ] |] ),
        ( "cast-as", 2, [TAny,TAny,TAny],
          \[e,[XType tp]] -> castAs e tp,
          \[e,tp] -> [| case $tp of [XType t] -> castAs $e t |] ),
        ( "castable-as", 2, [tBool,TAny,TAny],
          \[e,[XType tp]] -> [ XBool $ castableAs e tp ],
          \[e,tp] -> [| case $tp of [XType t] -> [ XBool $ castableAs $e t ] |] ),
-- miscellaneous operations
        ( "trace", 2, [TAny,TAny,TAny],
          \[xs,ys] -> trace ("*** trace: "++show xs) ys,
          \[xs,ys] -> [| trace ("*** trace: "++show $xs) $ys |] ),
        ( "debug", 1, [TAny,TAny],
          \_ -> error "the debug() call must be handled separately",
          \_ -> error "Debugging is not permitted at compile-time." ),
        ( "error", 0, [TAny],
          \[] -> error "XQuery error" ,
          \[] -> [| error "XQuery error" |] ),
        ( "error", 2, [TAny,TAny,TAny],
          \[xs,ys] -> error (showsXS xs (": "++show ys)),
          \[xs,ys] -> [| error (showsXS $xs (": "++show $ys)) |] ),
        ( "last", 0, [tInt],
          \[] -> error "the 'last()' call must be handled separately",
          \[] -> error "the 'last()' call must be handled separately" ),
        ( "position", 0, [tInt],
          \[] -> error "the 'position()' call must be handled separately",
          \[] -> error "the 'position()' call must be handled separately" )
   ]