xee_interpreter/function/
static_function.rs1use 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 ItemFirst,
21 ItemLast,
24 ItemLastOptional,
27 Position,
29 Size,
31 Collation,
34 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#[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 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 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 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 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}