packages feed

multifocal-0.0.1: src/Language/XPath/XPath2Pf.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Language.XPath.XPath2Pf
-- Copyright   :  (c) 2011 University of Minho
-- License     :  BSD3
--
-- Maintainer  :  hpacheco@di.uminho.pt
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Multifocal:
-- Bidirectional Two-level Transformation of XML Schemas
-- 
-- Translation from XPath expressions into point-free function representations.
--
-----------------------------------------------------------------------------

module Language.XPath.XPath2Pf where

import Data.Type
import Data.Pf
import Data.Spine
import Generics.Pointless.Functors hiding (fmap)
import Language.XPath.HXTAliases

import Text.XML.HXT.XPath as HXT hiding (parseXPath)
import Text.XML.HXT.XPath.XPathDataTypes as XPath
import Text.XML.HXT.DOM.QualifiedName
import Control.Monad
import Data.Monoid

type XPathQ = Q [Dynamic]

xpath2pf :: MonadPlus m => XPath -> m (Pf XPathQ)
xpath2pf = expr2pf . relativeExpr

-- | Converts top-level absolute paths to relative paths
relativeExpr :: XPath -> XPath
relativeExpr (GenExpr op exprs) = GenExpr op (map relativeExpr exprs)
relativeExpr (PathExpr expr path) = PathExpr (fmap relativeExpr expr) (fmap relativeLocPath path)
relativeExpr (FilterExpr exprs) = FilterExpr (map relativeExpr exprs)
relativeExpr (FctExpr name exprs) = FctExpr name (map relativeExpr exprs)
relativeExpr xp = xp
relativeLocPath (LocPath Abs steps) = LocPath Rel steps
relativeLocPath (LocPath Rel steps) = LocPath Rel steps

expr2pf :: MonadPlus m => XPath -> m (Pf XPathQ)
expr2pf e@(GenExpr op lexp) | isBoolOp op = do
	pf <- xpath2boolpf e
	return $ mkXPathQ Bool pf
expr2pf e@(GenExpr op lexp) | isNumOp op = do
	pf <- xpath2numpf e
	return $ mkXPathQ Int pf
expr2pf (GenExpr Union lexp) = do
	pfs <- mapM expr2pf lexp
	return $ nestedUnion pfs
expr2pf (PathExpr Nothing (Just path)) = locpath2pf path
expr2pf (PathExpr (Just pred) Nothing) = expr2pf (FilterExpr [pred])
expr2pf (PathExpr (Just pred) (Just path)) = do
	pf <- locpath2pf path
	filter2pf pf pred
expr2pf (FilterExpr (xpath:preds)) = do
	xp <- expr2pf xpath
	filters2pf xp preds
expr2pf (VarExpr var) = error "no variables supported"
expr2pf (LiteralExpr str) = return $ constantpf (List Char) str
expr2pf (NumberExpr i) = return $ constantpf Int (xpathnum2int i)
expr2pf (FctExpr "count" [arg]) = do
	pf <- expr2pf arg
	return $ mkXPathQ nat $ SEQQ pf LENGTH
expr2pf (FctExpr "sum" args) = do
	pfs <- mapM expr2pf args
	let pf = nestedUnion $ map (`SEQQ` MAP (CAST nat)) pfs
	return $ mkXPathQ nat $ pf `SEQQ` FOLD
expr2pf (FctExpr name args) = error $ "function unsupported :" ++ name

locpath2pf :: MonadPlus m => LocationPath -> m (Pf XPathQ)
locpath2pf (LocPath Rel xsteps) = xsteps2pf xsteps
locpath2pf (LocPath Abs _) = error "absolute paths not supported"

xsteps2pf :: MonadPlus m => [XStep] -> m (Pf XPathQ)
xsteps2pf xsteps = do
	xs <- mapM xstep2pf xsteps
	return $ nestedXComp xs

nestedXComp :: [Pf XPathQ] -> Pf XPathQ
nestedXComp [] = EMPTYQ
nestedXComp [x] = x
nestedXComp (x:xs) = x :/: nestedXComp xs

xstep2pf :: MonadPlus m => XStep -> m (Pf XPathQ)
xstep2pf (Step axis node preds) =
	do { nd <- node2pf node; filters2pf (axis2pf axis :/: nd) preds }
	`mplus`
	do { filters2pf (axis2pf axis) preds }

node2pf :: MonadPlus m =>  NodeTest -> m (Pf XPathQ)
node2pf (NameTest n) = return $ NAME (qname n)
node2pf (TypeTest XPNode) = mzero
node2pf node = error $ "node " ++ show node ++ " unsupported"

qname :: QName -> String
qname n = localPart n

axis2pf :: AxisSpec -> Pf XPathQ
axis2pf Child = CHILD
axis2pf Descendant = DESCENDANT
axis2pf DescendantOrSelf = DESCSELF
axis2pf XPath.Self = SELF
axis2pf Attribute = ATTRIBUTE
axis2pf x = error $ "axisSpec2PF: axis " ++ show x ++ " not supported"

filters2pf :: MonadPlus m => Pf XPathQ -> [XPath] -> m (Pf XPathQ)
filters2pf pf = foldM filter2pf pf

