1use cel_runtime::{DynSegment, ValueType};
7
8use crate::{
9 DynSegmentContext, Expr, ExprSpan, Literal, LogicalOp, OpLookup, ParseError, ParserContext,
10 Result, SourceSpan, TypeResolver,
11};
12
13pub fn compile_expr<F>(
48 expr: &Expr,
49 lookup: &OpLookup,
50 types: &dyn TypeResolver,
51 resolve: &mut F,
52) -> Result<DynSegment>
53where
54 F: FnMut(&str, &mut DynSegment, SourceSpan) -> anyhow::Result<bool>,
55{
56 let mut context = DynSegmentContext::new_context();
57 emit(expr, &mut context, lookup, types, resolve, None)?;
58 Ok(context.into_inner())
59}
60
61fn emit<F>(
70 expr: &Expr,
71 context: &mut DynSegmentContext,
72 lookup: &OpLookup,
73 types: &dyn TypeResolver,
74 resolve: &mut F,
75 locals: Option<&[(&str, ValueType)]>,
76) -> Result<()>
77where
78 F: FnMut(&str, &mut DynSegment, SourceSpan) -> anyhow::Result<bool>,
79{
80 let span = expr.span();
81 let error = |message: String| ParseError::new_range(message, span.start, span.end);
82 match expr {
83 Expr::Literal { value, .. } => push_literal(context, value, span)?,
84 Expr::Ident { name, .. } => {
85 if let Some(locals) = locals {
86 if let Some((index, (_, ty))) = locals
87 .iter()
88 .enumerate()
89 .rev()
90 .find(|(_, (local, _))| *local == name)
91 {
92 return ty
93 .push_argument(context, index)
94 .map_err(|e| error(e.to_string()));
95 }
96 } else if resolve(
97 name,
98 context,
99 SourceSpan::from_proc_macro2_range(span.start, span.end),
100 )
101 .map_err(|e| error(e.to_string()))?
102 {
103 return Ok(());
104 }
105 context.apply_scoped_op(lookup, name, 0, span, false, locals.is_some())?;
106 }
107 Expr::Op { name, operands, .. } => {
108 for operand in operands {
109 emit(operand, context, lookup, types, resolve, locals)?;
110 }
111 context.apply_scoped_op(lookup, name, operands.len(), span, false, locals.is_some())?;
112 }
113 Expr::Apply { callee, args, .. } => {
114 emit(callee, context, lookup, types, resolve, locals)?;
115 for arg in args {
116 emit(arg, context, lookup, types, resolve, locals)?;
117 }
118 context.apply_scoped_op(lookup, "()", args.len() + 1, span, false, locals.is_some())?;
119 }
120 Expr::Member {
121 receiver,
122 name,
123 name_span,
124 args,
125 ..
126 } => {
127 emit(receiver, context, lookup, types, resolve, locals)?;
128 if let Some(args) = args {
129 for arg in args {
130 emit(arg, context, lookup, types, resolve, locals)?;
131 }
132 }
133 context.apply_scoped_op(
134 lookup,
135 name,
136 1 + args.as_ref().map_or(0, Vec::len),
137 *name_span,
138 true,
139 locals.is_some(),
140 )?;
141 }
142 Expr::Struct {
143 name,
144 name_span,
145 fields,
146 ..
147 } => {
148 let ambient = context.current_stack_offset();
149 for field in fields {
150 emit(&field.value, context, lookup, types, resolve, locals)?;
151 }
152 context.make_record(
153 lookup,
154 name,
155 *name_span,
156 fields
157 .iter()
158 .map(|field| (field.name.clone(), field.name_span))
159 .collect(),
160 ambient,
161 span.start,
162 span.end,
163 )?;
164 }
165 Expr::Tuple { elements, .. } => {
166 let ambient = context.current_stack_offset();
167 for element in elements {
168 emit(element, context, lookup, types, resolve, locals)?;
169 }
170 context.make_tuple(elements.len(), ambient, span.start, span.end)?;
171 }
172 Expr::Array {
173 elements,
174 type_annotation,
175 ..
176 } => {
177 let ambient = context.current_stack_offset();
178 for element in elements {
179 emit(element, context, lookup, types, resolve, locals)?;
180 }
181 let element_type = type_annotation
182 .as_ref()
183 .map(|ty| {
184 crate::ResolvedArrayType::from_resolved_type(ty.resolve(types)?, ty.span())
185 .map(|resolved| resolved.into_element_type())
186 })
187 .transpose()?;
188 context.make_resolved_array(elements.len(), ambient, element_type, span)?;
189 }
190 Expr::TupleIndex { base, index, .. } => {
191 emit(base, context, lookup, types, resolve, locals)?;
192 context.tuple_index(*index, span.start, span.end)?;
193 }
194 Expr::If {
195 cond,
196 then_branch,
197 else_branch,
198 ..
199 } => {
200 emit(cond, context, lookup, types, resolve, locals)?;
201 let mut then_context = context.new_fragment();
202 emit(
203 then_branch,
204 &mut then_context,
205 lookup,
206 types,
207 resolve,
208 locals,
209 )?;
210 let else_context = if let Some(branch) = else_branch {
211 let mut fragment = context.new_fragment();
212 emit(branch, &mut fragment, lookup, types, resolve, locals)?;
213 Some(fragment)
214 } else {
215 None
216 };
217 context
218 .join2(then_context, else_context, span.start, span.end)
219 .map_err(|e| error(e.to_string()))?;
220 }
221 Expr::Logical { op, lhs, rhs, .. } => {
222 emit(lhs, context, lookup, types, resolve, locals)?;
223 let mut fragment = context.new_fragment();
224 emit(rhs, &mut fragment, lookup, types, resolve, locals)?;
225 context.apply_logical(
226 match op {
227 LogicalOp::And => "&&",
228 LogicalOp::Or => "||",
229 },
230 fragment,
231 span.start,
232 span.end,
233 )?;
234 }
235 Expr::Cast {
236 expr, type_name, ..
237 } => {
238 emit(expr, context, lookup, types, resolve, locals)?;
239 context.apply_cast(lookup, type_name, span.start, span.end)?;
240 }
241 Expr::Closure { params, body, .. } => {
242 if let Some(error) = crate::ast::closure_parameter_diagnostics(params)
243 .into_iter()
244 .next()
245 {
246 return Err(error);
247 }
248 let bindings = params
249 .iter()
250 .map(|param| {
251 crate::closure_param_type_from_type_expr(¶m.type_expr, types)
252 .map(|ty| (param.name.as_str(), ty))
253 })
254 .collect::<Result<Vec<_>>>()?;
255 let mut body_context = DynSegmentContext::new_context();
256 emit(
257 body,
258 &mut body_context,
259 lookup,
260 types,
261 resolve,
262 Some(&bindings),
263 )?;
264 context.push_closure(
265 bindings.into_iter().map(|(_, ty)| ty).collect(),
266 Vec::new(),
267 body_context,
268 span.start,
269 )?;
270 }
271 }
272 Ok(())
273}
274
275fn push_literal(context: &mut DynSegmentContext, value: &Literal, span: ExprSpan) -> Result<()> {
281 macro_rules! push {
282 ($($variant:ident),* $(,)?) => {
283 match value {
284 $(Literal::$variant(value) => context.push_literal(value.clone(), span.start),)*
285 Literal::Unit => context.make_tuple(
286 0, context.current_stack_offset(), span.start, span.end,
287 )?,
288 }
289 };
290 }
291 push!(
292 I8, I16, I32, I64, I128, Isize, U8, U16, U32, U64, U128, Usize, F32, F64, Bool, Char, Str,
293 ByteStr, CStr
294 );
295 Ok(())
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::{AstContext, Parser, builtin_type_resolver};
302 use std::{cell::Cell, rc::Rc};
303
304 #[test]
306 fn formals_shadow_external_names_without_retaining_syntax() {
307 let expr = Parser::<AstContext>::new(OpLookup::new())
308 .parse_str_ast("(|value: i32| value)(7)")
309 .unwrap();
310 let calls = Cell::new(0usize);
311 let mut segment = compile_expr(
312 &expr,
313 &OpLookup::new(),
314 builtin_type_resolver().as_ref(),
315 &mut |_, segment, _| {
316 calls.set(calls.get() + 1);
317 segment.just(99i32);
318 Ok(true)
319 },
320 )
321 .unwrap();
322 drop(expr);
323 assert_eq!(segment.call0::<i32>().unwrap(), 7);
324 assert_eq!(calls.get(), 0);
325 }
326
327 #[test]
329 fn failed_emission_releases_resources() {
330 let expr = Parser::<AstContext>::new(OpLookup::new())
331 .parse_str_ast("owned + missing")
332 .unwrap();
333 let owner = Rc::new(Cell::new(false));
334 let result = compile_expr(
335 &expr,
336 &OpLookup::new(),
337 builtin_type_resolver().as_ref(),
338 &mut |name, segment, _| {
339 if name == "owned" {
340 let owner = owner.clone();
341 segment.op0(move || {
342 owner.set(true);
343 1i32
344 });
345 Ok(true)
346 } else {
347 anyhow::bail!("external emission failed")
348 }
349 },
350 );
351 assert!(
352 result
353 .err()
354 .unwrap()
355 .message()
356 .contains("external emission failed")
357 );
358 assert_eq!(Rc::strong_count(&owner), 1);
359 assert!(!owner.get());
360 }
361}