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