xee_ir/
function_compiler.rs

1use ibig::{ibig, IBig};
2
3use xee_interpreter::error::Error;
4use xee_interpreter::function::FunctionRule;
5use xee_interpreter::interpreter::instruction::Instruction;
6use xee_interpreter::span::SourceSpan;
7use xee_interpreter::{error, function, sequence};
8
9use crate::declaration_compiler::ModeIds;
10use crate::ir;
11
12use super::builder::{BackwardJumpRef, ForwardJumpRef, FunctionBuilder, JumpCondition};
13use super::scope;
14
15pub(crate) type Scopes = scope::Scopes<ir::Name>;
16
17pub struct FunctionCompiler<'a> {
18    pub(crate) scopes: &'a mut Scopes,
19    pub(crate) mode_ids: &'a ModeIds,
20    pub(crate) builder: FunctionBuilder<'a>,
21}
22
23impl<'a> FunctionCompiler<'a> {
24    pub fn new(
25        builder: FunctionBuilder<'a>,
26        scopes: &'a mut Scopes,
27        mode_ids: &'a ModeIds,
28    ) -> Self {
29        Self {
30            builder,
31            scopes,
32            mode_ids,
33        }
34    }
35
36    pub fn compile_expr(&mut self, expr: &ir::ExprS) -> error::SpannedResult<()> {
37        let span = expr.span.into();
38        match &expr.value {
39            ir::Expr::Atom(atom) => self.compile_atom(atom),
40            ir::Expr::Let(let_) => self.compile_let(let_, span),
41            ir::Expr::Binary(binary) => self.compile_binary(binary, span),
42            ir::Expr::Unary(unary) => self.compile_unary(unary, span),
43            ir::Expr::FunctionDefinition(function_definition) => {
44                self.compile_function_definition(function_definition, span)
45            }
46            ir::Expr::FunctionCall(function_call) => {
47                self.compile_function_call(function_call, span)
48            }
49            ir::Expr::Lookup(lookup) => self.compile_lookup(lookup, span),
50            ir::Expr::WildcardLookup(wildcard_lookup) => {
51                self.compile_wildcard_lookup(wildcard_lookup, span)
52            }
53            ir::Expr::Step(step) => self.compile_step(step, span),
54            ir::Expr::Deduplicate(expr) => self.compile_deduplicate(expr, span),
55            ir::Expr::If(if_) => self.compile_if(if_, span),
56            ir::Expr::Map(map) => self.compile_map(map, span),
57            ir::Expr::Filter(filter) => self.compile_filter(filter, span),
58            ir::Expr::PatternPredicate(pattern_predicate) => {
59                self.compile_pattern_predicate(pattern_predicate, span)
60            }
61            ir::Expr::Quantified(quantified) => self.compile_quantified(quantified, span),
62            ir::Expr::Cast(cast) => self.compile_cast(cast, span),
63            ir::Expr::Castable(castable) => self.compile_castable(castable, span),
64            ir::Expr::InstanceOf(instance_of) => self.compile_instance_of(instance_of, span),
65            ir::Expr::Treat(treat) => self.compile_treat(treat, span),
66            ir::Expr::MapConstructor(map_constructor) => {
67                self.compile_map_constructor(map_constructor, span)
68            }
69            ir::Expr::ArrayConstructor(array_constructor) => {
70                self.compile_array_constructor(array_constructor, span)
71            }
72            ir::Expr::XmlName(xml_name) => self.compile_xml_name(xml_name, span),
73            ir::Expr::XmlDocument(root) => self.compile_xml_document(root, span),
74            ir::Expr::XmlElement(element) => self.compile_xml_element(element, span),
75            ir::Expr::XmlAttribute(attribute) => self.compile_xml_attribute(attribute, span),
76            ir::Expr::XmlNamespace(namespace) => self.compile_xml_namespace(namespace, span),
77            ir::Expr::XmlText(text) => self.compile_xml_text(text, span),
78            ir::Expr::XmlComment(comment) => self.compile_xml_comment(comment, span),
79            ir::Expr::XmlProcessingInstruction(processing_instruction) => {
80                self.compile_xml_processing_instruction(processing_instruction, span)
81            }
82            ir::Expr::XmlAppend(xml_append) => self.compile_xml_append(xml_append, span),
83            ir::Expr::ApplyTemplates(apply_templates) => {
84                self.compile_apply_templates(apply_templates, span)
85            }
86            ir::Expr::CopyShallow(copy_shallow) => self.compile_copy_shallow(copy_shallow, span),
87            ir::Expr::CopyDeep(copy_deep) => self.compile_copy_deep(copy_deep, span),
88        }
89    }
90
91    fn compile_atom(&mut self, atom: &ir::AtomS) -> error::SpannedResult<()> {
92        let span = atom.span.into();
93        match &atom.value {
94            ir::Atom::Const(c) => {
95                match c {
96                    ir::Const::Integer(i) => {
97                        self.builder.emit_constant((i.clone()).into(), span);
98                    }
99                    ir::Const::String(s) => {
100                        self.builder.emit_constant((s).into(), span);
101                    }
102                    ir::Const::Double(d) => {
103                        self.builder.emit_constant((*d).into(), span);
104                    }
105                    ir::Const::Decimal(d) => {
106                        self.builder.emit_constant((*d).into(), span);
107                    }
108                    ir::Const::EmptySequence => self
109                        .builder
110                        .emit_constant(sequence::Sequence::default(), span),
111                    ir::Const::StaticFunctionReference(static_function_id, context_names) => {
112                        self.compile_static_function_reference(
113                            *static_function_id,
114                            context_names.as_ref(),
115                            span,
116                        )?;
117                    }
118                };
119                Ok(())
120            }
121            ir::Atom::Variable(name) => self.compile_variable(name, span),
122        }
123    }
124
125    fn compile_variable(&mut self, name: &ir::Name, span: SourceSpan) -> error::SpannedResult<()> {
126        if let Some(index) = self.scopes.get(name) {
127            if index > u16::MAX as usize {
128                return Err(Error::XPDY0130.with_span(span));
129            }
130            self.builder.emit(Instruction::Var(index as u16), span);
131            Ok(())
132        } else {
133            // if value is in any outer scopes
134            if self.scopes.is_closed_over_name(name) {
135                let index = self.builder.add_closure_name(name);
136                if index > u16::MAX as usize {
137                    return Err(Error::XPDY0130.with_span(span));
138                }
139                self.builder
140                    .emit(Instruction::ClosureVar(index as u16), span);
141                Ok(())
142            } else {
143                // TODO: this should be unreachable but
144                // the XSLT test suite for some reason triggers
145                // this condition, so for now we've hacked our way
146                // around it
147                Err(Error::Unsupported.into())
148                // unreachable!("variable not found: {:?}", name);
149            }
150        }
151    }
152
153    fn compile_variable_set(
154        &mut self,
155        name: &ir::Name,
156        span: SourceSpan,
157    ) -> error::SpannedResult<()> {
158        if let Some(index) = self.scopes.get(name) {
159            if index > u16::MAX as usize {
160                return Err(Error::XPDY0130.with_span(span));
161            }
162            self.builder.emit(Instruction::Set(index as u16), span);
163        } else {
164            panic!("can only set locals: {:?}", name);
165        }
166        Ok(())
167    }
168
169    fn compile_let(&mut self, let_: &ir::Let, span: SourceSpan) -> error::SpannedResult<()> {
170        self.compile_expr(&let_.var_expr)?;
171        self.scopes.push_name(&let_.name);
172        self.compile_expr(&let_.return_expr)?;
173        self.builder.emit(Instruction::LetDone, span);
174        self.scopes.pop_name();
175        Ok(())
176    }
177
178    fn compile_if(&mut self, if_: &ir::If, span: SourceSpan) -> error::SpannedResult<()> {
179        self.compile_atom(&if_.condition)?;
180        let jump_else = self.builder.emit_jump_forward(JumpCondition::False, span);
181        self.compile_expr(&if_.then)?;
182        let jump_end = self.builder.emit_jump_forward(JumpCondition::Always, span);
183        self.builder.patch_jump(jump_else);
184        self.compile_expr(&if_.else_)?;
185        self.builder.patch_jump(jump_end);
186        Ok(())
187    }
188
189    fn compile_binary(
190        &mut self,
191        binary: &ir::Binary,
192        span: SourceSpan,
193    ) -> error::SpannedResult<()> {
194        self.compile_atom(&binary.left)?;
195        self.compile_atom(&binary.right)?;
196        match &binary.op {
197            ir::BinaryOperator::Add => {
198                self.builder.emit(Instruction::Add, span);
199            }
200            ir::BinaryOperator::Sub => {
201                self.builder.emit(Instruction::Sub, span);
202            }
203            ir::BinaryOperator::Mul => {
204                self.builder.emit(Instruction::Mul, span);
205            }
206            ir::BinaryOperator::Div => {
207                self.builder.emit(Instruction::Div, span);
208            }
209            ir::BinaryOperator::IntDiv => {
210                self.builder.emit(Instruction::IntDiv, span);
211            }
212            ir::BinaryOperator::Mod => {
213                self.builder.emit(Instruction::Mod, span);
214            }
215            ir::BinaryOperator::ValueEq => {
216                self.builder.emit(Instruction::Eq, span);
217            }
218            ir::BinaryOperator::ValueNe => {
219                self.builder.emit(Instruction::Ne, span);
220            }
221            ir::BinaryOperator::ValueLt => {
222                self.builder.emit(Instruction::Lt, span);
223            }
224            ir::BinaryOperator::ValueLe => {
225                self.builder.emit(Instruction::Le, span);
226            }
227            ir::BinaryOperator::ValueGt => {
228                self.builder.emit(Instruction::Gt, span);
229            }
230            ir::BinaryOperator::ValueGe => {
231                self.builder.emit(Instruction::Ge, span);
232            }
233            ir::BinaryOperator::GenEq => {
234                self.builder.emit(Instruction::GenEq, span);
235            }
236            ir::BinaryOperator::GenNe => {
237                self.builder.emit(Instruction::GenNe, span);
238            }
239            ir::BinaryOperator::GenLt => {
240                self.builder.emit(Instruction::GenLt, span);
241            }
242            ir::BinaryOperator::GenLe => {
243                self.builder.emit(Instruction::GenLe, span);
244            }
245            ir::BinaryOperator::GenGt => {
246                self.builder.emit(Instruction::GenGt, span);
247            }
248            ir::BinaryOperator::GenGe => {
249                self.builder.emit(Instruction::GenGe, span);
250            }
251            ir::BinaryOperator::Comma => {
252                self.builder.emit(Instruction::Comma, span);
253            }
254            ir::BinaryOperator::Union => {
255                self.builder.emit(Instruction::Union, span);
256            }
257            ir::BinaryOperator::Intersect => {
258                self.builder.emit(Instruction::Intersect, span);
259            }
260            ir::BinaryOperator::Except => {
261                self.builder.emit(Instruction::Except, span);
262            }
263            ir::BinaryOperator::Range => {
264                self.builder.emit(Instruction::Range, span);
265            }
266            ir::BinaryOperator::Concat => {
267                self.builder.emit(Instruction::Concat, span);
268            }
269            ir::BinaryOperator::And => {
270                // XXX we don't do any short-circuiting of evaluation yet
271                let first_false = self.builder.emit_jump_forward(JumpCondition::False, span);
272                let second_false = self.builder.emit_jump_forward(JumpCondition::False, span);
273                // both are true, so put true on stack and jump to end
274                self.builder.emit_constant(true.into(), span);
275                let end = self.builder.emit_jump_forward(JumpCondition::Always, span);
276                self.builder.patch_jump(first_false);
277                // pop the second item on the stack
278                self.builder.emit(Instruction::Pop, span);
279                self.builder.patch_jump(second_false);
280                // now put false on the stack
281                self.builder.emit_constant(false.into(), span);
282                self.builder.patch_jump(end);
283            }
284            ir::BinaryOperator::Or => {
285                // XXX we don't do any short-circuiting of evaluation yet
286                let first_true = self.builder.emit_jump_forward(JumpCondition::True, span);
287                let second_true = self.builder.emit_jump_forward(JumpCondition::True, span);
288                // both are false, so put false on stack and jump to end
289                self.builder.emit_constant(false.into(), span);
290                let end = self.builder.emit_jump_forward(JumpCondition::Always, span);
291                // if first is true, pop second
292                self.builder.patch_jump(first_true);
293                // pop the second item on the stack
294                self.builder.emit(Instruction::Pop, span);
295                self.builder.patch_jump(second_true);
296                // now put true on the stack
297                self.builder.emit_constant(true.into(), span);
298                self.builder.patch_jump(end);
299            }
300            ir::BinaryOperator::Is => {
301                self.builder.emit(Instruction::Is, span);
302            }
303            ir::BinaryOperator::Precedes => {
304                self.builder.emit(Instruction::Precedes, span);
305            }
306            ir::BinaryOperator::Follows => {
307                self.builder.emit(Instruction::Follows, span);
308            }
309        }
310        Ok(())
311    }
312
313    fn compile_unary(&mut self, unary: &ir::Unary, span: SourceSpan) -> error::SpannedResult<()> {
314        self.compile_atom(&unary.atom)?;
315        match unary.op {
316            ir::UnaryOperator::Plus => {
317                self.builder.emit(Instruction::Plus, span);
318            }
319            ir::UnaryOperator::Minus => {
320                self.builder.emit(Instruction::Minus, span);
321            }
322        }
323        Ok(())
324    }
325
326    pub fn compile_function_id(
327        &mut self,
328        function_definition: &ir::FunctionDefinition,
329        span: SourceSpan,
330    ) -> error::SpannedResult<function::InlineFunctionId> {
331        let nested_builder = self.builder.builder();
332        self.scopes.push_scope();
333
334        let mut compiler = FunctionCompiler {
335            builder: nested_builder,
336            scopes: self.scopes,
337            mode_ids: self.mode_ids,
338        };
339
340        for param in &function_definition.params {
341            compiler.scopes.push_name(&param.name);
342        }
343        compiler.compile_expr(&function_definition.body)?;
344        for _ in &function_definition.params {
345            compiler.scopes.pop_name();
346        }
347
348        compiler.scopes.pop_scope();
349
350        let function = compiler
351            .builder
352            .finish("inline".to_string(), function_definition, span);
353        // now place all captured names on stack, to ensure we have the
354        // closure
355        // in reverse order so we can pop them off in the right order
356        for name in function.closure_names.iter().rev() {
357            self.compile_variable(name, span)?;
358        }
359        Ok(self.builder.add_function(function))
360    }
361
362    pub(crate) fn compile_function_definition(
363        &mut self,
364        function_definition: &ir::FunctionDefinition,
365        span: SourceSpan,
366    ) -> error::SpannedResult<()> {
367        let function_id = self.compile_function_id(function_definition, span)?;
368        self.builder
369            .emit(Instruction::Closure(function_id.as_u16()), span);
370        Ok(())
371    }
372
373    fn compile_static_function_reference(
374        &mut self,
375        static_function_id: function::StaticFunctionId,
376        context_names: Option<&ir::ContextNames>,
377        span: SourceSpan,
378    ) -> error::SpannedResult<()> {
379        let static_function = self
380            .builder
381            .static_context()
382            .function_by_id(static_function_id);
383        match static_function.function_rule {
384            Some(FunctionRule::ItemFirst) => {
385                let context_names = context_names.ok_or(Error::XPDY0002.with_span(span))?;
386                self.compile_variable(&context_names.item, span)?
387            }
388            Some(FunctionRule::ItemLast) => {
389                let context_names = context_names.ok_or(Error::XPDY0002.with_span(span))?;
390                self.compile_variable(&context_names.item, span)?
391            }
392            Some(FunctionRule::ItemLastOptional) => {
393                if let Some(context_names) = context_names {
394                    self.compile_variable(&context_names.item, span)?;
395                } else {
396                    self.builder
397                        .emit_constant(sequence::Sequence::default(), span);
398                }
399            }
400            Some(FunctionRule::PositionFirst) => self.compile_variable(
401                {
402                    let context_names = context_names.ok_or(Error::XPDY0002.with_span(span))?;
403                    &context_names.position
404                },
405                span,
406            )?,
407            Some(FunctionRule::SizeFirst) => {
408                let context_names = context_names.ok_or(Error::XPDY0002.with_span(span))?;
409                self.compile_variable(&context_names.last, span)?
410            }
411            Some(FunctionRule::Collation) | Some(FunctionRule::AnonymousClosure) | None => {}
412        }
413        self.builder.emit(
414            Instruction::StaticClosure(static_function_id.as_u16()),
415            span,
416        );
417        Ok(())
418    }
419
420    fn compile_function_call(
421        &mut self,
422        function_call: &ir::FunctionCall,
423        span: SourceSpan,
424    ) -> error::SpannedResult<()> {
425        self.compile_atom(&function_call.atom)?;
426        for arg in &function_call.args {
427            self.compile_atom(arg)?;
428        }
429        self.builder
430            .emit(Instruction::Call(function_call.args.len() as u8), span);
431        Ok(())
432    }
433
434    fn compile_lookup(
435        &mut self,
436        lookup: &ir::Lookup,
437        span: SourceSpan,
438    ) -> error::SpannedResult<()> {
439        self.compile_atom(&lookup.atom)?;
440        self.compile_atom(&lookup.arg_atom)?;
441        self.builder.emit(Instruction::Lookup, span);
442        Ok(())
443    }
444
445    fn compile_wildcard_lookup(
446        &mut self,
447        lookup: &ir::WildcardLookup,
448        span: SourceSpan,
449    ) -> error::SpannedResult<()> {
450        self.compile_atom(&lookup.atom)?;
451        self.builder.emit(Instruction::WildcardLookup, span);
452        Ok(())
453    }
454
455    fn compile_step(&mut self, step: &ir::Step, span: SourceSpan) -> error::SpannedResult<()> {
456        self.compile_atom(&step.context)?;
457        let step_id = self.builder.add_step(step.step.clone());
458        self.builder.emit(Instruction::Step(step_id as u16), span);
459        Ok(())
460    }
461
462    fn compile_deduplicate(
463        &mut self,
464        expr: &ir::ExprS,
465        span: SourceSpan,
466    ) -> error::SpannedResult<()> {
467        self.compile_expr(expr)?;
468        self.builder.emit(Instruction::Deduplicate, span);
469        Ok(())
470    }
471
472    fn compile_cast(&mut self, cast: &ir::Cast, span: SourceSpan) -> error::SpannedResult<()> {
473        self.compile_atom(&cast.atom)?;
474        let cast_type = cast.cast_type();
475        let cast_type_id = self.builder.add_cast_type(cast_type);
476        self.builder
477            .emit(Instruction::Cast(cast_type_id as u16), span);
478        Ok(())
479    }
480
481    fn compile_castable(
482        &mut self,
483        castable: &ir::Castable,
484        span: SourceSpan,
485    ) -> error::SpannedResult<()> {
486        self.compile_atom(&castable.atom)?;
487        let cast_type = castable.cast_type();
488        let cast_type_id = self.builder.add_cast_type(cast_type);
489        self.builder
490            .emit(Instruction::Castable(cast_type_id as u16), span);
491        Ok(())
492    }
493
494    fn compile_instance_of(
495        &mut self,
496        instance_of: &ir::InstanceOf,
497        span: SourceSpan,
498    ) -> error::SpannedResult<()> {
499        self.compile_atom(&instance_of.atom)?;
500        let sequence_type_id = self
501            .builder
502            .add_sequence_type(instance_of.sequence_type.clone());
503        self.builder
504            .emit(Instruction::InstanceOf(sequence_type_id as u16), span);
505        Ok(())
506    }
507
508    fn compile_treat(&mut self, treat: &ir::Treat, span: SourceSpan) -> error::SpannedResult<()> {
509        self.compile_atom(&treat.atom)?;
510        let sequence_type_id = self.builder.add_sequence_type(treat.sequence_type.clone());
511        self.builder
512            .emit(Instruction::Treat(sequence_type_id as u16), span);
513        Ok(())
514    }
515
516    fn compile_map_constructor(
517        &mut self,
518        map_constructor: &ir::MapConstructor,
519        span: SourceSpan,
520    ) -> error::SpannedResult<()> {
521        // compile them in reverse, so we can pop them in the right
522        // order during runtime. It matters less with a map, but may
523        // still be important for consistent duplicate key detection.
524        for (key_atom, value_atom) in map_constructor.members.iter().rev() {
525            self.compile_atom(key_atom)?;
526            self.compile_atom(value_atom)?;
527        }
528        // emit constant with size of map
529        let len: IBig = map_constructor.members.len().into();
530        let len: sequence::Sequence = len.into();
531        self.builder.emit_constant(len, span);
532        self.builder.emit(Instruction::CurlyMap, span);
533        Ok(())
534    }
535
536    fn compile_array_constructor(
537        &mut self,
538        array_constructor: &ir::ArrayConstructor,
539        span: SourceSpan,
540    ) -> error::SpannedResult<()> {
541        match array_constructor {
542            ir::ArrayConstructor::Curly(atom) => {
543                self.compile_curly_array_constructor(atom, span)?;
544            }
545            ir::ArrayConstructor::Square(atoms) => {
546                self.compile_square_array_constructor(atoms, span)?;
547            }
548        }
549        Ok(())
550    }
551
552    fn compile_curly_array_constructor(
553        &mut self,
554        atom: &ir::AtomS,
555        span: SourceSpan,
556    ) -> error::SpannedResult<()> {
557        self.compile_atom(atom)?;
558        self.builder.emit(Instruction::CurlyArray, span);
559        Ok(())
560    }
561
562    fn compile_square_array_constructor(
563        &mut self,
564        atoms: &[ir::AtomS],
565        span: SourceSpan,
566    ) -> error::SpannedResult<()> {
567        // compile them in reverse, so we can pop them in the right
568        // order during runtime
569        for atom in atoms.iter().rev() {
570            self.compile_atom(atom)?;
571        }
572        // emit constant with length of array
573        let len: IBig = atoms.len().into();
574        let len: sequence::Sequence = len.into();
575        self.builder.emit_constant(len, span);
576        self.builder.emit(Instruction::SquareArray, span);
577        Ok(())
578    }
579
580    fn compile_map(&mut self, map: &ir::Map, span: SourceSpan) -> error::SpannedResult<()> {
581        // create new build sequence on build stack
582        self.builder.emit(Instruction::BuildNew, span);
583
584        let (loop_start, loop_end) =
585            self.compile_sequence_loop_init(&map.var_atom, &map.context_names, span)?;
586
587        self.compile_sequence_get_item(&map.var_atom, &map.context_names, span)?;
588        // name it
589        self.scopes.push_name(&map.context_names.item);
590        // execute the map expression, placing result on stack
591        self.compile_expr(&map.return_expr)?;
592        self.scopes.pop_name();
593
594        // push result to build
595        self.builder.emit(Instruction::BuildPush, span);
596
597        // clean up the var_name item
598        self.builder.emit(Instruction::Pop, span);
599
600        self.compile_sequence_loop_iterate(loop_start, &map.context_names, span)?;
601
602        self.builder.patch_jump(loop_end);
603        self.compile_sequence_loop_end(span);
604
605        self.builder.emit(Instruction::BuildComplete, span);
606        // pop sequence length name & index;
607        self.scopes.pop_name();
608        self.scopes.pop_name();
609        Ok(())
610    }
611
612    fn compile_filter(
613        &mut self,
614        filter: &ir::Filter,
615        span: SourceSpan,
616    ) -> error::SpannedResult<()> {
617        // create new build sequence on build stack
618        self.builder.emit(Instruction::BuildNew, span);
619
620        let (loop_start, loop_end) =
621            self.compile_sequence_loop_init(&filter.var_atom, &filter.context_names, span)?;
622
623        // place item to filter on stack
624        self.compile_sequence_get_item(&filter.var_atom, &filter.context_names, span)?;
625        // name it
626        self.scopes.push_name(&filter.context_names.item);
627        // execute the filter expression, placing result on stack
628        self.compile_expr(&filter.return_expr)?;
629        self.scopes.pop_name();
630        // duplicate result so we can do the IsNumeric check
631        self.builder.emit(Instruction::Dup, span);
632        // the resulting value can be a numeric value
633        self.builder.emit(Instruction::IsNumeric, span);
634        // if it's not a numeric expression we're going to interpret it as boolean,
635        // a normal filter
636        let is_not_numeric = self.builder.emit_jump_forward(JumpCondition::False, span);
637        // It was numeric, we have on the stack a position to compare with
638        self.compile_variable(&filter.context_names.position, span)?;
639        self.builder.emit(Instruction::Eq, span);
640        // Now we have a boolean on the stack: a normal filter
641
642        // We take the effective boolean value of the result
643        // if filter is false, we skip this item
644        self.builder.patch_jump(is_not_numeric);
645        let is_included = self.builder.emit_jump_forward(JumpCondition::True, span);
646        // we need to clean up the stack after this
647        self.builder.emit(Instruction::Pop, span);
648        // and iterate the loop
649        let iterate = self.builder.emit_jump_forward(JumpCondition::Always, span);
650
651        self.builder.patch_jump(is_included);
652        // push item to new build
653        self.builder.emit(Instruction::BuildPush, span);
654
655        self.builder.patch_jump(iterate);
656        // no need to clean up the stack, as filter get is pushed onto sequence
657        self.compile_sequence_loop_iterate(loop_start, &filter.context_names, span)?;
658
659        self.builder.patch_jump(loop_end);
660        self.compile_sequence_loop_end(span);
661
662        self.builder.emit(Instruction::BuildComplete, span);
663        // pop new sequence length name & index
664        self.scopes.pop_name();
665        self.scopes.pop_name();
666        Ok(())
667    }
668
669    fn compile_quantified(
670        &mut self,
671        quantified: &ir::Quantified,
672        span: SourceSpan,
673    ) -> error::SpannedResult<()> {
674        let (loop_start, loop_end) =
675            self.compile_sequence_loop_init(&quantified.var_atom, &quantified.context_names, span)?;
676
677        self.compile_sequence_get_item(&quantified.var_atom, &quantified.context_names, span)?;
678        // name it
679        self.scopes.push_name(&quantified.context_names.item);
680        // execute the satisfies expression, placing result in on stack
681        self.compile_expr(&quantified.satisifies_expr)?;
682        self.scopes.pop_name();
683
684        let jump_out_end = match quantified.quantifier {
685            ir::Quantifier::Some => self.builder.emit_jump_forward(JumpCondition::True, span),
686            ir::Quantifier::Every => self.builder.emit_jump_forward(JumpCondition::False, span),
687        };
688        // we didn't jump out, clean up quantifier variable
689        self.builder.emit(Instruction::Pop, span);
690
691        self.compile_sequence_loop_iterate(loop_start, &quantified.context_names, span)?;
692
693        self.builder.patch_jump(loop_end);
694
695        // if we reached the end, without jumping out
696        self.compile_sequence_loop_end(span);
697
698        let reached_end_value = match quantified.quantifier {
699            ir::Quantifier::Some => false.into(),
700            ir::Quantifier::Every => true.into(),
701        };
702        self.builder.emit_constant(reached_end_value, span);
703        let end = self.builder.emit_jump_forward(JumpCondition::Always, span);
704
705        // we jumped out
706        self.builder.patch_jump(jump_out_end);
707        // clean up quantifier variable
708        self.builder.emit(Instruction::Pop, span);
709        self.compile_sequence_loop_end(span);
710
711        let jumped_out_value = match quantified.quantifier {
712            ir::Quantifier::Some => true.into(),
713            ir::Quantifier::Every => false.into(),
714        };
715        // if we jumped out, we set satisfies to true
716        self.builder.emit_constant(jumped_out_value, span);
717
718        self.builder.patch_jump(end);
719        // pop sequence length name & index
720        self.scopes.pop_name();
721        self.scopes.pop_name();
722        Ok(())
723    }
724
725    fn compile_sequence_loop_init(
726        &mut self,
727        atom: &ir::AtomS,
728        context_names: &ir::ContextNames,
729        span: SourceSpan,
730    ) -> error::SpannedResult<(BackwardJumpRef, ForwardJumpRef)> {
731        //  sequence length
732        self.compile_atom(atom)?;
733        self.scopes.push_name(&context_names.last);
734        self.builder.emit(Instruction::SequenceLen, span);
735
736        // place index on stack
737        self.builder.emit_constant(ibig!(1).into(), span);
738        self.scopes.push_name(&context_names.position);
739
740        let loop_start_ref = self.builder.loop_start();
741
742        // compare with sequence length, if index is gt length, we're done with the loop
743        self.compile_variable(&context_names.position, span)?;
744        self.compile_variable(&context_names.last, span)?;
745        self.builder.emit(Instruction::Gt, span);
746        // check whether index is gt length, if so, we're done with the loop
747        let loop_end_ref = self.builder.emit_jump_forward(JumpCondition::True, span);
748
749        Ok((loop_start_ref, loop_end_ref))
750    }
751
752    fn compile_sequence_get_item(
753        &mut self,
754        atom: &ir::AtomS,
755        context_names: &ir::ContextNames,
756        span: SourceSpan,
757    ) -> error::SpannedResult<()> {
758        // get item at the index
759        self.compile_variable(&context_names.position, span)?;
760        self.compile_atom(atom)?;
761        self.builder.emit(Instruction::SequenceGet, span);
762        Ok(())
763    }
764
765    fn compile_sequence_loop_iterate(
766        &mut self,
767        loop_start: BackwardJumpRef,
768        context_names: &ir::ContextNames,
769        span: SourceSpan,
770    ) -> error::SpannedResult<()> {
771        // update index with 1
772        self.compile_variable(&context_names.position, span)?;
773        self.builder.emit_constant(ibig!(1).into(), span);
774        self.builder.emit(Instruction::Add, span);
775        self.compile_variable_set(&context_names.position, span)?;
776        self.builder
777            .emit_jump_backward(loop_start, JumpCondition::Always, span);
778        Ok(())
779    }
780
781    fn compile_sequence_loop_end(&mut self, span: SourceSpan) {
782        // pop length and index
783        self.builder.emit(Instruction::Pop, span);
784        self.builder.emit(Instruction::Pop, span);
785    }
786
787    fn compile_xml_name(
788        &mut self,
789        xml_name: &ir::XmlName,
790        span: SourceSpan,
791    ) -> error::SpannedResult<()> {
792        self.compile_atom(&xml_name.namespace)?;
793        self.compile_atom(&xml_name.local_name)?;
794        self.builder.emit(Instruction::XmlName, span);
795        Ok(())
796    }
797
798    fn compile_xml_document(
799        &mut self,
800        _root: &ir::XmlRoot,
801        span: SourceSpan,
802    ) -> error::SpannedResult<()> {
803        self.builder.emit(Instruction::XmlDocument, span);
804        Ok(())
805    }
806
807    fn compile_xml_element(
808        &mut self,
809        element: &ir::XmlElement,
810        span: SourceSpan,
811    ) -> error::SpannedResult<()> {
812        self.compile_atom(&element.name)?;
813        self.builder.emit(Instruction::XmlElement, span);
814        Ok(())
815    }
816
817    fn compile_xml_attribute(
818        &mut self,
819        attribute: &ir::XmlAttribute,
820        span: SourceSpan,
821    ) -> error::SpannedResult<()> {
822        self.compile_atom(&attribute.name)?;
823        self.compile_atom(&attribute.value)?;
824        self.builder.emit(Instruction::XmlAttribute, span);
825        Ok(())
826    }
827
828    fn compile_xml_namespace(
829        &mut self,
830        prefix: &ir::XmlNamespace,
831        span: SourceSpan,
832    ) -> error::SpannedResult<()> {
833        self.compile_atom(&prefix.prefix)?;
834        self.compile_atom(&prefix.namespace)?;
835        self.builder.emit(Instruction::XmlNamespace, span);
836        Ok(())
837    }
838
839    fn compile_xml_text(
840        &mut self,
841        text: &ir::XmlText,
842        span: SourceSpan,
843    ) -> error::SpannedResult<()> {
844        // self.compile_atom(&text.element)?;
845        self.compile_atom(&text.value)?;
846        self.builder.emit(Instruction::XmlText, span);
847        Ok(())
848    }
849
850    fn compile_xml_append(
851        &mut self,
852        append: &ir::XmlAppend,
853        span: SourceSpan,
854    ) -> error::SpannedResult<()> {
855        self.compile_atom(&append.parent)?;
856        self.compile_atom(&append.child)?;
857        self.builder.emit(Instruction::XmlAppend, span);
858        Ok(())
859    }
860
861    fn compile_xml_comment(
862        &mut self,
863        comment: &ir::XmlComment,
864        span: SourceSpan,
865    ) -> error::SpannedResult<()> {
866        self.compile_atom(&comment.value)?;
867        self.builder.emit(Instruction::XmlComment, span);
868        Ok(())
869    }
870
871    fn compile_xml_processing_instruction(
872        &mut self,
873        processing_instruction: &ir::XmlProcessingInstruction,
874        span: SourceSpan,
875    ) -> error::SpannedResult<()> {
876        self.compile_atom(&processing_instruction.target)?;
877        self.compile_atom(&processing_instruction.content)?;
878        self.builder
879            .emit(Instruction::XmlProcessingInstruction, span);
880        Ok(())
881    }
882
883    fn compile_apply_templates(
884        &mut self,
885        apply_templates: &ir::ApplyTemplates,
886        span: SourceSpan,
887    ) -> error::SpannedResult<()> {
888        self.compile_atom(&apply_templates.select)?;
889
890        let mode_id = if matches!(
891            apply_templates.mode,
892            ir::ApplyTemplatesModeValue::Named(_) | ir::ApplyTemplatesModeValue::Unnamed
893        ) {
894            self.mode_ids.get(&apply_templates.mode)
895        } else {
896            todo!("#current mode not handled yet")
897        };
898        if let Some(mode_id) = mode_id {
899            self.builder
900                .emit(Instruction::ApplyTemplates(mode_id.get() as u16), span);
901        } else {
902            // the mode was never used by any templates, so compile the empty
903            // sequence
904            self.builder
905                .emit_constant(sequence::Sequence::default(), span);
906        }
907        Ok(())
908    }
909
910    fn compile_copy_shallow(
911        &mut self,
912        copy_shallow: &ir::CopyShallow,
913        span: SourceSpan,
914    ) -> error::SpannedResult<()> {
915        self.compile_atom(&copy_shallow.select)?;
916        self.builder.emit(Instruction::CopyShallow, span);
917        Ok(())
918    }
919
920    fn compile_copy_deep(
921        &mut self,
922        copy_deep: &ir::CopyDeep,
923        span: SourceSpan,
924    ) -> error::SpannedResult<()> {
925        self.compile_atom(&copy_deep.select)?;
926        self.builder.emit(Instruction::CopyDeep, span);
927        Ok(())
928    }
929
930    fn compile_pattern_predicate(
931        &mut self,
932        predicate: &ir::PatternPredicate,
933        span: SourceSpan,
934    ) -> error::SpannedResult<()> {
935        // execute the expression, placing result on stack
936        self.compile_expr(&predicate.expr)?;
937        // duplicate result so we can do the IsNumeric check
938        self.builder.emit(Instruction::Dup, span);
939        // the resulting value can be a numeric value
940        self.builder.emit(Instruction::IsNumeric, span);
941        // if it's not a numeric expression we're going to interpret it as boolean,
942        // a normal filter
943        let is_not_numeric = self.builder.emit_jump_forward(JumpCondition::False, span);
944        // It was numeric, we have on the stack a position to compare with
945        self.compile_variable(&predicate.context_names.position, span)?;
946        self.builder.emit(Instruction::Eq, span);
947        self.builder.patch_jump(is_not_numeric);
948        Ok(())
949    }
950}