filter2pf :: MonadPlus m => Pf XPathQ -> XPath -> m (Pf XPathQ)
filter2pf pf (NumberExpr i) = return $ filternatpf pf (intNat $ xpathnum2int i)
filter2pf pf pred = do
	p <- xpath2boolpf pred
	return $ pf :?: p

filternatpf :: Pf XPathQ -> Nat -> Pf XPathQ
filternatpf pf (Nat 0) = EMPTYQ
filternatpf pf (Nat 1) = pf `SEQQ` LHEAD
filternatpf pf (Nat (pred -> n)) = filternatpf (SEQQ pf LTAIL) (Nat n)

mkXPathQ :: Typeable a => Type a -> Pf (Q a) -> Pf XPathQ
mkXPathQ a pf = pf `SEQQ` (MKDYN a) `SEQQ` WRAP

constantpf :: Typeable a => Type a -> a -> Pf XPathQ
constantpf a x = mkXPathQ a $ SEQQ EMPTYQ (PNT x)

xpathnum2int :: XPNumber -> Int
xpathnum2int (XPath.Float f) = fromEnum f
xpathnum2int (XPath.NaN) = error "xpnumber NaN"
xpathnum2int (XPath.NegInf) = -2147483648
xpathnum2int (XPath.PosInf) = 2147483647
xpathnum2int (XPath.Neg0) = 0
xpathnum2int (XPath.Pos0) = 0

xpath2boolpf :: MonadPlus m => XPath -> m (Pf (Q Bool))
xpath2boolpf (GenExpr op ps) | isBoolOp op = do
	xs <- mapM xpath2boolpf ps
	return $ nestedOp (boolop2pf op) xs
xpath2boolpf exp = do
	pf <- expr2pf exp
	return $ boolean pf
	where boolean :: Pf XPathQ -> Pf (Q Bool)
              boolean pf = pf `SEQQ` NONEMPTY

boolop2pf :: Op -> Pf ((Bool,Bool) -> Bool)
boolop2pf Or = FUN "or" $ uncurry (||)
boolop2pf And = FUN "and" $ uncurry (&&)
boolop2pf Eq = FUN "eq" $ uncurry (==)
boolop2pf NEq = FUN "neq" $ uncurry (/=)
boolop2pf Less = FUN "less" $ uncurry (<)
boolop2pf Greater = FUN "greater" $ uncurry (>)
boolop2pf LessEq = FUN "lesseq" $ uncurry (<=)
boolop2pf GreaterEq = FUN "greatereq" $ uncurry (>=)

xpath2numpf :: MonadPlus m => XPath -> m (Pf (Q Int))
xpath2numpf (GenExpr op is) | isNumOp op = do
	xs <- mapM xpath2numpf is
	return $ nestedOp (numop2pf op) xs
xpath2numpf (NumberExpr i) = return $ SEQQ EMPTYQ (PNT (xpathnum2int i))
xpath2numpf exp = do
	pf <- expr2pf exp
	return $ number pf
	where number :: Pf XPathQ -> Pf (Q Int)
	      number (SEQQ pf WRAP) = SEQQ pf (UNDYN Int)
	      number pf = error $ "expression not a number: " ++ show pf

numop2pf :: Op -> Pf ((Int,Int) -> Int)
numop2pf Plus = FUN "plus" (uncurry (+))
numop2pf Minus = FUN "plus" (uncurry (-))
numop2pf Div = FUN "plus" (uncurry div)
numop2pf Mod = FUN "mod" (uncurry mod)
numop2pf Mult = FUN "mult" (uncurry (*))
numop2pf Unary = error "unary?"

xpath2natpf :: MonadPlus m => XPath -> m (Pf (Q Nat))
xpath2natpf (GenExpr op is) | isNumOp op = do
	xs <- mapM xpath2natpf is
	return $ nestedOp (natop2pf op) xs
xpath2natpf exp = do
	pf <- expr2pf exp
	return $ number pf
	where number :: Pf XPathQ -> Pf (Q Nat)
	      number (SEQQ pf WRAP) = SEQQ pf (UNDYN nat)
	      number pf = error $ "expression not a natural: " ++ show pf

natop2pf :: Op -> Pf ((Nat,Nat) -> Nat)
natop2pf Plus = PLUS
natop2pf Minus = error "minus undefined for naturals"
natop2pf Div = error "div undefined for naturals"
natop2pf Mod = error "mod undefined for naturals"
natop2pf Mult = error "mult undefined for naturals"
natop2pf Unary = error "unary?"

nestedOp :: (Monoid a,Typeable a) => Pf ((a,a) -> a) -> [Pf (Q a)] -> Pf (Q a)
nestedOp op [] = EMPTYQ
nestedOp op [p] = p
nestedOp op (p:ps) = SEQQ (p :/\: nestedOp op ps) op

isBoolOp :: Op -> Bool
isBoolOp x = or $ map (x==) $ [Or,And,XPath.Eq,NEq,Less,Greater,LessEq,GreaterEq]

isNumOp :: Op -> Bool
isNumOp x = or $ map (x==) $ [Plus,Minus,Div,Mod,Mult,Unary]

nestedUnion :: [Pf (Q [a])] -> Pf (Q [a])
nestedUnion [] = EMPTYQ
nestedUnion [x] = x
nestedUnion (x:xs) = x `UNION` (nestedUnion xs)