xee_xpath_type/
sequence_type.rs

1// https://www.w3.org/TR/xpath-31/#id-sequencetype-subtype
2
3use crate::ast;
4
5/// Type information for a sequence type.
6pub trait TypeInfo {
7    /// Get whether `self` is a subtype of `other`.
8    fn subtype(&self, other: &Self) -> bool;
9}
10
11impl TypeInfo for ast::SequenceType {
12    // https://www.w3.org/TR/xpath-31/#id-seqtype-subtype
13    fn subtype(&self, other: &ast::SequenceType) -> bool {
14        match (self, other) {
15            (ast::SequenceType::Empty, ast::SequenceType::Empty) => true,
16            (ast::SequenceType::Empty, ast::SequenceType::Item(ast::Item { occurrence, .. })) => {
17                match occurrence {
18                    ast::Occurrence::Option => true,
19                    ast::Occurrence::Many => true,
20                    ast::Occurrence::One => false,
21                    ast::Occurrence::NonEmpty => false,
22                }
23            }
24            (ast::SequenceType::Item(_), ast::SequenceType::Empty) => false,
25            (
26                ast::SequenceType::Item(ast::Item {
27                    item_type: a_item_type,
28                    occurrence: a_occurrence,
29                }),
30                ast::SequenceType::Item(ast::Item {
31                    item_type: b_item_type,
32                    occurrence: b_occurrence,
33                }),
34            ) => match (a_occurrence, b_occurrence) {
35                (ast::Occurrence::Option, ast::Occurrence::Option) => {
36                    a_item_type.subtype(b_item_type)
37                }
38                (ast::Occurrence::Option, ast::Occurrence::Many) => {
39                    a_item_type.subtype(b_item_type)
40                }
41                (ast::Occurrence::Option, ast::Occurrence::One) => false,
42                (ast::Occurrence::Option, ast::Occurrence::NonEmpty) => false,
43                (ast::Occurrence::Many, ast::Occurrence::Option) => false,
44                (ast::Occurrence::Many, ast::Occurrence::Many) => a_item_type.subtype(b_item_type),
45                (ast::Occurrence::Many, ast::Occurrence::One) => false,
46                (ast::Occurrence::Many, ast::Occurrence::NonEmpty) => false,
47                (ast::Occurrence::One, ast::Occurrence::Option) => a_item_type.subtype(b_item_type),
48                (ast::Occurrence::One, ast::Occurrence::Many) => a_item_type.subtype(b_item_type),
49                (ast::Occurrence::One, ast::Occurrence::One) => a_item_type.subtype(b_item_type),
50                (ast::Occurrence::One, ast::Occurrence::NonEmpty) => {
51                    a_item_type.subtype(b_item_type)
52                }
53                (ast::Occurrence::NonEmpty, ast::Occurrence::Option) => false,
54                (ast::Occurrence::NonEmpty, ast::Occurrence::Many) => {
55                    a_item_type.subtype(b_item_type)
56                }
57                (ast::Occurrence::NonEmpty, ast::Occurrence::One) => false,
58                (ast::Occurrence::NonEmpty, ast::Occurrence::NonEmpty) => {
59                    a_item_type.subtype(b_item_type)
60                }
61            },
62        }
63    }
64}
65
66impl TypeInfo for ast::ItemType {
67    // https://www.w3.org/TR/xpath-31/#id-itemtype-subtype
68    fn subtype(&self, other: &ast::ItemType) -> bool {
69        match (self, other) {
70            // 1 Ai and Bi are AtomicOrUnionTypes, and derives-from(Ai, Bi)
71            // returns true.
72            (ast::ItemType::AtomicOrUnionType(a), ast::ItemType::AtomicOrUnionType(b)) => {
73                a.derives_from(*b)
74            }
75            // 2 TODO Ai is a pure union type, and every type t in the
76            // transitive membership of Ai satisfies subtype-itemType(t, Bi).
77
78            // 3 TODO Ai is xs:error and Bi is a generalized atomic type. 4 Bi
79            // is item()
80            (_, ast::ItemType::Item) => true,
81            // 5 Bi is node(), and Ai is a KindTest.
82            (ast::ItemType::KindTest(_), ast::ItemType::KindTest(ast::KindTest::Any)) => true,
83            // 6 Bi is text() and Ai is also text().
84            (
85                ast::ItemType::KindTest(ast::KindTest::Text),
86                ast::ItemType::KindTest(ast::KindTest::Text),
87            ) => true,
88            // 7 Bi is comment() and Ai is also comment().
89            (
90                ast::ItemType::KindTest(ast::KindTest::Comment),
91                ast::ItemType::KindTest(ast::KindTest::Comment),
92            ) => true,
93            // 8 Bi is namespace-node() and Ai is also namespace-node().
94            (
95                ast::ItemType::KindTest(ast::KindTest::NamespaceNode),
96                ast::ItemType::KindTest(ast::KindTest::NamespaceNode),
97            ) => true,
98            // 9 Bi is processing-instruction() and Ai is either
99            // processing-instruction() or processing-instruction(N) for any
100            // name N.
101            (
102                ast::ItemType::KindTest(ast::KindTest::PI(_)),
103                ast::ItemType::KindTest(ast::KindTest::PI(None)),
104            ) => true,
105            // 10 Bi is processing-instruction(Bn), and Ai is also
106            // processing-instruction(Bn)
107            (
108                ast::ItemType::KindTest(ast::KindTest::PI(Some(a))),
109                ast::ItemType::KindTest(ast::KindTest::PI(Some(b))),
110            ) => a == b,
111            // 11 Bi is document-node() and Ai is either document-node() or
112            // document-node(E) for any ElementTest E.
113            (
114                ast::ItemType::KindTest(ast::KindTest::Document(_)),
115                ast::ItemType::KindTest(ast::KindTest::Document(None)),
116            ) => true,
117            // 12 Bi is document-node(Be) and Ai is document-node(Ae), and
118            // subtype-itemtype(Ae, Be).
119            (
120                ast::ItemType::KindTest(ast::KindTest::Document(Some(a))),
121                ast::ItemType::KindTest(ast::KindTest::Document(Some(b))),
122            ) => a.subtype(b),
123            // 13 Bi is either element() or element(*), and Ai is an ElementTest.
124            (
125                ast::ItemType::KindTest(ast::KindTest::Element(_)),
126                ast::ItemType::KindTest(ast::KindTest::Element(None)),
127            ) => true,
128            // 14-18 element comparisons are factored out
129            (
130                ast::ItemType::KindTest(ast::KindTest::Element(Some(a))),
131                ast::ItemType::KindTest(ast::KindTest::Element(Some(b))),
132            ) => a.subtype(b),
133            // 19 Bi is schema-element(Bn), Ai is schema-element(An), and every
134            // element declaration that is an actual member of the substitution
135            // group of An is also an actual member of the substitution group
136            // of Bn.
137            // TODO, dummy implementation
138            (
139                ast::ItemType::KindTest(ast::KindTest::SchemaElement(_)),
140                ast::ItemType::KindTest(ast::KindTest::SchemaElement(_)),
141            ) => false,
142            // 20 Bi is either attribute() or attribute(*), and Ai is an
143            // AttributeTest.
144            (
145                ast::ItemType::KindTest(ast::KindTest::Attribute(_)),
146                ast::ItemType::KindTest(ast::KindTest::Attribute(None)),
147            ) => true,
148            // 21-23 attribute comparisons are factored out
149            (
150                ast::ItemType::KindTest(ast::KindTest::Attribute(Some(a))),
151                ast::ItemType::KindTest(ast::KindTest::Attribute(Some(b))),
152            ) => a.subtype(b),
153            // 24 Bi is schema-attribute(Bn), the expanded QName of An equals
154            // the expanded QName of Bn, and Ai is schema-attribute(An).
155            // TODO, dummy implementation
156            (
157                ast::ItemType::KindTest(ast::KindTest::SchemaAttribute(_)),
158                ast::ItemType::KindTest(ast::KindTest::SchemaAttribute(_)),
159            ) => false,
160            // 25 Bi is function(*), Ai is a FunctionTest.
161            (
162                ast::ItemType::FunctionTest(_),
163                ast::ItemType::FunctionTest(ast::FunctionTest::AnyFunctionTest),
164            ) => true,
165            // 26 function comparison factored out
166            (
167                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(a)),
168                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(b)),
169            ) => a.subtype(b),
170            // 27 Ai is map(K, V), for any K and V and Bi is map(*).
171            (ast::ItemType::MapTest(_), ast::ItemType::MapTest(ast::MapTest::AnyMapTest)) => true,
172            // 28 Ai is map(Ka, Va) and Bi is map(Kb, Vb), where
173            // subtype-itemtype(Ka, Kb) and subtype(Va, Vb).
174            (
175                ast::ItemType::MapTest(ast::MapTest::TypedMapTest(a_typed_map_test)),
176                ast::ItemType::MapTest(ast::MapTest::TypedMapTest(b_typed_map_test)),
177            ) => a_typed_map_test.as_ref().subtype(b_typed_map_test.as_ref()),
178            // 29 Ai is map(*) (or, because of the transitivity rules, any
179            // other map type), and Bi is function(*).
180            (
181                ast::ItemType::MapTest(_),
182                ast::ItemType::FunctionTest(ast::FunctionTest::AnyFunctionTest),
183            ) => true,
184
185            // 30 Ai is map(*) (or, because of the transitivity rules, any
186            // other map type), and Bi is function(xs:anyAtomicType) as
187            // item()*.
188            (
189                ast::ItemType::MapTest(_),
190                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(
191                    typed_function_test,
192                )),
193            ) if typed_function_test.as_ref() == &map_function_test() => true,
194            // 31 Ai is array(X) and Bi is array(*).
195            (
196                ast::ItemType::ArrayTest(_),
197                ast::ItemType::ArrayTest(ast::ArrayTest::AnyArrayTest),
198            ) => true,
199            // 32 Ai is array(X) and Bi is array(Y), and subtype(X, Y) is true.
200            (
201                ast::ItemType::ArrayTest(ast::ArrayTest::TypedArrayTest(a_typed_array_test)),
202                ast::ItemType::ArrayTest(ast::ArrayTest::TypedArrayTest(b_typed_array_test)),
203            ) if a_typed_array_test
204                .as_ref()
205                .subtype(b_typed_array_test.as_ref()) =>
206            {
207                true
208            }
209            // 33 Ai is array(*) (or, because of the transitivity rules, any
210            // other array type) and Bi is function(*).
211            (
212                ast::ItemType::ArrayTest(_),
213                ast::ItemType::FunctionTest(ast::FunctionTest::AnyFunctionTest),
214            ) => true,
215
216            // 34 Ai is array(*) (or, because of the transitivity rules, any
217            // other array type) and Bi is function(xs:integer) as item()*.
218            (
219                ast::ItemType::ArrayTest(_),
220                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(
221                    typed_function_test,
222                )),
223            ) if typed_function_test.as_ref() == &array_function_test() => true,
224            // 35 Ai is map(K, V), and Bi is function(xs:anyAtomicType) as V?.
225            // has to be above 30 to match at all
226            (
227                ast::ItemType::MapTest(ast::MapTest::TypedMapTest(typed_map_test)),
228                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(
229                    typed_function_test,
230                )),
231            ) => {
232                typed_function_test.parameter_types.len() == 1
233                    && typed_function_test.parameter_types[0]
234                        == ast::SequenceType::Item(ast::Item {
235                            item_type: ast::ItemType::AtomicOrUnionType(
236                                xee_schema_type::Xs::AnyAtomicType,
237                            ),
238                            occurrence: ast::Occurrence::One,
239                        })
240                    && is_type_question_mark(
241                        &typed_map_test.value_type,
242                        &typed_function_test.return_type,
243                    )
244            }
245            // 36 Ai is array(X) and Bi is function(xs:integer) as X.
246            (
247                ast::ItemType::ArrayTest(ast::ArrayTest::TypedArrayTest(a_sequence_type)),
248                ast::ItemType::FunctionTest(ast::FunctionTest::TypedFunctionTest(
249                    typed_function_test,
250                )),
251            ) => {
252                typed_function_test.parameter_types.len() == 1
253                    && typed_function_test.parameter_types[0]
254                        == ast::SequenceType::Item(ast::Item {
255                            item_type: ast::ItemType::AtomicOrUnionType(
256                                xee_schema_type::Xs::Integer,
257                            ),
258                            occurrence: ast::Occurrence::One,
259                        })
260                    && a_sequence_type.item_type == typed_function_test.return_type
261            }
262            _ => false,
263        }
264    }
265}
266
267// V? occurs in places in the spec. This isn't very well defined, but
268// I've analyzed it here: https://github.com/w3c/qt3tests/issues/28
269fn is_type_question_mark(a: &ast::SequenceType, b: &ast::SequenceType) -> bool {
270    match (a, b) {
271        (ast::SequenceType::Empty, ast::SequenceType::Empty) => true,
272        (ast::SequenceType::Item(a_item), ast::SequenceType::Item(b_item)) => {
273            if a_item != b_item {
274                return false;
275            }
276            match &b_item.occurrence {
277                ast::Occurrence::One => false,
278                ast::Occurrence::NonEmpty => false,
279                ast::Occurrence::Option => true,
280                ast::Occurrence::Many => true,
281            }
282        }
283        _ => false,
284    }
285}
286
287fn map_function_test() -> ast::TypedFunctionTest {
288    map_function_test_with_return_type(&ast::SequenceType::Item(ast::Item {
289        item_type: ast::ItemType::Item,
290        occurrence: ast::Occurrence::Many,
291    }))
292}
293
294fn map_function_test_with_return_type(return_type: &ast::SequenceType) -> ast::TypedFunctionTest {
295    ast::TypedFunctionTest {
296        parameter_types: vec![ast::SequenceType::Item(ast::Item {
297            item_type: ast::ItemType::AtomicOrUnionType(xee_schema_type::Xs::AnyAtomicType),
298            occurrence: ast::Occurrence::One,
299        })],
300        return_type: return_type.clone(),
301    }
302}
303
304fn array_function_test() -> ast::TypedFunctionTest {
305    ast::TypedFunctionTest {
306        parameter_types: vec![ast::SequenceType::Item(ast::Item {
307            item_type: ast::ItemType::AtomicOrUnionType(xee_schema_type::Xs::Integer),
308            occurrence: ast::Occurrence::One,
309        })],
310        return_type: ast::SequenceType::Item(ast::Item {
311            item_type: ast::ItemType::Item,
312            occurrence: ast::Occurrence::Many,
313        }),
314    }
315}
316
317impl TypeInfo for ast::DocumentTest {
318    fn subtype(&self, other: &ast::DocumentTest) -> bool {
319        match (self, other) {
320            // duplicate of 13 Bi is either element() or element(*), and Ai is an ElementTest.
321            (ast::DocumentTest::Element(..), ast::DocumentTest::Element(None)) => true,
322            (
323                ast::DocumentTest::Element(Some(a_element_or_attribute_test)),
324                ast::DocumentTest::Element(Some(b_element_or_attribute_test)),
325            ) => a_element_or_attribute_test.subtype(b_element_or_attribute_test),
326            // TODO: schema element test
327            _ => false,
328        }
329    }
330}
331
332impl TypeInfo for ast::ElementOrAttributeTest {
333    fn subtype(&self, other: &ast::ElementOrAttributeTest) -> bool {
334        match (self, other) {
335            // 14 Bi is either element(Bn) or element(Bn, xs:anyType?), the
336            // expanded QName of An equals the expanded QName of Bn, and Ai
337            // is either element(An) or element(An, T) or element(An, T?)
338            // for any type T.
339            (
340                ast::ElementOrAttributeTest {
341                    name_or_wildcard: ast::NameOrWildcard::Name(a_name),
342                    ..
343                },
344                ast::ElementOrAttributeTest {
345                    name_or_wildcard: ast::NameOrWildcard::Name(b_name),
346                    type_name:
347                        None
348                        | Some(ast::TypeName {
349                            name: xee_schema_type::Xs::AnyType,
350                            can_be_nilled: true,
351                        }),
352                },
353            ) => a_name == b_name,
354            // 15 Bi is element(Bn, Bt), the expanded QName of An equals the
355            // expanded QName of Bn, Ai is element(An, At), and
356            // derives-from(At, Bt) returns true.
357            (
358                ast::ElementOrAttributeTest {
359                    name_or_wildcard: ast::NameOrWildcard::Name(a_name),
360                    type_name:
361                        Some(ast::TypeName {
362                            name: a_type_name,
363                            can_be_nilled: false,
364                        }),
365                },
366                ast::ElementOrAttributeTest {
367                    name_or_wildcard: ast::NameOrWildcard::Name(b_name),
368                    type_name:
369                        Some(ast::TypeName {
370                            name: b_type_name,
371                            can_be_nilled: false,
372                        }),
373                },
374            ) => a_name == b_name && a_type_name.derives_from(*b_type_name),
375            // 16 Bi is element(Bn, Bt?), the expanded QName of An equals the
376            // expanded QName of Bn, Ai is either element(An, At) or
377            // element(An, At?), and derives-from(At, Bt) returns true.
378            (
379                ast::ElementOrAttributeTest {
380                    name_or_wildcard: ast::NameOrWildcard::Name(a_name),
381                    type_name:
382                        Some(ast::TypeName {
383                            name: a_type_name, ..
384                        }),
385                },
386                ast::ElementOrAttributeTest {
387                    name_or_wildcard: ast::NameOrWildcard::Name(b_name),
388                    type_name:
389                        Some(ast::TypeName {
390                            name: b_type_name,
391                            can_be_nilled: true,
392                        }),
393                },
394            ) => a_name == b_name && a_type_name.derives_from(*b_type_name),
395            // 17 Bi is element(*, Bt), Ai is either element(*, At) or
396            // element(N, At) for any name N, and derives-from(At, Bt) returns
397            // true.
398            (
399                ast::ElementOrAttributeTest {
400                    type_name:
401                        Some(ast::TypeName {
402                            name: a_type_name,
403                            can_be_nilled: false,
404                        }),
405                    ..
406                },
407                ast::ElementOrAttributeTest {
408                    name_or_wildcard: ast::NameOrWildcard::Wildcard,
409                    type_name:
410                        Some(ast::TypeName {
411                            name: b_type_name,
412                            can_be_nilled: false,
413                        }),
414                },
415            ) => a_type_name.derives_from(*b_type_name),
416            // 18 Bi is element(*, Bt?), Ai is either element(*, At),
417            // element(*, At?), element(N, At), or element(N, At?) for any name
418            // N, and derives-from(At, Bt) returns true.
419            (
420                ast::ElementOrAttributeTest {
421                    type_name:
422                        Some(ast::TypeName {
423                            name: a_type_name, ..
424                        }),
425                    ..
426                },
427                ast::ElementOrAttributeTest {
428                    name_or_wildcard: ast::NameOrWildcard::Wildcard,
429                    type_name:
430                        Some(ast::TypeName {
431                            name: b_type_name,
432                            can_be_nilled: true,
433                        }),
434                },
435            ) => a_type_name.derives_from(*b_type_name),
436            _ => false,
437        }
438    }
439}
440
441impl TypeInfo for ast::TypedFunctionTest {
442    fn subtype(&self, other: &ast::TypedFunctionTest) -> bool {
443        // 26 Bi is function(Ba_1, Ba_2, ... Ba_N) as Br, Ai is function(Aa_1,
444        // Aa_2, ... Aa_M) as Ar, where N (arity of Bi) equals M (arity of Ai);
445        // subtype(Ar, Br); and for values of I between 1 and N, subtype(Ba_I,
446        // Aa_I).
447        // That is, the arguments are contravariant and the return type is covariant
448        if self.parameter_types.len() != other.parameter_types.len() {
449            return false;
450        }
451
452        // covariant
453        if !self.return_type.subtype(&other.return_type) {
454            return false;
455        }
456
457        for (a, b) in self
458            .parameter_types
459            .iter()
460            .zip(other.parameter_types.iter())
461        {
462            // contravariant
463            if !b.subtype(a) {
464                return false;
465            }
466        }
467        true
468    }
469}
470
471impl TypeInfo for ast::TypedMapTest {
472    fn subtype(&self, other: &ast::TypedMapTest) -> bool {
473        self.key_type.derives_from(other.key_type) && self.value_type.subtype(&other.value_type)
474    }
475}
476
477impl TypeInfo for ast::TypedArrayTest {
478    fn subtype(&self, other: &ast::TypedArrayTest) -> bool {
479        self.item_type.subtype(&other.item_type)
480    }
481}