Skip to content

Commit 3c68f1d

Browse files
Fix XPath conformance suite failures
1 parent fb712eb commit 3c68f1d

4 files changed

Lines changed: 60 additions & 6 deletions

File tree

xee-interpreter/src/context/static_context.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use ahash::HashSet;
12
use std::cell::RefCell;
23
use std::fmt::Debug;
34
use std::rc::Rc;
@@ -28,14 +29,20 @@ static DEFAULT_COLLATION: LazyLock<IriAbsoluteString> = LazyLock::new(|| {
2829
pub struct StaticContext {
2930
parser_context: XPathParserContext,
3031
functions: &'static function::StaticFunctions,
32+
disabled_functions: HashSet<xot::xmlname::OwnedName>,
3133
// TODO: try to make collations static
3234
collations: RefCell<Collations>,
3335
static_base_uri: Option<IriAbsoluteString>,
3436
}
3537

3638
impl Default for StaticContext {
3739
fn default() -> Self {
38-
Self::new(Namespaces::default(), VariableNames::default(), None)
40+
Self::new(
41+
Namespaces::default(),
42+
VariableNames::default(),
43+
HashSet::default(),
44+
None,
45+
)
3946
}
4047
}
4148

@@ -44,6 +51,7 @@ impl From<XPathParserContext> for StaticContext {
4451
Self {
4552
parser_context,
4653
functions: &STATIC_FUNCTIONS,
54+
disabled_functions: HashSet::default(),
4755
collations: RefCell::new(Collations::new()),
4856
static_base_uri: None,
4957
}
@@ -54,18 +62,25 @@ impl StaticContext {
5462
pub(crate) fn new(
5563
namespaces: Namespaces,
5664
variable_names: VariableNames,
65+
disabled_functions: HashSet<xot::xmlname::OwnedName>,
5766
static_base_uri: Option<IriAbsoluteString>,
5867
) -> Self {
5968
Self {
6069
parser_context: XPathParserContext::new(namespaces, variable_names),
6170
functions: &STATIC_FUNCTIONS,
71+
disabled_functions,
6272
collations: RefCell::new(Collations::new()),
6373
static_base_uri,
6474
}
6575
}
6676

6777
pub fn from_namespaces(namespaces: Namespaces) -> Self {
68-
Self::new(namespaces, VariableNames::default(), None)
78+
Self::new(
79+
namespaces,
80+
VariableNames::default(),
81+
HashSet::default(),
82+
None,
83+
)
6984
}
7085

7186
pub fn namespaces(&self) -> &Namespaces {
@@ -137,6 +152,9 @@ impl StaticContext {
137152
name: &xot::xmlname::OwnedName,
138153
arity: u8,
139154
) -> Option<function::StaticFunctionId> {
155+
if self.disabled_functions.contains(name) {
156+
return None;
157+
}
140158
self.functions.get_by_name(name, arity)
141159
}
142160

xee-interpreter/src/context/static_context_builder.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ahash::HashMap;
1+
use ahash::{HashMap, HashSet};
22
use iri_string::types::IriAbsoluteString;
33
use xee_name::Namespaces;
44
use xot::xmlname::OwnedName;
@@ -9,6 +9,7 @@ use crate::context;
99
pub struct StaticContextBuilder<'a> {
1010
variable_names: Vec<OwnedName>,
1111
namespaces: HashMap<&'a str, &'a str>,
12+
disabled_functions: HashSet<OwnedName>,
1213
default_element_namespace: &'a str,
1314
default_function_namespace: &'a str,
1415
static_base_uri: Option<IriAbsoluteString>,
@@ -75,6 +76,12 @@ impl<'a> StaticContextBuilder<'a> {
7576
self
7677
}
7778

79+
/// Disable a function name in this static context.
80+
pub fn disable_function(&mut self, name: OwnedName) -> &mut Self {
81+
self.disabled_functions.insert(name);
82+
self
83+
}
84+
7885
/// Build the static context.
7986
///
8087
/// This will always include the default known namespaces for
@@ -96,7 +103,12 @@ impl<'a> StaticContextBuilder<'a> {
96103
default_function_namespace.to_string(),
97104
);
98105
let variable_names = self.variable_names.clone().into_iter().collect();
99-
context::StaticContext::new(namespaces, variable_names, self.static_base_uri.clone())
106+
context::StaticContext::new(
107+
namespaces,
108+
variable_names,
109+
self.disabled_functions.clone(),
110+
self.static_base_uri.clone(),
111+
)
100112
}
101113
}
102114

xee-testrunner/src/testcase/assert.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,10 +1131,18 @@ fn run_xpath_with_result(
11311131
let q = queries.sequence_with_context(expr, static_context)?;
11321132

11331133
let variables = AHashMap::from([(name, sequence.clone())]);
1134-
let context_item = sequence.normalize(" ", documents.xot_mut())?;
1134+
let context_item = match sequence.normalize(" ", documents.xot_mut()) {
1135+
Ok(context_item) => Some(context_item.into()),
1136+
// Function items cannot become the implicit context item for the
1137+
// assertion query, but $result should still be available.
1138+
Err(error::ErrorValue::SENR0001) => None,
1139+
Err(error) => return Err(error.into()),
1140+
};
11351141

11361142
q.execute_build_context(documents, |build| {
1137-
build.context_item(context_item.into());
1143+
if let Some(context_item) = context_item {
1144+
build.context_item(context_item);
1145+
}
11381146
build.variables(variables);
11391147
})
11401148
}
@@ -1212,6 +1220,17 @@ mod tests {
12121220
assert!(result.effective_boolean_value().unwrap());
12131221
}
12141222

1223+
#[test]
1224+
fn test_run_xpath_with_result_allows_function_item_results_via_result_variable() {
1225+
let mut documents = Documents::new();
1226+
let sequence = run_xpath(&"map:entry('foo', 3)".to_string()).unwrap();
1227+
1228+
let expr = "$result?foo = 3".to_string();
1229+
let result = run_xpath_with_result(&expr, &sequence, &mut documents).unwrap();
1230+
1231+
assert!(result.effective_boolean_value().unwrap());
1232+
}
1233+
12151234
#[test]
12161235
fn test_assert_xml_ignores_outer_fragment_whitespace() {
12171236
let mut xot = Xot::new();

xee-testrunner/src/testcase/xpath.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ impl Runnable<XPathLanguage> for XPathTestCase {
9999
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
100100
};
101101
static_context_builder.namespaces(namespaces);
102+
static_context_builder.disable_function(xot::xmlname::OwnedName::new(
103+
"document".to_string(),
104+
xee_name::Namespaces::FN_NAMESPACE.to_string(),
105+
"fn".to_string(),
106+
));
102107

103108
// now construct a query with that static context
104109
let static_context = static_context_builder.build();

0 commit comments

Comments
 (0)