xee_ir/
builder.rs

1use xee_xpath_ast::ast;
2
3use xee_interpreter::interpreter::instruction::{
4    encode_instruction, instruction_size, Instruction,
5};
6use xee_interpreter::{context, function, interpreter, sequence, span, xml};
7
8use crate::ir;
9
10#[must_use]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub(crate) struct ForwardJumpRef(usize);
13
14#[must_use]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub(crate) struct BackwardJumpRef(usize);
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub(crate) enum JumpCondition {
20    Always,
21    True,
22    False,
23}
24
25pub struct FunctionBuilder<'a> {
26    program: &'a mut interpreter::Program,
27    compiled: Vec<u8>,
28    spans: Vec<span::SourceSpan>,
29    constants: Vec<sequence::Sequence>,
30    steps: Vec<xml::Step>,
31    cast_types: Vec<function::CastType>,
32    sequence_types: Vec<ast::SequenceType>,
33    closure_names: Vec<ir::Name>,
34}
35
36impl<'a> FunctionBuilder<'a> {
37    pub fn new(program: &'a mut interpreter::Program) -> Self {
38        FunctionBuilder {
39            program,
40            compiled: Vec::new(),
41            spans: Vec::new(),
42            constants: Vec::new(),
43            steps: Vec::new(),
44            cast_types: Vec::new(),
45            sequence_types: Vec::new(),
46            closure_names: Vec::new(),
47        }
48    }
49
50    pub(crate) fn static_context(&self) -> &context::StaticContext {
51        self.program.static_context()
52    }
53
54    pub(crate) fn emit(&mut self, instruction: Instruction, span: span::SourceSpan) {
55        for _ in 0..instruction_size(&instruction) {
56            self.spans.push(span);
57        }
58        encode_instruction(instruction, &mut self.compiled);
59    }
60
61    pub(crate) fn emit_constant(&mut self, constant: sequence::Sequence, span: span::SourceSpan) {
62        let constant_id = self.constants.len();
63        self.constants.push(constant);
64        if constant_id > (u16::MAX as usize) {
65            panic!("too many constants");
66        }
67        self.emit(Instruction::Const(constant_id as u16), span);
68    }
69
70    pub(crate) fn add_closure_name(&mut self, name: &ir::Name) -> usize {
71        let found = self.closure_names.iter().position(|n| n == name);
72        if let Some(index) = found {
73            return index;
74        }
75        let index = self.closure_names.len();
76        self.closure_names.push(name.clone());
77        if index > (u16::MAX as usize) {
78            panic!("too many closure names");
79        }
80        index
81    }
82
83    pub(crate) fn add_step(&mut self, step: xml::Step) -> usize {
84        let step_id = self.steps.len();
85        self.steps.push(step);
86        if step_id > (u16::MAX as usize) {
87            panic!("too many steps");
88        }
89        step_id
90    }
91
92    pub(crate) fn add_cast_type(&mut self, cast_type: function::CastType) -> usize {
93        let cast_type_id = self.cast_types.len();
94        self.cast_types.push(cast_type);
95        if cast_type_id > (u16::MAX as usize) {
96            panic!("too many cast types");
97        }
98        cast_type_id
99    }
100
101    pub(crate) fn add_sequence_type(&mut self, sequence_type: ast::SequenceType) -> usize {
102        let sequence_type_id = self.sequence_types.len();
103        self.sequence_types.push(sequence_type);
104        if sequence_type_id > (u16::MAX as usize) {
105            panic!("too many sequence types");
106        }
107        sequence_type_id
108    }
109
110    pub(crate) fn loop_start(&self) -> BackwardJumpRef {
111        BackwardJumpRef(self.compiled.len())
112    }
113
114    pub(crate) fn emit_jump_backward(
115        &mut self,
116        jump_ref: BackwardJumpRef,
117        condition: JumpCondition,
118        span: span::SourceSpan,
119    ) {
120        let current = self.compiled.len() + 3;
121        let offset = current - jump_ref.0;
122        if jump_ref.0 > current {
123            panic!("cannot jump forward");
124        }
125        if offset > (u16::MAX as usize) {
126            panic!("jump too far");
127        }
128
129        match condition {
130            JumpCondition::True => self.emit(Instruction::JumpIfTrue(-(offset as i16)), span),
131            JumpCondition::False => self.emit(Instruction::JumpIfFalse(-(offset as i16)), span),
132            JumpCondition::Always => self.emit(Instruction::Jump(-(offset as i16)), span),
133        }
134    }
135
136    pub(crate) fn emit_jump_forward(
137        &mut self,
138        condition: JumpCondition,
139        span: span::SourceSpan,
140    ) -> ForwardJumpRef {
141        let index = self.compiled.len();
142        match condition {
143            JumpCondition::True => self.emit(Instruction::JumpIfTrue(0), span),
144            JumpCondition::False => self.emit(Instruction::JumpIfFalse(0), span),
145            JumpCondition::Always => self.emit(Instruction::Jump(0), span),
146        }
147        ForwardJumpRef(index)
148    }
149
150    pub(crate) fn patch_jump(&mut self, jump_ref: ForwardJumpRef) {
151        let current = self.compiled.len();
152        if jump_ref.0 > current {
153            panic!("can only patch forward jumps");
154        }
155        let offset = current - jump_ref.0 - 3; // 3 for size of the jump
156        if offset > (u16::MAX as usize) {
157            panic!("jump too far");
158        }
159        let offset_bytes = offset.to_le_bytes();
160        self.compiled[jump_ref.0 + 1] = offset_bytes[0];
161        self.compiled[jump_ref.0 + 2] = offset_bytes[1];
162    }
163
164    pub(crate) fn finish(
165        mut self,
166        name: String,
167        function_definition: &ir::FunctionDefinition,
168        span: span::SourceSpan,
169    ) -> function::InlineFunction {
170        if let Some(return_type) = &function_definition.return_type {
171            let sequence_type_id = self.add_sequence_type(return_type.clone());
172            if sequence_type_id > (u16::MAX as usize) {
173                panic!("too many sequence types");
174            }
175            self.emit(Instruction::ReturnConvert(sequence_type_id as u16), span);
176        }
177        self.emit(Instruction::Return, span);
178        function::InlineFunction {
179            name,
180            signature: function_definition.signature(),
181            chunk: self.compiled,
182            spans: self.spans,
183            closure_names: self.closure_names,
184            constants: self.constants,
185            steps: self.steps,
186            cast_types: self.cast_types,
187            sequence_types: self.sequence_types,
188        }
189    }
190
191    pub(crate) fn builder(&mut self) -> FunctionBuilder {
192        FunctionBuilder::new(self.program)
193    }
194
195    pub(crate) fn add_function(
196        &mut self,
197        function: function::InlineFunction,
198    ) -> function::InlineFunctionId {
199        self.program.add_function(function)
200    }
201}