1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
------------------------------------------------------------------------------
--- Library for declarative processing XML data.
--- 
--- This libary contains definitions of combinators which can be used
--- to specify queries and transformations on XML data in a high-level
--- declarative manner. Actually, these combinators can be used as
--- functional patterns in operations to query and transform XML data.
---
--- The ideas and usage of this library is described in the paper
---
--- > M. Hanus: Declarative Processing of Semistructured Web Data.
--- > Technical Communications of the 27th International Conference
--- > on Logic Programming (ICLP 2011),
--- > Leibniz International Proceedings in Informatics (LIPIcs),
--- > Vol. 11, pp. 198-208, 2011
--- > [Online](https://doi.org/10.4230/LIPIcs.ICLP.2011.198)
---
--- @author Michael Hanus
--- @version February 2025
------------------------------------------------------------------------------
{-# OPTIONS_FRONTEND -Wno-overlapping #-}

module XCuery where

import XML

------------------------------------------------------------------------------

--- The operation `with` evaluates to all lists containing the
--- elements in the argument in the same order. For instance,
---
---     > with [1,2]
---     1:2:_a
---     1:_a:2:_b
---     1:_a:_b:2:_c
---     ...
---
--- This operation can be used in a pattern to match XML structures
--- having at least the given elements as children, as in
---
---     getNamePhone
---       (xml "entry"
---            (with [xml "name"  [xtxt name],
---                   xml "phone" [xtxt phone]])) = name ++ ": " ++ phone
with :: Data a => [a] -> [a]
with []     = unknown
with (x:xs) = unknown ++ x : with xs

--- The operation `xml'` can be used to match an XML structure with a given
--- tag and children independent of the attributes. For instance,
---
---     getName (xml’ "entry" (with [xml’ "name" [xtxt n]])) = n
---
--- matches an XML element with tag `entry` and some child element with
--- tag `name`, independent of the possible attributes attached to these
--- elements.
xml' :: String -> [XmlExp] -> XmlExp
xml' t xs = XElem t unknown xs

--- The operation `anyorder` returns all permutations of the input list.
--- It can be used in a pattern to match XML structures with the given
--- elements in arbitrary order, as in
---
---     getNamePhone
---       (xml "entry"
---         (with (anyorder [xml "name"  [xtxt name],
---                          xml "phone" [xtxt phone]]))) = name ++ ": " ++ phone
anyorder :: [a] -> [a]
anyorder []     = []
anyorder (x:xs) = insert (anyorder xs)
 where insert []     = [x]
       insert (y:ys) = x:y:ys  ?  y : insert ys


--- The operation `deepXml` evaluates to any XML structure containing
--- an XML structure with the given tag and children at an abritrarily
--- deep position. For instance,
---
---     getNamePhone
---       (deepXml "entry"
---                (with [xml "name"  [xtxt name],
---                       xml "phone" [xtxt phone]])) = name ++ ": " ++ phone
---
--- allows to query an XML structure having a structure with tag `entry`
--- and children structures with tags `name` and `phone`.
deepXml :: String -> [XmlExp] -> XmlExp
deepXml tag elems = xml tag elems
deepXml tag elems = xml' unknown (unknown ++ [deepXml tag elems] ++ unknown)

--- This operation is similar to 'deepXml' but matches the XML structures
--- independent of their attributes.
deepXml' :: String -> [XmlExp] -> XmlExp
deepXml' tag elems = xml' tag elems
deepXml' tag elems = xml' unknown (unknown ++ [deepXml' tag elems] ++ unknown)

--- This operation is similar to 'deepXml' but allows to provide a list
--- of attributes to the matched XML structures.
--- For instance,
---
---     getMaleFirstNames
---       (deepXElem "first" (with [("sex","male")]) [xtxt f]) = f
---
--- matches an XML structure containing an XML structure with tag `first`
--- and some attribute `sex` with value `male`.
deepXElem :: String -> [(String,String)] -> [XmlExp] -> XmlExp
deepXElem tag attrs elems = XElem tag attrs elems
deepXElem tag attrs elems =
  xml' unknown (unknown ++ [deepXElem tag attrs elems] ++ unknown)

--- The predicate `noTagOf` returns `True` if the given tag is not a tag
--- of all argument XML structures.
noTagOf :: String -> [XmlExp] -> Bool
noTagOf tag = all ((/= tag) . tagOf)

--- The operation `withOthers` is similar to 'with' but has a second argument
--- that contains the child nodes that are present but not part of the
--- first argument. One can use this operation to denote the
--- `unmatched` part of an XML structure in order to put conditions on it.
--- For instance, if one wants to get the name and phone number of an entry
--- that has no email address, one can use the following definition:
--- 
---     getNamePhoneWithoutEmail
---       (deepXml "entry"
---          (withOthers [xml "name" [xtxt name], xml "phone" [xtxt phone]]
---                      others))
---       | "email" `noTagOf` others
---       = name ++ ": " ++ phone
withOthers :: Data a => [a] -> [a] -> [a]
withOthers ys zs = withAcc [] ys zs
 where -- Accumulate remaining elements:
   withAcc prevs [] others | others=:=prevs++suffix = suffix
                                             where suffix free
   withAcc prevs (x:xs) others =
      prefix ++ x : withAcc (prevs++prefix) xs others
                                             where prefix free

--- The operation `optXml` can be used in transformations of XML elements
--- to insert a structure with a given tag depending on the presence
--- of this tag in a list of XML strucutres.
--- For this purpose, `optXml t xs ys` evaluates to `xml t xs`
--- if there is no element with tag `t` in `ys`, otherwise the first element
--- of `ys` with tag `t` is returned.
--- 
--- The following definition shows a usage of `optXml`:
--- 
---     transNickPhone
---       (deepXml "entry"
---                (withOthers [xml "name"  [xtxt n],
---                             xml "first" [xtxt f],
---                             xml "phone" phone]
---                            others)) =
---       xml "nickphone" [optXml "nickname" [xtxt (f++n)] others,
---                        xml "phone" phone]
optXml :: String -> [XmlExp] -> [XmlExp] -> XmlExp
optXml tag elems [] = xml tag elems
optXml tag elems (x:xs) =
  if tag == tagOf x then x else optXml tag elems xs

------------------------------------------------------------------------------