xee_interpreter/function/
static_function.rs

1use ahash::{HashMap, HashMapExt};
2use std::fmt::{Debug, Formatter};
3use xot::xmlname::NameStrInfo;
4
5use xee_name::{Name, Namespaces};
6use xee_xpath_ast::ast;
7
8use crate::context::DynamicContext;
9use crate::error;
10use crate::function;
11use crate::interpreter;
12use crate::library::static_function_descriptions;
13use crate::sequence;
14use crate::stack;
15
16#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)]
17pub(crate) enum FunctionKind {
18    // generate a function with one less arity that takes the
19    // item as the first argument
20    ItemFirst,
21    // generate a function with one less arity that takes the item
22    // as the last argument
23    ItemLast,
24    // generate just one function, but it takes an additional last
25    // argument that contains an option of the context item
26    ItemLastOptional,
27    // this function takes position as the implicit only argument
28    Position,
29    // this function takes size as the implicit only argument
30    Size,
31    // generate a function with one less arity that takes the collation
32    // as the last argument
33    Collation,
34    // this function is anonymous and takes a closure value as the
35    // last argument
36    AnonymousClosure,
37}
38
39impl FunctionKind {
40    pub(crate) fn parse(s: &str) -> Option<FunctionKind> {
41        match s {
42            "" => None,
43            "context_first" => Some(FunctionKind::ItemFirst),
44            "context_last" => Some(FunctionKind::ItemLast),
45            "context_last_optional" => Some(FunctionKind::ItemLastOptional),
46            "position" => Some(FunctionKind::Position),
47            "size" => Some(FunctionKind::Size),
48            "collation" => Some(FunctionKind::Collation),
49            "anonymous_closure" => Some(FunctionKind::AnonymousClosure),
50            _ => panic!("Unknown function kind {}", s),
51        }
52    }
53}
54
55pub(crate) type StaticFunctionType = fn(
56    context: &DynamicContext,
57    interpreter: &mut interpreter::Interpreter,
58    arguments: &[sequence::Sequence],
59) -> error::Result<sequence::Sequence>;
60
61pub(crate) struct StaticFunctionDescription {
62    pub(crate) name: Name,
63    pub(crate) signature: function::Signature,
64    pub(crate) function_kind: Option<FunctionKind>,
65    pub(crate) func: StaticFunctionType,
66}
67
68// Wraps a Rust function annotated with `#[xpath_fn]` and turns it
69// into a StaticFunctionDescription
70#[macro_export]
71macro_rules! wrap_xpath_fn {
72    ($function:path) => {{
73        use $function as wrapped_function;
74        let namespaces = &xee_name::DEFAULT_NAMESPACES;
75        $crate::function::StaticFunctionDescription::new(
76            wrapped_function::WRAPPER,
77            wrapped_function::SIGNATURE,
78            $crate::function::FunctionKind::parse(wrapped_function::KIND),
79            namespaces,
80        )
81    }};
82}
83
84impl StaticFunctionDescription {
85    pub(crate) fn new(
86        func: StaticFunctionType,
87        signature: &str,
88        function_kind: Option<FunctionKind>,
89        namespaces: &Namespaces,
90    ) -> Self {
91        // TODO reparse signature; the macro could have stored the parsed
92        // version as code, but that's more work than I'm prepared to do
93        // right now.
94        let signature = ast::Signature::parse(signature, namespaces)
95            .expect("Signature parse failed unexpectedly");
96        let name = signature.name.value.clone();
97        let signature: function::Signature = signature.into();
98        Self {
99            name,
100            signature,
101            function_kind,
102            func,
103        }
104    }
105
106    fn functions(&self) -> Vec<StaticFunction> {
107        if let Some(function_kind) = &self.function_kind {
108            self.signature
109                .alternative_signatures(*function_kind)
110                .into_iter()
111                .map(|(signature, function_kind)| {
112                    StaticFunction::new(self.func, self.name.clone(), signature, function_kind)
113                })
114                .collect()
115        } else {
116            vec![StaticFunction::new(
117                self.func,
118                self.name.clone(),
119                self.signature.clone(),
120                None,
121            )]
122        }
123    }
124}
125
126#[derive(Debug, Clone, Eq, PartialEq, Hash)]
127pub enum FunctionRule {
128    ItemFirst,
129    ItemLast,
130    ItemLastOptional,
131    PositionFirst,
132    SizeFirst,
133    Collation,
134    AnonymousClosure,
135}
136
137impl From<FunctionKind> for FunctionRule {
138    fn from(function_kind: FunctionKind) -> Self {
139        match function_kind {
140            FunctionKind::ItemFirst => FunctionRule::ItemFirst,
141            FunctionKind::ItemLast => FunctionRule::ItemLast,
142            FunctionKind::ItemLastOptional => FunctionRule::ItemLastOptional,
143            FunctionKind::Position => FunctionRule::PositionFirst,
144            FunctionKind::Size => FunctionRule::SizeFirst,
145            FunctionKind::Collation => FunctionRule::Collation,
146            FunctionKind::AnonymousClosure => FunctionRule::AnonymousClosure,
147        }
148    }
149}
150
151pub struct StaticFunction {
152    name: Name,
153    signature: function::Signature,
154    arity: usize,
155    pub function_rule: Option<FunctionRule>,
156    func: StaticFunctionType,
157}
158
159impl Debug for StaticFunction {
160    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
161        f.debug_struct("StaticFunction")
162            .field("name", &self.name)
163            .field("arity", &self.arity)
164            .field("function_rule", &self.function_rule)
165            .finish()
166    }
167}
168
169impl StaticFunction {
170    pub(crate) fn new(
171        func: StaticFunctionType,
172        name: Name,
173        signature: function::Signature,
174        function_kind: Option<FunctionKind>,
175    ) -> Self {
176        let function_rule = function_kind.map(|k| k.into());
177        let arity = signature.arity();
178        Self {
179            name,
180            signature,
181            arity,
182            function_rule,
183            func,
184        }
185    }
186
187    pub(crate) fn needs_context(&self) -> bool {
188        match self.function_rule {
189            None | Some(FunctionRule::Collation) => false,
190            Some(_) => true,
191        }
192    }
193
194    pub(crate) fn invoke(
195        &self,
196        context: &DynamicContext,
197        interpreter: &mut interpreter::Interpreter,
198        arguments: Vec<sequence::Sequence>,
199        closure_values: &[stack::Value],
200    ) -> error::Result<sequence::Sequence> {
201        if let Some(function_rule) = &self.function_rule {
202            match function_rule {
203                FunctionRule::ItemFirst | FunctionRule::PositionFirst | FunctionRule::SizeFirst => {
204                    let mut new_arguments: Vec<sequence::Sequence> =
205                        vec![closure_values[0].clone().try_into()?];
206                    new_arguments.extend(arguments);
207                    (self.func)(context, interpreter, &new_arguments)
208                }
209                FunctionRule::ItemLast => {
210                    let mut new_arguments = arguments;
211                    new_arguments.push(closure_values[0].clone().try_into()?);
212                    (self.func)(context, interpreter, &new_arguments)
213                }
214                FunctionRule::ItemLastOptional | FunctionRule::AnonymousClosure => {
215                    let mut new_arguments = arguments;
216                    let value: sequence::Sequence =
217                        if !closure_values.is_empty() && !closure_values[0].is_absent() {
218                            closure_values[0].clone().try_into()?
219                        } else {
220                            sequence::Sequence::default()
221                        };
222                    new_arguments.push(value);
223                    (self.func)(context, interpreter, &new_arguments)
224                }
225                FunctionRule::Collation => {
226                    let mut new_arguments = arguments;
227                    // the default collation query
228                    new_arguments.push(context.static_context().default_collation_uri().into());
229                    (self.func)(context, interpreter, &new_arguments)
230                }
231            }
232        } else {
233            (self.func)(context, interpreter, &arguments)
234        }
235    }
236
237    pub(crate) fn name(&self) -> Option<&Name> {
238        match self.function_rule {
239            Some(FunctionRule::AnonymousClosure) => None,
240            _ => Some(&self.name),
241        }
242    }
243
244    pub(crate) fn arity(&self) -> usize {
245        self.arity
246    }
247
248    pub(crate) fn signature(&self) -> &function::Signature {
249        &self.signature
250    }
251
252    pub fn display_representation(&self) -> String {
253        let name = self.name.full_name();
254        let signature = self.signature.display_representation();
255        format!("{}{}", name, signature)
256    }
257}
258
259fn into_sequences(values: &[stack::Value]) -> error::Result<Vec<sequence::Sequence>> {
260    values
261        .iter()
262        .map(|v| match v {
263            stack::Value::Sequence(sequence) => Ok(sequence.clone()),
264            stack::Value::Absent => Err(error::Error::XPDY0002),
265        })
266        .collect()
267}
268
269#[derive(Debug)]
270pub struct StaticFunctions {
271    by_name: HashMap<(Name, u8), function::StaticFunctionId>,
272    by_internal_name: HashMap<(Name, u8), function::StaticFunctionId>,
273    by_index: Vec<StaticFunction>,
274}
275
276impl StaticFunctions {
277    pub(crate) fn new() -> Self {
278        let mut by_name = HashMap::new();
279        let mut by_internal_name = HashMap::new();
280        let descriptions = static_function_descriptions();
281        let mut by_index = Vec::new();
282        for description in descriptions {
283            by_index.extend(description.functions());
284        }
285
286        for (i, static_function) in by_index.iter().enumerate() {
287            let map = match static_function.function_rule {
288                Some(FunctionRule::AnonymousClosure) => &mut by_internal_name,
289                _ => &mut by_name,
290            };
291            map.insert(
292                (static_function.name.clone(), static_function.arity as u8),
293                function::StaticFunctionId(i),
294            );
295        }
296        Self {
297            by_name,
298            by_internal_name,
299            by_index,
300        }
301    }
302
303    pub fn get_by_name(&self, name: &Name, arity: u8) -> Option<function::StaticFunctionId> {
304        // TODO annoying clone
305        self.by_name.get(&(name.clone(), arity)).copied()
306    }
307
308    pub fn get_by_internal_name(
309        &self,
310        name: &Name,
311        arity: u8,
312    ) -> Option<function::StaticFunctionId> {
313        // TODO annoying clone
314        self.by_internal_name.get(&(name.clone(), arity)).copied()
315    }
316
317    pub fn get_by_index(&self, static_function_id: function::StaticFunctionId) -> &StaticFunction {
318        &self.by_index[static_function_id.0]
319    }
320}