1use anyhow::{Result, anyhow};
30use cel_runtime::DynSegment;
31use once_cell::sync::Lazy;
32use phf::phf_map;
33use std::any::TypeId;
34
35use crate::SourceSpan;
36
37#[cfg(feature = "span-diagnostics")]
41#[inline]
42fn span_err(span: SourceSpan, e: anyhow::Error) -> anyhow::Error {
43 e.context(crate::SpanContext::new(span))
44}
45
46#[cfg(not(feature = "span-diagnostics"))]
47#[inline]
48fn span_err(_span: SourceSpan, e: anyhow::Error) -> anyhow::Error {
49 e
50}
51
52pub type OpFn = fn(&mut DynSegment, SourceSpan) -> Result<()>;
58
59pub struct TupleOpSignature {
79 pub name: String,
81 pub shape: Vec<TypeId>,
84 pub tuple_operand_index: usize,
86 pub operand_type_ids: Vec<TypeId>,
89 pub op_fn: OpFn,
91}
92
93pub type ScopeFn =
103 Box<dyn Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync>;
104
105#[derive(Clone, Copy)]
110struct OpSignature {
111 type_id_index: usize,
113 rhs_type_id_index: usize,
115 arity: u8,
117 op_fn: OpFn,
119}
120
121impl OpSignature {
122 fn lhs_type_id(&self) -> TypeId {
124 TYPE_IDS[self.type_id_index]
125 }
126
127 fn rhs_type_id(&self) -> TypeId {
129 TYPE_IDS[self.rhs_type_id_index]
130 }
131}
132
133static TYPE_IDS: Lazy<Vec<TypeId>> = Lazy::new(|| {
137 vec![
138 TypeId::of::<u8>(),
139 TypeId::of::<u16>(),
140 TypeId::of::<u32>(),
141 TypeId::of::<u64>(),
142 TypeId::of::<u128>(),
143 TypeId::of::<usize>(),
144 TypeId::of::<i8>(),
145 TypeId::of::<i16>(),
146 TypeId::of::<i32>(),
147 TypeId::of::<i64>(),
148 TypeId::of::<i128>(),
149 TypeId::of::<isize>(),
150 TypeId::of::<f32>(),
151 TypeId::of::<f64>(),
152 TypeId::of::<bool>(),
153 TypeId::of::<String>(),
154 ]
155});
156
157const TYPE_U8: usize = 0;
159const TYPE_U16: usize = 1;
160const TYPE_U32: usize = 2;
161const TYPE_U64: usize = 3;
162const TYPE_U128: usize = 4;
163const TYPE_USIZE: usize = 5;
164const TYPE_I8: usize = 6;
165const TYPE_I16: usize = 7;
166const TYPE_I32: usize = 8;
167const TYPE_I64: usize = 9;
168const TYPE_I128: usize = 10;
169const TYPE_ISIZE: usize = 11;
170const TYPE_F32: usize = 12;
171const TYPE_F64: usize = 13;
172const TYPE_BOOL: usize = 14;
173const TYPE_STR: usize = 15;
174
175macro_rules! sig {
178 ($type_idx:expr, $arity:expr, $closure:expr) => {
179 OpSignature {
180 type_id_index: $type_idx,
181 rhs_type_id_index: $type_idx,
182 arity: $arity,
183 op_fn: $closure,
184 }
185 };
186}
187
188macro_rules! sig_het {
189 ($lhs_idx:expr, $rhs_idx:expr, $closure:expr) => {
190 OpSignature {
191 type_id_index: $lhs_idx,
192 rhs_type_id_index: $rhs_idx,
193 arity: 2,
194 op_fn: $closure,
195 }
196 };
197}
198
199static ADD_SIGNATURES: &[OpSignature] = &[
201 sig!(TYPE_U8, 2, |seg, _span| seg
202 .op2(|a: u8, b: u8| a.wrapping_add(b))),
203 sig!(TYPE_U16, 2, |seg, _span| seg
204 .op2(|a: u16, b: u16| a.wrapping_add(b))),
205 sig!(TYPE_U32, 2, |seg, _span| seg
206 .op2(|a: u32, b: u32| a.wrapping_add(b))),
207 sig!(TYPE_U64, 2, |seg, _span| seg
208 .op2(|a: u64, b: u64| a.wrapping_add(b))),
209 sig!(TYPE_U128, 2, |seg, _span| seg
210 .op2(|a: u128, b: u128| a.wrapping_add(b))),
211 sig!(TYPE_USIZE, 2, |seg, _span| seg
212 .op2(|a: usize, b: usize| a.wrapping_add(b))),
213 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
214 .checked_add(b)
215 .ok_or_else(|| anyhow!("arithmetic overflow"))
216 .map_err(|e| span_err(span, e)))),
217 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
218 .checked_add(b)
219 .ok_or_else(|| anyhow!("arithmetic overflow"))
220 .map_err(|e| span_err(span, e)))),
221 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
222 .checked_add(b)
223 .ok_or_else(|| anyhow!("arithmetic overflow"))
224 .map_err(|e| span_err(span, e)))),
225 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
226 .checked_add(b)
227 .ok_or_else(|| anyhow!("arithmetic overflow"))
228 .map_err(|e| span_err(span, e)))),
229 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
230 move |a: i128, b: i128| a
231 .checked_add(b)
232 .ok_or_else(|| anyhow!("arithmetic overflow"))
233 .map_err(|e| span_err(span, e))
234 )),
235 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
236 move |a: isize, b: isize| a
237 .checked_add(b)
238 .ok_or_else(|| anyhow!("arithmetic overflow"))
239 .map_err(|e| span_err(span, e))
240 )),
241 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a + b)),
242 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a + b)),
243 sig!(TYPE_STR, 2, |seg, _span| seg
244 .op2(|a: String, b: String| a + &b)),
245];
246
247static SUB_SIGNATURES: &[OpSignature] = &[
249 sig!(TYPE_U8, 2, |seg, _span| seg
251 .op2(|a: u8, b: u8| a.wrapping_sub(b))),
252 sig!(TYPE_U16, 2, |seg, _span| seg
253 .op2(|a: u16, b: u16| a.wrapping_sub(b))),
254 sig!(TYPE_U32, 2, |seg, _span| seg
255 .op2(|a: u32, b: u32| a.wrapping_sub(b))),
256 sig!(TYPE_U64, 2, |seg, _span| seg
257 .op2(|a: u64, b: u64| a.wrapping_sub(b))),
258 sig!(TYPE_U128, 2, |seg, _span| seg
259 .op2(|a: u128, b: u128| a.wrapping_sub(b))),
260 sig!(TYPE_USIZE, 2, |seg, _span| seg
261 .op2(|a: usize, b: usize| a.wrapping_sub(b))),
262 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
263 .checked_sub(b)
264 .ok_or_else(|| anyhow!("arithmetic overflow"))
265 .map_err(|e| span_err(span, e)))),
266 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
267 .checked_sub(b)
268 .ok_or_else(|| anyhow!("arithmetic overflow"))
269 .map_err(|e| span_err(span, e)))),
270 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
271 .checked_sub(b)
272 .ok_or_else(|| anyhow!("arithmetic overflow"))
273 .map_err(|e| span_err(span, e)))),
274 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
275 .checked_sub(b)
276 .ok_or_else(|| anyhow!("arithmetic overflow"))
277 .map_err(|e| span_err(span, e)))),
278 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
279 move |a: i128, b: i128| a
280 .checked_sub(b)
281 .ok_or_else(|| anyhow!("arithmetic overflow"))
282 .map_err(|e| span_err(span, e))
283 )),
284 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
285 move |a: isize, b: isize| a
286 .checked_sub(b)
287 .ok_or_else(|| anyhow!("arithmetic overflow"))
288 .map_err(|e| span_err(span, e))
289 )),
290 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a - b)),
291 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a - b)),
292 sig!(TYPE_I8, 1, |seg, span| seg.op1r(move |a: i8| a
294 .checked_neg()
295 .ok_or_else(|| anyhow!("arithmetic overflow"))
296 .map_err(|e| span_err(span, e)))),
297 sig!(TYPE_I16, 1, |seg, span| seg.op1r(move |a: i16| a
298 .checked_neg()
299 .ok_or_else(|| anyhow!("arithmetic overflow"))
300 .map_err(|e| span_err(span, e)))),
301 sig!(TYPE_I32, 1, |seg, span| seg.op1r(move |a: i32| a
302 .checked_neg()
303 .ok_or_else(|| anyhow!("arithmetic overflow"))
304 .map_err(|e| span_err(span, e)))),
305 sig!(TYPE_I64, 1, |seg, span| seg.op1r(move |a: i64| a
306 .checked_neg()
307 .ok_or_else(|| anyhow!("arithmetic overflow"))
308 .map_err(|e| span_err(span, e)))),
309 sig!(TYPE_I128, 1, |seg, span| seg.op1r(move |a: i128| a
310 .checked_neg()
311 .ok_or_else(|| anyhow!("arithmetic overflow"))
312 .map_err(|e| span_err(span, e)))),
313 sig!(TYPE_ISIZE, 1, |seg, span| seg.op1r(move |a: isize| a
314 .checked_neg()
315 .ok_or_else(|| anyhow!("arithmetic overflow"))
316 .map_err(|e| span_err(span, e)))),
317 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| -a)),
318 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| -a)),
319];
320
321static MUL_SIGNATURES: &[OpSignature] = &[
323 sig!(TYPE_U8, 2, |seg, _span| seg
324 .op2(|a: u8, b: u8| a.wrapping_mul(b))),
325 sig!(TYPE_U16, 2, |seg, _span| seg
326 .op2(|a: u16, b: u16| a.wrapping_mul(b))),
327 sig!(TYPE_U32, 2, |seg, _span| seg
328 .op2(|a: u32, b: u32| a.wrapping_mul(b))),
329 sig!(TYPE_U64, 2, |seg, _span| seg
330 .op2(|a: u64, b: u64| a.wrapping_mul(b))),
331 sig!(TYPE_U128, 2, |seg, _span| seg
332 .op2(|a: u128, b: u128| a.wrapping_mul(b))),
333 sig!(TYPE_USIZE, 2, |seg, _span| seg
334 .op2(|a: usize, b: usize| a.wrapping_mul(b))),
335 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
336 .checked_mul(b)
337 .ok_or_else(|| anyhow!("arithmetic overflow"))
338 .map_err(|e| span_err(span, e)))),
339 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
340 .checked_mul(b)
341 .ok_or_else(|| anyhow!("arithmetic overflow"))
342 .map_err(|e| span_err(span, e)))),
343 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
344 .checked_mul(b)
345 .ok_or_else(|| anyhow!("arithmetic overflow"))
346 .map_err(|e| span_err(span, e)))),
347 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
348 .checked_mul(b)
349 .ok_or_else(|| anyhow!("arithmetic overflow"))
350 .map_err(|e| span_err(span, e)))),
351 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
352 move |a: i128, b: i128| a
353 .checked_mul(b)
354 .ok_or_else(|| anyhow!("arithmetic overflow"))
355 .map_err(|e| span_err(span, e))
356 )),
357 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
358 move |a: isize, b: isize| a
359 .checked_mul(b)
360 .ok_or_else(|| anyhow!("arithmetic overflow"))
361 .map_err(|e| span_err(span, e))
362 )),
363 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a * b)),
364 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a * b)),
365];
366
367static DIV_SIGNATURES: &[OpSignature] = &[
372 sig!(TYPE_U8, 2, |seg, span| seg.op2r(move |a: u8, b: u8| a
373 .checked_div(b)
374 .ok_or_else(|| anyhow!("division by zero"))
375 .map_err(|e| span_err(span, e)))),
376 sig!(TYPE_U16, 2, |seg, span| seg.op2r(move |a: u16, b: u16| a
377 .checked_div(b)
378 .ok_or_else(|| anyhow!("division by zero"))
379 .map_err(|e| span_err(span, e)))),
380 sig!(TYPE_U32, 2, |seg, span| seg.op2r(move |a: u32, b: u32| a
381 .checked_div(b)
382 .ok_or_else(|| anyhow!("division by zero"))
383 .map_err(|e| span_err(span, e)))),
384 sig!(TYPE_U64, 2, |seg, span| seg.op2r(move |a: u64, b: u64| a
385 .checked_div(b)
386 .ok_or_else(|| anyhow!("division by zero"))
387 .map_err(|e| span_err(span, e)))),
388 sig!(TYPE_U128, 2, |seg, span| seg.op2r(
389 move |a: u128, b: u128| a
390 .checked_div(b)
391 .ok_or_else(|| anyhow!("division by zero"))
392 .map_err(|e| span_err(span, e))
393 )),
394 sig!(TYPE_USIZE, 2, |seg, span| seg.op2r(
395 move |a: usize, b: usize| a
396 .checked_div(b)
397 .ok_or_else(|| anyhow!("division by zero"))
398 .map_err(|e| span_err(span, e))
399 )),
400 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
401 .checked_div(b)
402 .ok_or_else(|| anyhow!("division by zero"))
403 .map_err(|e| span_err(span, e)))),
404 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
405 .checked_div(b)
406 .ok_or_else(|| anyhow!("division by zero"))
407 .map_err(|e| span_err(span, e)))),
408 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
409 .checked_div(b)
410 .ok_or_else(|| anyhow!("division by zero"))
411 .map_err(|e| span_err(span, e)))),
412 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
413 .checked_div(b)
414 .ok_or_else(|| anyhow!("division by zero"))
415 .map_err(|e| span_err(span, e)))),
416 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
417 move |a: i128, b: i128| a
418 .checked_div(b)
419 .ok_or_else(|| anyhow!("division by zero"))
420 .map_err(|e| span_err(span, e))
421 )),
422 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
423 move |a: isize, b: isize| a
424 .checked_div(b)
425 .ok_or_else(|| anyhow!("division by zero"))
426 .map_err(|e| span_err(span, e))
427 )),
428 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a / b)),
429 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a / b)),
430];
431
432static MOD_SIGNATURES: &[OpSignature] = &[
437 sig!(TYPE_U8, 2, |seg, span| seg.op2r(move |a: u8, b: u8| a
438 .checked_rem(b)
439 .ok_or_else(|| anyhow!("division by zero"))
440 .map_err(|e| span_err(span, e)))),
441 sig!(TYPE_U16, 2, |seg, span| seg.op2r(move |a: u16, b: u16| a
442 .checked_rem(b)
443 .ok_or_else(|| anyhow!("division by zero"))
444 .map_err(|e| span_err(span, e)))),
445 sig!(TYPE_U32, 2, |seg, span| seg.op2r(move |a: u32, b: u32| a
446 .checked_rem(b)
447 .ok_or_else(|| anyhow!("division by zero"))
448 .map_err(|e| span_err(span, e)))),
449 sig!(TYPE_U64, 2, |seg, span| seg.op2r(move |a: u64, b: u64| a
450 .checked_rem(b)
451 .ok_or_else(|| anyhow!("division by zero"))
452 .map_err(|e| span_err(span, e)))),
453 sig!(TYPE_U128, 2, |seg, span| seg.op2r(
454 move |a: u128, b: u128| a
455 .checked_rem(b)
456 .ok_or_else(|| anyhow!("division by zero"))
457 .map_err(|e| span_err(span, e))
458 )),
459 sig!(TYPE_USIZE, 2, |seg, span| seg.op2r(
460 move |a: usize, b: usize| a
461 .checked_rem(b)
462 .ok_or_else(|| anyhow!("division by zero"))
463 .map_err(|e| span_err(span, e))
464 )),
465 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
466 .checked_rem(b)
467 .ok_or_else(|| anyhow!("division by zero"))
468 .map_err(|e| span_err(span, e)))),
469 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
470 .checked_rem(b)
471 .ok_or_else(|| anyhow!("division by zero"))
472 .map_err(|e| span_err(span, e)))),
473 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
474 .checked_rem(b)
475 .ok_or_else(|| anyhow!("division by zero"))
476 .map_err(|e| span_err(span, e)))),
477 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
478 .checked_rem(b)
479 .ok_or_else(|| anyhow!("division by zero"))
480 .map_err(|e| span_err(span, e)))),
481 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
482 move |a: i128, b: i128| a
483 .checked_rem(b)
484 .ok_or_else(|| anyhow!("division by zero"))
485 .map_err(|e| span_err(span, e))
486 )),
487 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
488 move |a: isize, b: isize| a
489 .checked_rem(b)
490 .ok_or_else(|| anyhow!("division by zero"))
491 .map_err(|e| span_err(span, e))
492 )),
493 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a % b)),
494 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a % b)),
495];
496
497static BITWISE_AND_SIGNATURES: &[OpSignature] = &[
499 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a & b)),
500 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a & b)),
501 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a & b)),
502 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a & b)),
503 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a & b)),
504 sig!(TYPE_USIZE, 2, |seg, _span| seg
505 .op2(|a: usize, b: usize| a & b)),
506 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a & b)),
507 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a & b)),
508 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a & b)),
509 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a & b)),
510 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a & b)),
511 sig!(TYPE_ISIZE, 2, |seg, _span| seg
512 .op2(|a: isize, b: isize| a & b)),
513];
514
515static BITWISE_OR_SIGNATURES: &[OpSignature] = &[
517 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a | b)),
518 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a | b)),
519 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a | b)),
520 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a | b)),
521 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a | b)),
522 sig!(TYPE_USIZE, 2, |seg, _span| seg
523 .op2(|a: usize, b: usize| a | b)),
524 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a | b)),
525 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a | b)),
526 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a | b)),
527 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a | b)),
528 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a | b)),
529 sig!(TYPE_ISIZE, 2, |seg, _span| seg
530 .op2(|a: isize, b: isize| a | b)),
531];
532
533static BITWISE_XOR_SIGNATURES: &[OpSignature] = &[
535 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a ^ b)),
536 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a ^ b)),
537 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a ^ b)),
538 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a ^ b)),
539 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a ^ b)),
540 sig!(TYPE_USIZE, 2, |seg, _span| seg
541 .op2(|a: usize, b: usize| a ^ b)),
542 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a ^ b)),
543 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a ^ b)),
544 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a ^ b)),
545 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a ^ b)),
546 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a ^ b)),
547 sig!(TYPE_ISIZE, 2, |seg, _span| seg
548 .op2(|a: isize, b: isize| a ^ b)),
549];
550
551macro_rules! shl_push {
563 ($v:ident, $lhs_idx:expr, $lhs_ty:ty) => {
564 $v.push(sig_het!($lhs_idx, TYPE_U8, |seg, span| seg.op2r(
565 move |a: $lhs_ty, b: u8| a
566 .checked_shl(u32::from(b))
567 .ok_or_else(|| anyhow!("shift overflow"))
568 .map_err(|e| span_err(span, e))
569 )));
570 $v.push(sig_het!($lhs_idx, TYPE_U16, |seg, span| seg.op2r(
571 move |a: $lhs_ty, b: u16| a
572 .checked_shl(u32::from(b))
573 .ok_or_else(|| anyhow!("shift overflow"))
574 .map_err(|e| span_err(span, e))
575 )));
576 $v.push(sig_het!($lhs_idx, TYPE_U32, |seg, span| seg.op2r(
577 move |a: $lhs_ty, b: u32| a
578 .checked_shl(b)
579 .ok_or_else(|| anyhow!("shift overflow"))
580 .map_err(|e| span_err(span, e))
581 )));
582 $v.push(sig_het!($lhs_idx, TYPE_U64, |seg, span| seg.op2r(
583 move |a: $lhs_ty, b: u64| u32::try_from(b)
584 .ok()
585 .and_then(|r| a.checked_shl(r))
586 .ok_or_else(|| anyhow!("shift overflow"))
587 .map_err(|e| span_err(span, e))
588 )));
589 $v.push(sig_het!($lhs_idx, TYPE_U128, |seg, span| seg.op2r(
590 move |a: $lhs_ty, b: u128| u32::try_from(b)
591 .ok()
592 .and_then(|r| a.checked_shl(r))
593 .ok_or_else(|| anyhow!("shift overflow"))
594 .map_err(|e| span_err(span, e))
595 )));
596 $v.push(sig_het!($lhs_idx, TYPE_USIZE, |seg, span| seg.op2r(
597 move |a: $lhs_ty, b: usize| u32::try_from(b)
598 .ok()
599 .and_then(|r| a.checked_shl(r))
600 .ok_or_else(|| anyhow!("shift overflow"))
601 .map_err(|e| span_err(span, e))
602 )));
603 $v.push(sig_het!($lhs_idx, TYPE_I8, |seg, span| seg.op2r(
604 move |a: $lhs_ty, b: i8| u32::try_from(b)
605 .ok()
606 .and_then(|r| a.checked_shl(r))
607 .ok_or_else(|| anyhow!("shift overflow"))
608 .map_err(|e| span_err(span, e))
609 )));
610 $v.push(sig_het!($lhs_idx, TYPE_I16, |seg, span| seg.op2r(
611 move |a: $lhs_ty, b: i16| u32::try_from(b)
612 .ok()
613 .and_then(|r| a.checked_shl(r))
614 .ok_or_else(|| anyhow!("shift overflow"))
615 .map_err(|e| span_err(span, e))
616 )));
617 $v.push(sig_het!($lhs_idx, TYPE_I32, |seg, span| seg.op2r(
618 move |a: $lhs_ty, b: i32| u32::try_from(b)
619 .ok()
620 .and_then(|r| a.checked_shl(r))
621 .ok_or_else(|| anyhow!("shift overflow"))
622 .map_err(|e| span_err(span, e))
623 )));
624 $v.push(sig_het!($lhs_idx, TYPE_I64, |seg, span| seg.op2r(
625 move |a: $lhs_ty, b: i64| u32::try_from(b)
626 .ok()
627 .and_then(|r| a.checked_shl(r))
628 .ok_or_else(|| anyhow!("shift overflow"))
629 .map_err(|e| span_err(span, e))
630 )));
631 $v.push(sig_het!($lhs_idx, TYPE_I128, |seg, span| seg.op2r(
632 move |a: $lhs_ty, b: i128| u32::try_from(b)
633 .ok()
634 .and_then(|r| a.checked_shl(r))
635 .ok_or_else(|| anyhow!("shift overflow"))
636 .map_err(|e| span_err(span, e))
637 )));
638 $v.push(sig_het!($lhs_idx, TYPE_ISIZE, |seg, span| seg.op2r(
639 move |a: $lhs_ty, b: isize| u32::try_from(b)
640 .ok()
641 .and_then(|r| a.checked_shl(r))
642 .ok_or_else(|| anyhow!("shift overflow"))
643 .map_err(|e| span_err(span, e))
644 )));
645 };
646}
647
648macro_rules! shr_push {
649 ($v:ident, $lhs_idx:expr, $lhs_ty:ty) => {
650 $v.push(sig_het!($lhs_idx, TYPE_U8, |seg, span| seg.op2r(
651 move |a: $lhs_ty, b: u8| a
652 .checked_shr(u32::from(b))
653 .ok_or_else(|| anyhow!("shift overflow"))
654 .map_err(|e| span_err(span, e))
655 )));
656 $v.push(sig_het!($lhs_idx, TYPE_U16, |seg, span| seg.op2r(
657 move |a: $lhs_ty, b: u16| a
658 .checked_shr(u32::from(b))
659 .ok_or_else(|| anyhow!("shift overflow"))
660 .map_err(|e| span_err(span, e))
661 )));
662 $v.push(sig_het!($lhs_idx, TYPE_U32, |seg, span| seg.op2r(
663 move |a: $lhs_ty, b: u32| a
664 .checked_shr(b)
665 .ok_or_else(|| anyhow!("shift overflow"))
666 .map_err(|e| span_err(span, e))
667 )));
668 $v.push(sig_het!($lhs_idx, TYPE_U64, |seg, span| seg.op2r(
669 move |a: $lhs_ty, b: u64| u32::try_from(b)
670 .ok()
671 .and_then(|r| a.checked_shr(r))
672 .ok_or_else(|| anyhow!("shift overflow"))
673 .map_err(|e| span_err(span, e))
674 )));
675 $v.push(sig_het!($lhs_idx, TYPE_U128, |seg, span| seg.op2r(
676 move |a: $lhs_ty, b: u128| u32::try_from(b)
677 .ok()
678 .and_then(|r| a.checked_shr(r))
679 .ok_or_else(|| anyhow!("shift overflow"))
680 .map_err(|e| span_err(span, e))
681 )));
682 $v.push(sig_het!($lhs_idx, TYPE_USIZE, |seg, span| seg.op2r(
683 move |a: $lhs_ty, b: usize| u32::try_from(b)
684 .ok()
685 .and_then(|r| a.checked_shr(r))
686 .ok_or_else(|| anyhow!("shift overflow"))
687 .map_err(|e| span_err(span, e))
688 )));
689 $v.push(sig_het!($lhs_idx, TYPE_I8, |seg, span| seg.op2r(
690 move |a: $lhs_ty, b: i8| u32::try_from(b)
691 .ok()
692 .and_then(|r| a.checked_shr(r))
693 .ok_or_else(|| anyhow!("shift overflow"))
694 .map_err(|e| span_err(span, e))
695 )));
696 $v.push(sig_het!($lhs_idx, TYPE_I16, |seg, span| seg.op2r(
697 move |a: $lhs_ty, b: i16| u32::try_from(b)
698 .ok()
699 .and_then(|r| a.checked_shr(r))
700 .ok_or_else(|| anyhow!("shift overflow"))
701 .map_err(|e| span_err(span, e))
702 )));
703 $v.push(sig_het!($lhs_idx, TYPE_I32, |seg, span| seg.op2r(
704 move |a: $lhs_ty, b: i32| u32::try_from(b)
705 .ok()
706 .and_then(|r| a.checked_shr(r))
707 .ok_or_else(|| anyhow!("shift overflow"))
708 .map_err(|e| span_err(span, e))
709 )));
710 $v.push(sig_het!($lhs_idx, TYPE_I64, |seg, span| seg.op2r(
711 move |a: $lhs_ty, b: i64| u32::try_from(b)
712 .ok()
713 .and_then(|r| a.checked_shr(r))
714 .ok_or_else(|| anyhow!("shift overflow"))
715 .map_err(|e| span_err(span, e))
716 )));
717 $v.push(sig_het!($lhs_idx, TYPE_I128, |seg, span| seg.op2r(
718 move |a: $lhs_ty, b: i128| u32::try_from(b)
719 .ok()
720 .and_then(|r| a.checked_shr(r))
721 .ok_or_else(|| anyhow!("shift overflow"))
722 .map_err(|e| span_err(span, e))
723 )));
724 $v.push(sig_het!($lhs_idx, TYPE_ISIZE, |seg, span| seg.op2r(
725 move |a: $lhs_ty, b: isize| u32::try_from(b)
726 .ok()
727 .and_then(|r| a.checked_shr(r))
728 .ok_or_else(|| anyhow!("shift overflow"))
729 .map_err(|e| span_err(span, e))
730 )));
731 };
732}
733
734static LEFT_SHIFT_SIGNATURES: Lazy<Vec<OpSignature>> = Lazy::new(|| {
737 let mut v = Vec::with_capacity(144);
738 shl_push!(v, TYPE_U8, u8);
739 shl_push!(v, TYPE_U16, u16);
740 shl_push!(v, TYPE_U32, u32);
741 shl_push!(v, TYPE_U64, u64);
742 shl_push!(v, TYPE_U128, u128);
743 shl_push!(v, TYPE_USIZE, usize);
744 shl_push!(v, TYPE_I8, i8);
745 shl_push!(v, TYPE_I16, i16);
746 shl_push!(v, TYPE_I32, i32);
747 shl_push!(v, TYPE_I64, i64);
748 shl_push!(v, TYPE_I128, i128);
749 shl_push!(v, TYPE_ISIZE, isize);
750 v
751});
752
753static RIGHT_SHIFT_SIGNATURES: Lazy<Vec<OpSignature>> = Lazy::new(|| {
755 let mut v = Vec::with_capacity(144);
756 shr_push!(v, TYPE_U8, u8);
757 shr_push!(v, TYPE_U16, u16);
758 shr_push!(v, TYPE_U32, u32);
759 shr_push!(v, TYPE_U64, u64);
760 shr_push!(v, TYPE_U128, u128);
761 shr_push!(v, TYPE_USIZE, usize);
762 shr_push!(v, TYPE_I8, i8);
763 shr_push!(v, TYPE_I16, i16);
764 shr_push!(v, TYPE_I32, i32);
765 shr_push!(v, TYPE_I64, i64);
766 shr_push!(v, TYPE_I128, i128);
767 shr_push!(v, TYPE_ISIZE, isize);
768 v
769});
770
771static LOGICAL_NOT_SIGNATURES: &[OpSignature] =
773 &[sig!(TYPE_BOOL, 1, |seg, _span| seg.op1(|a: bool| !a))];
774
775static EQUAL_SIGNATURES: &[OpSignature] = &[
777 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a == b)),
778 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a == b)),
779 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a == b)),
780 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a == b)),
781 sig!(TYPE_U128, 2, |seg, _span| seg
782 .op2(|a: u128, b: u128| a == b)),
783 sig!(TYPE_USIZE, 2, |seg, _span| seg
784 .op2(|a: usize, b: usize| a == b)),
785 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a == b)),
786 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a == b)),
787 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a == b)),
788 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a == b)),
789 sig!(TYPE_I128, 2, |seg, _span| seg
790 .op2(|a: i128, b: i128| a == b)),
791 sig!(TYPE_ISIZE, 2, |seg, _span| seg
792 .op2(|a: isize, b: isize| a == b)),
793 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a == b)),
794 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a == b)),
795 sig!(TYPE_BOOL, 2, |seg, _span| seg
796 .op2(|a: bool, b: bool| a == b)),
797 sig!(TYPE_STR, 2, |seg, _span| seg
798 .op2(|a: String, b: String| a == b)),
799];
800
801static NOT_EQUAL_SIGNATURES: &[OpSignature] = &[
803 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a != b)),
804 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a != b)),
805 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a != b)),
806 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a != b)),
807 sig!(TYPE_U128, 2, |seg, _span| seg
808 .op2(|a: u128, b: u128| a != b)),
809 sig!(TYPE_USIZE, 2, |seg, _span| seg
810 .op2(|a: usize, b: usize| a != b)),
811 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a != b)),
812 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a != b)),
813 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a != b)),
814 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a != b)),
815 sig!(TYPE_I128, 2, |seg, _span| seg
816 .op2(|a: i128, b: i128| a != b)),
817 sig!(TYPE_ISIZE, 2, |seg, _span| seg
818 .op2(|a: isize, b: isize| a != b)),
819 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a != b)),
820 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a != b)),
821 sig!(TYPE_BOOL, 2, |seg, _span| seg
822 .op2(|a: bool, b: bool| a != b)),
823 sig!(TYPE_STR, 2, |seg, _span| seg
824 .op2(|a: String, b: String| a != b)),
825];
826
827static LESS_THAN_SIGNATURES: &[OpSignature] = &[
829 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a < b)),
830 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a < b)),
831 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a < b)),
832 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a < b)),
833 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a < b)),
834 sig!(TYPE_USIZE, 2, |seg, _span| seg
835 .op2(|a: usize, b: usize| a < b)),
836 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a < b)),
837 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a < b)),
838 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a < b)),
839 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a < b)),
840 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a < b)),
841 sig!(TYPE_ISIZE, 2, |seg, _span| seg
842 .op2(|a: isize, b: isize| a < b)),
843 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a < b)),
844 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a < b)),
845 sig!(TYPE_STR, 2, |seg, _span| seg
846 .op2(|a: String, b: String| a < b)),
847];
848
849static LESS_THAN_OR_EQUAL_SIGNATURES: &[OpSignature] = &[
851 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a <= b)),
852 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a <= b)),
853 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a <= b)),
854 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a <= b)),
855 sig!(TYPE_U128, 2, |seg, _span| seg
856 .op2(|a: u128, b: u128| a <= b)),
857 sig!(TYPE_USIZE, 2, |seg, _span| seg
858 .op2(|a: usize, b: usize| a <= b)),
859 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a <= b)),
860 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a <= b)),
861 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a <= b)),
862 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a <= b)),
863 sig!(TYPE_I128, 2, |seg, _span| seg
864 .op2(|a: i128, b: i128| a <= b)),
865 sig!(TYPE_ISIZE, 2, |seg, _span| seg
866 .op2(|a: isize, b: isize| a <= b)),
867 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a <= b)),
868 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a <= b)),
869 sig!(TYPE_STR, 2, |seg, _span| seg
870 .op2(|a: String, b: String| a <= b)),
871];
872
873static GREATER_THAN_SIGNATURES: &[OpSignature] = &[
875 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a > b)),
876 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a > b)),
877 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a > b)),
878 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a > b)),
879 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a > b)),
880 sig!(TYPE_USIZE, 2, |seg, _span| seg
881 .op2(|a: usize, b: usize| a > b)),
882 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a > b)),
883 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a > b)),
884 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a > b)),
885 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a > b)),
886 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a > b)),
887 sig!(TYPE_ISIZE, 2, |seg, _span| seg
888 .op2(|a: isize, b: isize| a > b)),
889 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a > b)),
890 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a > b)),
891 sig!(TYPE_STR, 2, |seg, _span| seg
892 .op2(|a: String, b: String| a > b)),
893];
894
895static GREATER_THAN_OR_EQUAL_SIGNATURES: &[OpSignature] = &[
897 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a >= b)),
898 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a >= b)),
899 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a >= b)),
900 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a >= b)),
901 sig!(TYPE_U128, 2, |seg, _span| seg
902 .op2(|a: u128, b: u128| a >= b)),
903 sig!(TYPE_USIZE, 2, |seg, _span| seg
904 .op2(|a: usize, b: usize| a >= b)),
905 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a >= b)),
906 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a >= b)),
907 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a >= b)),
908 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a >= b)),
909 sig!(TYPE_I128, 2, |seg, _span| seg
910 .op2(|a: i128, b: i128| a >= b)),
911 sig!(TYPE_ISIZE, 2, |seg, _span| seg
912 .op2(|a: isize, b: isize| a >= b)),
913 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a >= b)),
914 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a >= b)),
915 sig!(TYPE_STR, 2, |seg, _span| seg
916 .op2(|a: String, b: String| a >= b)),
917];
918
919static RANGE_SIGNATURES: &[OpSignature] = &[
923 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a..b)),
924 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a..b)),
925 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a..b)),
926 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a..b)),
927 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a..b)),
928 sig!(TYPE_USIZE, 2, |seg, _span| seg
929 .op2(|a: usize, b: usize| a..b)),
930 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a..b)),
931 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a..b)),
932 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a..b)),
933 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a..b)),
934 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a..b)),
935 sig!(TYPE_ISIZE, 2, |seg, _span| seg
936 .op2(|a: isize, b: isize| a..b)),
937 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a..b)),
938 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a..b)),
939];
940
941static RANGE_INCLUSIVE_SIGNATURES: &[OpSignature] = &[
942 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a..=b)),
943 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a..=b)),
944 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a..=b)),
945 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a..=b)),
946 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a..=b)),
947 sig!(TYPE_USIZE, 2, |seg, _span| seg
948 .op2(|a: usize, b: usize| a..=b)),
949 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a..=b)),
950 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a..=b)),
951 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a..=b)),
952 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a..=b)),
953 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a..=b)),
954 sig!(TYPE_ISIZE, 2, |seg, _span| seg
955 .op2(|a: isize, b: isize| a..=b)),
956 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a..=b)),
957 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a..=b)),
958];
959
960static RANGE_FROM_SIGNATURES: &[OpSignature] = &[
961 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| a..)),
962 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| a..)),
963 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| a..)),
964 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| a..)),
965 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| a..)),
966 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| a..)),
967 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| a..)),
968 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| a..)),
969 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| a..)),
970 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| a..)),
971 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| a..)),
972 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| a..)),
973 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| a..)),
974 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| a..)),
975];
976
977static RANGE_TO_SIGNATURES: &[OpSignature] = &[
978 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| ..a)),
979 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| ..a)),
980 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| ..a)),
981 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| ..a)),
982 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| ..a)),
983 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| ..a)),
984 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| ..a)),
985 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| ..a)),
986 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| ..a)),
987 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| ..a)),
988 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| ..a)),
989 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| ..a)),
990 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| ..a)),
991 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| ..a)),
992];
993
994static RANGE_TO_INCLUSIVE_SIGNATURES: &[OpSignature] = &[
995 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| ..=a)),
996 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| ..=a)),
997 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| ..=a)),
998 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| ..=a)),
999 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| ..=a)),
1000 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| ..=a)),
1001 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| ..=a)),
1002 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| ..=a)),
1003 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| ..=a)),
1004 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| ..=a)),
1005 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| ..=a)),
1006 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| ..=a)),
1007 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| ..=a)),
1008 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| ..=a)),
1009];
1010
1011static BUILTINS: phf::Map<&'static str, &'static [OpSignature]> = phf_map! {
1015 "+" => ADD_SIGNATURES,
1016 "-" => SUB_SIGNATURES,
1017 "*" => MUL_SIGNATURES,
1018 "/" => DIV_SIGNATURES,
1019 "%" => MOD_SIGNATURES,
1020 "&" => BITWISE_AND_SIGNATURES,
1021 "|" => BITWISE_OR_SIGNATURES,
1022 "^" => BITWISE_XOR_SIGNATURES,
1023 "!" => LOGICAL_NOT_SIGNATURES,
1024 "==" => EQUAL_SIGNATURES,
1025 "!=" => NOT_EQUAL_SIGNATURES,
1026 "<" => LESS_THAN_SIGNATURES,
1027 "<=" => LESS_THAN_OR_EQUAL_SIGNATURES,
1028 ">" => GREATER_THAN_SIGNATURES,
1029 ">=" => GREATER_THAN_OR_EQUAL_SIGNATURES,
1030 "range" => RANGE_SIGNATURES,
1031 "range_inclusive" => RANGE_INCLUSIVE_SIGNATURES,
1032 "range_from" => RANGE_FROM_SIGNATURES,
1033 "range_to" => RANGE_TO_SIGNATURES,
1034 "range_to_inclusive" => RANGE_TO_INCLUSIVE_SIGNATURES,
1035};
1036
1037#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1040pub struct OperandTypes {
1041 pub arity: u8,
1043 pub lhs: TypeId,
1045 pub rhs: TypeId,
1047}
1048
1049pub fn builtin_operand_types(name: &str) -> Vec<OperandTypes> {
1072 let Some(signatures) = signatures_for(name) else {
1073 return Vec::new();
1074 };
1075 signatures
1076 .iter()
1077 .map(|sig| OperandTypes {
1078 arity: sig.arity,
1079 lhs: sig.lhs_type_id(),
1080 rhs: sig.rhs_type_id(),
1081 })
1082 .collect()
1083}
1084
1085fn signatures_for(name: &str) -> Option<&'static [OpSignature]> {
1089 match name {
1090 "<<" => Some(&LEFT_SHIFT_SIGNATURES),
1091 ">>" => Some(&RIGHT_SHIFT_SIGNATURES),
1092 _ => BUILTINS.get(name).copied(),
1093 }
1094}
1095
1096#[derive(Clone, Copy)]
1137struct CastSignature {
1138 source_type_id_index: usize,
1140 op_fn: OpFn,
1142}
1143
1144impl CastSignature {
1145 fn source_type_id(&self) -> TypeId {
1147 TYPE_IDS[self.source_type_id_index]
1148 }
1149}
1150
1151macro_rules! try_from_cast_push {
1153 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1154 $v.push(CastSignature {
1155 source_type_id_index: $src_idx,
1156 op_fn: |seg, span| {
1157 seg.op1r(move |x: $src_ty| -> Result<$tgt_ty> {
1158 <$tgt_ty>::try_from(x).map_err(|_| {
1159 span_err(
1160 span,
1161 anyhow!(concat!("value does not fit in `", stringify!($tgt_ty), "`")),
1162 )
1163 })
1164 })
1165 },
1166 });
1167 };
1168}
1169
1170macro_rules! all_int_to_int_casts {
1172 ($v:ident, $tgt_ty:ty) => {
1173 try_from_cast_push!($v, TYPE_U8, u8, $tgt_ty);
1174 try_from_cast_push!($v, TYPE_U16, u16, $tgt_ty);
1175 try_from_cast_push!($v, TYPE_U32, u32, $tgt_ty);
1176 try_from_cast_push!($v, TYPE_U64, u64, $tgt_ty);
1177 try_from_cast_push!($v, TYPE_U128, u128, $tgt_ty);
1178 try_from_cast_push!($v, TYPE_USIZE, usize, $tgt_ty);
1179 try_from_cast_push!($v, TYPE_I8, i8, $tgt_ty);
1180 try_from_cast_push!($v, TYPE_I16, i16, $tgt_ty);
1181 try_from_cast_push!($v, TYPE_I32, i32, $tgt_ty);
1182 try_from_cast_push!($v, TYPE_I64, i64, $tgt_ty);
1183 try_from_cast_push!($v, TYPE_I128, i128, $tgt_ty);
1184 try_from_cast_push!($v, TYPE_ISIZE, isize, $tgt_ty);
1185 };
1186}
1187
1188macro_rules! as_cast_push {
1190 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1191 $v.push(CastSignature {
1192 source_type_id_index: $src_idx,
1193 op_fn: |seg, _span| seg.op1(|x: $src_ty| x as $tgt_ty),
1194 });
1195 };
1196}
1197
1198macro_rules! all_int_to_float_casts {
1200 ($v:ident, $tgt_ty:ty) => {
1201 as_cast_push!($v, TYPE_U8, u8, $tgt_ty);
1202 as_cast_push!($v, TYPE_U16, u16, $tgt_ty);
1203 as_cast_push!($v, TYPE_U32, u32, $tgt_ty);
1204 as_cast_push!($v, TYPE_U64, u64, $tgt_ty);
1205 as_cast_push!($v, TYPE_U128, u128, $tgt_ty);
1206 as_cast_push!($v, TYPE_USIZE, usize, $tgt_ty);
1207 as_cast_push!($v, TYPE_I8, i8, $tgt_ty);
1208 as_cast_push!($v, TYPE_I16, i16, $tgt_ty);
1209 as_cast_push!($v, TYPE_I32, i32, $tgt_ty);
1210 as_cast_push!($v, TYPE_I64, i64, $tgt_ty);
1211 as_cast_push!($v, TYPE_I128, i128, $tgt_ty);
1212 as_cast_push!($v, TYPE_ISIZE, isize, $tgt_ty);
1213 };
1214}
1215
1216macro_rules! float_to_int_cast_push {
1233 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1234 $v.push(CastSignature {
1235 source_type_id_index: $src_idx,
1236 op_fn: |seg, span| {
1237 seg.op1r(move |x: $src_ty| -> Result<$tgt_ty> {
1238 if !x.is_finite() {
1239 return Err(span_err(span, anyhow!("value is not finite")));
1240 }
1241 let is_unsigned = <$tgt_ty>::MIN == 0;
1242 let bits = <$tgt_ty>::BITS as i32;
1243 let exponent = if is_unsigned { bits } else { bits - 1 };
1244 let upper_exclusive: $src_ty = (2.0 as $src_ty).powi(exponent);
1245 if x < <$tgt_ty>::MIN as $src_ty || x >= upper_exclusive {
1246 return Err(span_err(
1247 span,
1248 anyhow!(concat!("value does not fit in `", stringify!($tgt_ty), "`")),
1249 ));
1250 }
1251 Ok(x as $tgt_ty)
1252 })
1253 },
1254 });
1255 };
1256}
1257
1258macro_rules! all_float_to_int_casts {
1260 ($v:ident, $tgt_ty:ty) => {
1261 float_to_int_cast_push!($v, TYPE_F32, f32, $tgt_ty);
1262 float_to_int_cast_push!($v, TYPE_F64, f64, $tgt_ty);
1263 };
1264}
1265
1266macro_rules! int_cast_sources {
1267 ($name:ident, $tgt_ty:ty) => {
1268 static $name: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1269 let mut v = Vec::with_capacity(15);
1270 all_int_to_int_casts!(v, $tgt_ty);
1271 all_float_to_int_casts!(v, $tgt_ty);
1272 as_cast_push!(v, TYPE_BOOL, bool, $tgt_ty);
1273 v
1274 });
1275 };
1276}
1277
1278int_cast_sources!(U8_CAST_SOURCES, u8);
1279int_cast_sources!(U16_CAST_SOURCES, u16);
1280int_cast_sources!(U32_CAST_SOURCES, u32);
1281int_cast_sources!(U64_CAST_SOURCES, u64);
1282int_cast_sources!(U128_CAST_SOURCES, u128);
1283int_cast_sources!(USIZE_CAST_SOURCES, usize);
1284int_cast_sources!(I8_CAST_SOURCES, i8);
1285int_cast_sources!(I16_CAST_SOURCES, i16);
1286int_cast_sources!(I32_CAST_SOURCES, i32);
1287int_cast_sources!(I64_CAST_SOURCES, i64);
1288int_cast_sources!(I128_CAST_SOURCES, i128);
1289int_cast_sources!(ISIZE_CAST_SOURCES, isize);
1290
1291static F32_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1292 let mut v = vec![
1293 CastSignature {
1299 source_type_id_index: TYPE_F64,
1300 op_fn: |seg, span| {
1301 seg.op1r(move |x: f64| -> Result<f32> {
1302 if !x.is_finite() {
1303 return Err(span_err(span, anyhow!("value is not finite")));
1304 }
1305 if x.abs() > f32::MAX as f64 {
1306 return Err(span_err(span, anyhow!("value does not fit in `f32`")));
1307 }
1308 Ok(x as f32)
1309 })
1310 },
1311 },
1312 CastSignature {
1313 source_type_id_index: TYPE_F32,
1314 op_fn: |seg, _span| seg.op1(|x: f32| x),
1315 },
1316 ];
1317 all_int_to_float_casts!(v, f32);
1318 v
1319});
1320
1321static F64_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1322 let mut v = vec![
1323 CastSignature {
1325 source_type_id_index: TYPE_F32,
1326 op_fn: |seg, _span| seg.op1(|x: f32| x as f64),
1327 },
1328 CastSignature {
1329 source_type_id_index: TYPE_F64,
1330 op_fn: |seg, _span| seg.op1(|x: f64| x),
1331 },
1332 ];
1333 all_int_to_float_casts!(v, f64);
1334 v
1335});
1336
1337static BOOL_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1341 vec![CastSignature {
1342 source_type_id_index: TYPE_BOOL,
1343 op_fn: |seg, _span| seg.op1(|x: bool| x),
1344 }]
1345});
1346
1347static STRING_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1351 vec![CastSignature {
1352 source_type_id_index: TYPE_STR,
1353 op_fn: |seg, _span| seg.op1(|x: String| x),
1354 }]
1355});
1356
1357fn signatures_for_cast(target_name: &str) -> Option<&'static [CastSignature]> {
1363 match target_name {
1364 "u8" => Some(&U8_CAST_SOURCES),
1365 "u16" => Some(&U16_CAST_SOURCES),
1366 "u32" => Some(&U32_CAST_SOURCES),
1367 "u64" => Some(&U64_CAST_SOURCES),
1368 "u128" => Some(&U128_CAST_SOURCES),
1369 "usize" => Some(&USIZE_CAST_SOURCES),
1370 "i8" => Some(&I8_CAST_SOURCES),
1371 "i16" => Some(&I16_CAST_SOURCES),
1372 "i32" => Some(&I32_CAST_SOURCES),
1373 "i64" => Some(&I64_CAST_SOURCES),
1374 "i128" => Some(&I128_CAST_SOURCES),
1375 "isize" => Some(&ISIZE_CAST_SOURCES),
1376 "f32" => Some(&F32_CAST_SOURCES),
1377 "f64" => Some(&F64_CAST_SOURCES),
1378 "bool" => Some(&BOOL_CAST_SOURCES),
1379 "String" => Some(&STRING_CAST_SOURCES),
1380 _ => None,
1381 }
1382}
1383
1384pub fn cast_source_types(target_name: &str) -> impl Iterator<Item = TypeId> {
1401 signatures_for_cast(target_name)
1402 .into_iter()
1403 .flat_map(|sigs| sigs.iter().map(CastSignature::source_type_id))
1404}
1405
1406pub(crate) struct BuiltinScalarType {
1411 pub(crate) type_name: &'static str,
1412 pub(crate) value_type: fn() -> cel_runtime::value_type::ValueType,
1414}
1415
1416fn complete_builtin_leaf<T: Clone + PartialEq + std::fmt::Debug + 'static>()
1418-> cel_runtime::value_type::ValueType {
1419 use cel_runtime::value_type::{Leaf, ValueType};
1420 ValueType::cloneable_leaf::<T>()
1421 .with_equality::<Leaf<T>>()
1422 .expect("same concrete leaf")
1423 .with_debug::<Leaf<T>>()
1424 .expect("same concrete leaf")
1425}
1426
1427macro_rules! builtin_scalars {
1431 ($($name:literal => $ty:ty),* $(,)?) => {
1432 #[cfg(test)]
1435 pub(crate) const BUILTIN_SCALAR_NAMES: &[&str] = &[$($name),*];
1436
1437 pub(crate) fn builtin_value_type(id: TypeId) -> Option<cel_runtime::ValueType> {
1439 $(if id == TypeId::of::<$ty>() {
1440 return Some(complete_builtin_leaf::<$ty>());
1441 })*
1442 None
1443 }
1444
1445 pub(crate) fn builtin_scalar_type(name: &str) -> Option<BuiltinScalarType> {
1451 Some(match name {
1452 $($name => BuiltinScalarType {
1453 type_name: $name,
1454 value_type: complete_builtin_leaf::<$ty>,
1455 },)*
1456 _ => return None,
1457 })
1458 }
1459 };
1460}
1461
1462builtin_scalars! {
1463 "u8" => u8,
1464 "u16" => u16,
1465 "u32" => u32,
1466 "u64" => u64,
1467 "u128" => u128,
1468 "usize" => usize,
1469 "i8" => i8,
1470 "i16" => i16,
1471 "i32" => i32,
1472 "i64" => i64,
1473 "i128" => i128,
1474 "isize" => isize,
1475 "f32" => f32,
1476 "f64" => f64,
1477 "bool" => bool,
1478 "String" => String,
1479}
1480
1481macro_rules! builtin_generic_types {
1487 ($($generic:literal => { $($arg_name:literal => $ty:ty),* $(,)? }),* $(,)?) => {
1488 pub(crate) fn builtin_generic_type(
1495 generic_name: &str,
1496 arg_type_name: &str,
1497 ) -> Option<BuiltinScalarType> {
1498 match generic_name {
1499 $($generic => match arg_type_name {
1500 $($arg_name => Some(BuiltinScalarType {
1501 type_name: concat!($generic, "(", $arg_name, ")"),
1502 value_type: complete_builtin_leaf::<$ty>,
1503 }),)*
1504 _ => None,
1505 },)*
1506 _ => None,
1507 }
1508 }
1509 };
1510}
1511
1512builtin_generic_types! {
1513 "Range" => {
1514 "u8" => std::ops::Range<u8>, "u16" => std::ops::Range<u16>,
1515 "u32" => std::ops::Range<u32>, "u64" => std::ops::Range<u64>,
1516 "u128" => std::ops::Range<u128>, "usize" => std::ops::Range<usize>,
1517 "i8" => std::ops::Range<i8>, "i16" => std::ops::Range<i16>,
1518 "i32" => std::ops::Range<i32>, "i64" => std::ops::Range<i64>,
1519 "i128" => std::ops::Range<i128>, "isize" => std::ops::Range<isize>,
1520 "f32" => std::ops::Range<f32>, "f64" => std::ops::Range<f64>,
1521 },
1522 "RangeInclusive" => {
1523 "u8" => std::ops::RangeInclusive<u8>, "u16" => std::ops::RangeInclusive<u16>,
1524 "u32" => std::ops::RangeInclusive<u32>, "u64" => std::ops::RangeInclusive<u64>,
1525 "u128" => std::ops::RangeInclusive<u128>, "usize" => std::ops::RangeInclusive<usize>,
1526 "i8" => std::ops::RangeInclusive<i8>, "i16" => std::ops::RangeInclusive<i16>,
1527 "i32" => std::ops::RangeInclusive<i32>, "i64" => std::ops::RangeInclusive<i64>,
1528 "i128" => std::ops::RangeInclusive<i128>, "isize" => std::ops::RangeInclusive<isize>,
1529 "f32" => std::ops::RangeInclusive<f32>, "f64" => std::ops::RangeInclusive<f64>,
1530 },
1531 "RangeFrom" => {
1532 "u8" => std::ops::RangeFrom<u8>, "u16" => std::ops::RangeFrom<u16>,
1533 "u32" => std::ops::RangeFrom<u32>, "u64" => std::ops::RangeFrom<u64>,
1534 "u128" => std::ops::RangeFrom<u128>, "usize" => std::ops::RangeFrom<usize>,
1535 "i8" => std::ops::RangeFrom<i8>, "i16" => std::ops::RangeFrom<i16>,
1536 "i32" => std::ops::RangeFrom<i32>, "i64" => std::ops::RangeFrom<i64>,
1537 "i128" => std::ops::RangeFrom<i128>, "isize" => std::ops::RangeFrom<isize>,
1538 "f32" => std::ops::RangeFrom<f32>, "f64" => std::ops::RangeFrom<f64>,
1539 },
1540 "RangeTo" => {
1541 "u8" => std::ops::RangeTo<u8>, "u16" => std::ops::RangeTo<u16>,
1542 "u32" => std::ops::RangeTo<u32>, "u64" => std::ops::RangeTo<u64>,
1543 "u128" => std::ops::RangeTo<u128>, "usize" => std::ops::RangeTo<usize>,
1544 "i8" => std::ops::RangeTo<i8>, "i16" => std::ops::RangeTo<i16>,
1545 "i32" => std::ops::RangeTo<i32>, "i64" => std::ops::RangeTo<i64>,
1546 "i128" => std::ops::RangeTo<i128>, "isize" => std::ops::RangeTo<isize>,
1547 "f32" => std::ops::RangeTo<f32>, "f64" => std::ops::RangeTo<f64>,
1548 },
1549 "RangeToInclusive" => {
1550 "u8" => std::ops::RangeToInclusive<u8>, "u16" => std::ops::RangeToInclusive<u16>,
1551 "u32" => std::ops::RangeToInclusive<u32>, "u64" => std::ops::RangeToInclusive<u64>,
1552 "u128" => std::ops::RangeToInclusive<u128>, "usize" => std::ops::RangeToInclusive<usize>,
1553 "i8" => std::ops::RangeToInclusive<i8>, "i16" => std::ops::RangeToInclusive<i16>,
1554 "i32" => std::ops::RangeToInclusive<i32>, "i64" => std::ops::RangeToInclusive<i64>,
1555 "i128" => std::ops::RangeToInclusive<i128>, "isize" => std::ops::RangeToInclusive<isize>,
1556 "f32" => std::ops::RangeToInclusive<f32>, "f64" => std::ops::RangeToInclusive<f64>,
1557 },
1558}
1559
1560pub(crate) fn builtin_generic_type_0(name: &str) -> Option<BuiltinScalarType> {
1565 match name {
1566 "RangeFull" => Some(BuiltinScalarType {
1567 type_name: "RangeFull",
1568 value_type: complete_builtin_leaf::<std::ops::RangeFull>,
1569 }),
1570 _ => None,
1571 }
1572}
1573
1574struct BuiltinScope;
1578
1579impl BuiltinScope {
1580 fn lookup(
1586 &self,
1587 name: &str,
1588 segment: &mut DynSegment,
1589 num_operands: usize,
1590 span: SourceSpan,
1591 ) -> Result<bool> {
1592 let stack_infos = segment.peek_stack_infos(num_operands);
1593 let Some(signatures) = signatures_for(name) else {
1594 return Ok(false);
1595 };
1596 for sig in signatures {
1597 let arity = sig.arity as usize;
1598 let matches = arity == stack_infos.len()
1599 && stack_infos[0].value_type.type_id() == sig.lhs_type_id()
1600 && (arity < 2 || stack_infos[1].value_type.type_id() == sig.rhs_type_id());
1601 if matches {
1602 (sig.op_fn)(segment, span)?;
1603 return Ok(true);
1604 }
1605 }
1606 Ok(false)
1607 }
1608}
1609
1610fn range_full_scope(
1619 name: &str,
1620 segment: &mut DynSegment,
1621 num_operands: usize,
1622 _span: SourceSpan,
1623) -> Result<bool> {
1624 match (name, num_operands) {
1625 ("range_full", 0) => {
1626 segment.op0(|| std::ops::RangeFull);
1627 Ok(true)
1628 }
1629 _ => Ok(false),
1630 }
1631}
1632
1633pub struct OpLookup {
1655 pub(crate) registered: crate::registered_ops::RegisteredOps,
1656 scopes: Vec<ScopeFn>,
1657 library_scope_count: usize,
1658 builtin_scope: BuiltinScope,
1659 tuple_signatures: Vec<TupleOpSignature>,
1660}
1661
1662impl OpLookup {
1663 pub fn new() -> Self {
1674 let mut lookup = OpLookup {
1675 registered: Default::default(),
1676 scopes: Vec::new(),
1677 library_scope_count: 0,
1678 builtin_scope: BuiltinScope,
1679 tuple_signatures: Vec::new(),
1680 };
1681 lookup.push_library_scope(range_full_scope);
1682 lookup
1683 }
1684
1685 pub fn register_tuple_op(&mut self, signature: TupleOpSignature) {
1689 self.tuple_signatures.push(signature);
1690 }
1691
1692 fn lookup_tuple_signature(
1698 &self,
1699 name: &str,
1700 segment: &mut DynSegment,
1701 num_operands: usize,
1702 span: SourceSpan,
1703 ) -> Result<bool> {
1704 let stack_infos = segment.peek_stack_infos(num_operands);
1705 for sig in &self.tuple_signatures {
1706 if sig.name != name || sig.tuple_operand_index >= stack_infos.len() {
1707 continue;
1708 }
1709 let tuple_info = &stack_infos[sig.tuple_operand_index];
1710 let shape_matches = tuple_info
1711 .value_type
1712 .tuple_elements()
1713 .is_some_and(|elements| {
1714 elements.len() == sig.shape.len()
1715 && elements
1716 .iter()
1717 .zip(&sig.shape)
1718 .all(|(a, t)| a.value_type.type_id() == *t)
1719 });
1720 if !shape_matches {
1721 continue;
1722 }
1723 let others_match = stack_infos.iter().enumerate().all(|(i, info)| {
1724 i == sig.tuple_operand_index
1725 || sig.operand_type_ids.get(i) == Some(&info.value_type.type_id())
1726 });
1727 if others_match {
1728 (sig.op_fn)(segment, span)?;
1729 return Ok(true);
1730 }
1731 }
1732 Ok(false)
1733 }
1734
1735 pub fn push_scope<F>(&mut self, scope: F)
1742 where
1743 F: Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync + 'static,
1744 {
1745 self.scopes.push(Box::new(scope));
1746 }
1747
1748 pub fn pop_scope(&mut self) -> Option<ScopeFn> {
1756 debug_assert!(
1757 self.scopes.len() > self.library_scope_count,
1758 "pop_scope must not remove a library scope — use isolate_scopes/restore_scopes semantics instead"
1759 );
1760 self.scopes.pop()
1761 }
1762
1763 pub fn push_library_scope<F>(&mut self, scope: F)
1776 where
1777 F: Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync + 'static,
1778 {
1779 debug_assert!(
1780 self.scopes.len() == self.library_scope_count,
1781 "push_library_scope must not be called after a transient scope — use isolate_scopes/restore_scopes semantics instead"
1782 );
1783 self.scopes.push(Box::new(scope));
1784 self.library_scope_count = self.scopes.len();
1785 }
1786
1787 pub fn isolate_scopes(&mut self) -> Vec<ScopeFn> {
1830 self.scopes.split_off(self.library_scope_count)
1831 }
1832
1833 pub fn restore_scopes(&mut self, scopes: Vec<ScopeFn>) {
1864 self.scopes.truncate(self.library_scope_count);
1865 self.scopes.extend(scopes);
1866 }
1867
1868 pub fn lookup(
1897 &self,
1898 name: &str,
1899 segment: &mut DynSegment,
1900 num_operands: usize,
1901 start: proc_macro2::Span,
1902 end: proc_macro2::Span,
1903 ) -> std::result::Result<(), crate::ParseError> {
1904 self.lookup_impl(name, segment, num_operands, start, end, false)
1905 }
1906
1907 pub fn lookup_member(
1932 &self,
1933 name: &str,
1934 segment: &mut DynSegment,
1935 arity: usize,
1936 start: proc_macro2::Span,
1937 end: proc_macro2::Span,
1938 ) -> crate::Result<()> {
1939 self.lookup_impl(name, segment, arity, start, end, true)
1940 }
1941
1942 pub(crate) fn lookup_direct_operation(
1953 &self,
1954 name: &str,
1955 segment: &mut DynSegment,
1956 arity: usize,
1957 span: SourceSpan,
1958 ) -> Result<bool> {
1959 if arity > 0 {
1960 for scope in self.scopes.iter().rev() {
1961 if scope(name, segment, arity, span)? {
1962 return Ok(true);
1963 }
1964 }
1965 }
1966 self.lookup_registered_operation(name, segment, arity, span)
1967 }
1968
1969 fn lookup_impl(
1974 &self,
1975 name: &str,
1976 segment: &mut DynSegment,
1977 num_operands: usize,
1978 start: proc_macro2::Span,
1979 end: proc_macro2::Span,
1980 member: bool,
1981 ) -> crate::Result<()> {
1982 let source_span = SourceSpan::from_proc_macro2_range(start, end);
1983 for scope in self.scopes.iter().rev().filter(|_| !member) {
1984 match scope(name, segment, num_operands, source_span) {
1985 Ok(true) => return Ok(()),
1986 Ok(false) => {}
1987 Err(e) => return Err(crate::ParseError::new_range(e.to_string(), start, end)),
1988 }
1989 }
1990
1991 let registered = if member {
1992 self.lookup_direct_operation(name, segment, num_operands, source_span)
1993 } else {
1994 self.lookup_registered(name, segment, num_operands, source_span)
1995 };
1996 match registered {
1997 Ok(true) => return Ok(()),
1998 Ok(false) => {}
1999 Err(error) => return Err(crate::ParseError::new_range(error.to_string(), start, end)),
2000 }
2001
2002 match self.lookup_tuple_signature(name, segment, num_operands, source_span) {
2003 Ok(true) => return Ok(()),
2004 Ok(false) => {}
2005 Err(e) => {
2006 return Err(crate::ParseError::new_range(
2007 format!("operation error: {}", e),
2008 start,
2009 end,
2010 ));
2011 }
2012 }
2013
2014 match self
2015 .builtin_scope
2016 .lookup(name, segment, num_operands, source_span)
2017 {
2018 Ok(true) => return Ok(()),
2019 Ok(false) => {}
2020 Err(e) => {
2021 return Err(crate::ParseError::new_range(
2022 format!("operation error: {}", e),
2023 start,
2024 end,
2025 ));
2026 }
2027 }
2028
2029 if !member
2030 && name == "()"
2031 && num_operands > 0
2032 && segment
2033 .peek_stack_infos(num_operands)
2034 .first()
2035 .is_some_and(|info| {
2036 info.value_type.type_id() == TypeId::of::<cel_runtime::DynClosure>()
2037 })
2038 {
2039 return segment
2040 .invoke_closure(num_operands - 1)
2041 .map_err(|error| crate::ParseError::new_range(error.to_string(), start, end));
2042 }
2043
2044 if num_operands == 0 {
2045 return Err(crate::ParseError::new(
2046 format!("undefined identifier: `{name}`"),
2047 start,
2048 ));
2049 }
2050 let infos = segment.peek_stack_infos(num_operands);
2051 let mut type_names = String::new();
2052 for (i, info) in infos.iter().enumerate() {
2053 if i > 0 {
2054 type_names.push_str(", ");
2055 }
2056 type_names.push('`');
2057 type_names.push_str(info.value_type.type_name());
2058 type_names.push('`');
2059 }
2060 Err(crate::ParseError::new_range(
2061 format!("no operation `{name}` for types [{type_names}]"),
2062 start,
2063 end,
2064 ))
2065 }
2066
2067 pub fn lookup_cast(
2101 &self,
2102 type_name: &str,
2103 segment: &mut DynSegment,
2104 start: proc_macro2::Span,
2105 end: proc_macro2::Span,
2106 ) -> std::result::Result<(), crate::ParseError> {
2107 let source_span = SourceSpan::from_proc_macro2_range(start, end);
2108 let Some(signatures) = signatures_for_cast(type_name) else {
2109 return Err(crate::ParseError::new_range(
2110 format!("unknown type `{type_name}`"),
2111 start,
2112 end,
2113 ));
2114 };
2115 let Some(operand) = segment.peek_stack_infos(1).first() else {
2116 return Err(crate::ParseError::new_range(
2117 "cast requires an operand on the stack".to_string(),
2118 start,
2119 end,
2120 ));
2121 };
2122 let source_type_id = operand.value_type.type_id();
2123 for sig in signatures {
2124 if sig.source_type_id() == source_type_id {
2125 (sig.op_fn)(segment, source_span).map_err(|e| {
2126 crate::ParseError::new_range(format!("cast error: {}", e), start, end)
2127 })?;
2128 return Ok(());
2129 }
2130 }
2131 Err(crate::ParseError::new_range(
2132 format!(
2133 "no cast from `{}` to `{type_name}`",
2134 operand.value_type.type_name()
2135 ),
2136 start,
2137 end,
2138 ))
2139 }
2140}
2141
2142impl Default for OpLookup {
2143 fn default() -> Self {
2144 Self::new()
2145 }
2146}
2147
2148#[cfg(test)]
2149mod tests {
2150 use super::*;
2151 use proc_macro2::Span;
2152
2153 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
2154
2155 #[test]
2156 fn test_addition_u32() -> Result<()> {
2157 let lookup = OpLookup::new();
2158 let mut segment = DynSegment::new::<()>();
2159 segment.just(10u32);
2160 segment.just(20u32);
2161 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2162 assert_eq!(segment.call0::<u32>()?, 30);
2163 Ok(())
2164 }
2165
2166 #[test]
2167 fn test_subtraction_i32() -> Result<()> {
2168 let lookup = OpLookup::new();
2169 let mut segment = DynSegment::new::<()>();
2170 segment.just(50i32);
2171 segment.just(20i32);
2172 lookup.lookup("-", &mut segment, 2, Span::call_site(), Span::call_site())?;
2173 assert_eq!(segment.call0::<i32>()?, 30);
2174 Ok(())
2175 }
2176
2177 #[test]
2178 fn test_arithmetic_overflow() -> Result<()> {
2179 let lookup = OpLookup::new();
2180 let mut segment = DynSegment::new::<()>();
2181 segment.just(i32::MAX);
2182 segment.just(1i32);
2183 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2184 let result = segment.call0::<i32>();
2185 assert!(result.is_err());
2186 let err = result.unwrap_err();
2187 let message = format!("{:#}", err);
2188 assert!(
2189 message.contains("arithmetic overflow"),
2190 "error message should mention arithmetic overflow, got: {message}"
2191 );
2192 Ok(())
2193 }
2194
2195 #[test]
2196 fn test_division_by_zero() -> Result<()> {
2197 let lookup = OpLookup::new();
2198 let mut segment = DynSegment::new::<()>();
2199 segment.just(10i32);
2200 segment.just(0i32);
2201 lookup.lookup("/", &mut segment, 2, Span::call_site(), Span::call_site())?;
2202 let result = segment.call0::<i32>();
2203 assert!(result.is_err());
2204 let err = result.unwrap_err();
2205 let message = format!("{:#}", err);
2206 assert!(
2207 message.contains("division by zero"),
2208 "error message should mention division by zero, got: {message}"
2209 );
2210 Ok(())
2211 }
2212
2213 #[test]
2214 fn test_modulo_by_zero() -> Result<()> {
2215 let lookup = OpLookup::new();
2216 let mut segment = DynSegment::new::<()>();
2217 segment.just(10u32);
2218 segment.just(0u32);
2219 lookup.lookup("%", &mut segment, 2, Span::call_site(), Span::call_site())?;
2220 let result = segment.call0::<u32>();
2221 assert!(result.is_err());
2222 let err = result.unwrap_err();
2223 let message = format!("{:#}", err);
2224 assert!(
2225 message.contains("division by zero"),
2226 "error message should mention division by zero, got: {message}"
2227 );
2228 Ok(())
2229 }
2230
2231 #[test]
2232 fn test_multiplication_f64() -> Result<()> {
2233 let lookup = OpLookup::new();
2234 let mut segment = DynSegment::new::<()>();
2235 segment.just(3.5f64);
2236 segment.just(2.0f64);
2237 lookup.lookup("*", &mut segment, 2, Span::call_site(), Span::call_site())?;
2238 assert_eq!(segment.call0::<f64>()?, 7.0);
2239 Ok(())
2240 }
2241
2242 #[test]
2243 fn test_comparison_less_than() -> Result<()> {
2244 let lookup = OpLookup::new();
2245 let mut segment = DynSegment::new::<()>();
2246 segment.just(10u32);
2247 segment.just(20u32);
2248 lookup.lookup("<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2249 assert!(segment.call0::<bool>()?);
2250 Ok(())
2251 }
2252
2253 #[test]
2254 fn test_bitwise_and() -> Result<()> {
2255 let lookup = OpLookup::new();
2256 let mut segment = DynSegment::new::<()>();
2257 segment.just(0b1010u32);
2258 segment.just(0b1100u32);
2259 lookup.lookup("&", &mut segment, 2, Span::call_site(), Span::call_site())?;
2260 assert_eq!(segment.call0::<u32>()?, 0b1000);
2261 Ok(())
2262 }
2263
2264 #[test]
2265 fn test_unary_negation() -> Result<()> {
2266 let lookup = OpLookup::new();
2267 let mut segment = DynSegment::new::<()>();
2268 segment.just(42i32);
2269 lookup.lookup("-", &mut segment, 1, Span::call_site(), Span::call_site())?;
2270 assert_eq!(segment.call0::<i32>()?, -42);
2271 Ok(())
2272 }
2273
2274 #[test]
2275 fn test_logical_not() -> Result<()> {
2276 let lookup = OpLookup::new();
2277 let mut segment = DynSegment::new::<()>();
2278 segment.just(true);
2279 lookup.lookup("!", &mut segment, 1, Span::call_site(), Span::call_site())?;
2280 assert!(!segment.call0::<bool>()?);
2281 Ok(())
2282 }
2283
2284 #[test]
2285 fn test_unregistered_operation() {
2286 let lookup = OpLookup::new();
2287 let mut segment = DynSegment::new::<()>();
2288 segment.just(10u32);
2289 segment.just(20u32);
2290 let result = lookup.lookup(
2291 "unknown_op",
2292 &mut segment,
2293 2,
2294 Span::call_site(),
2295 Span::call_site(),
2296 );
2297 assert!(result.is_err());
2298 }
2299
2300 #[test]
2301 fn test_custom_scope() -> Result<()> {
2302 let mut lookup = OpLookup::new();
2303
2304 lookup.push_scope(|name, segment, num_operands, _span| {
2305 let matches = {
2306 let top = segment.peek_stack_infos(num_operands);
2307 name == "double"
2308 && top.len() == 1
2309 && top[0].value_type.type_id() == TypeId::of::<u32>()
2310 };
2311 if matches {
2312 segment.op1(|a: u32| a * 2)?;
2313 Ok(true)
2314 } else {
2315 Ok(false)
2316 }
2317 });
2318
2319 let mut segment = DynSegment::new::<()>();
2320 segment.just(21u32);
2321 lookup.lookup(
2322 "double",
2323 &mut segment,
2324 1,
2325 Span::call_site(),
2326 Span::call_site(),
2327 )?;
2328 assert_eq!(segment.call0::<u32>()?, 42);
2329
2330 Ok(())
2331 }
2332
2333 #[test]
2334 fn test_scope_override() -> Result<()> {
2335 let mut lookup = OpLookup::new();
2336
2337 lookup.push_scope(|name, segment, num_operands, _span| {
2338 let matches = {
2339 let top = segment.peek_stack_infos(num_operands);
2340 name == "+" && top.len() == 2 && top[0].value_type.type_id() == TypeId::of::<u32>()
2341 };
2342 if matches {
2343 segment.op2(|_a: u32, _b: u32| 100u32)?;
2344 Ok(true)
2345 } else {
2346 Ok(false)
2347 }
2348 });
2349
2350 let mut segment = DynSegment::new::<()>();
2351 segment.just(10u32);
2352 segment.just(20u32);
2353 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2354 assert_eq!(segment.call0::<u32>()?, 100);
2355
2356 Ok(())
2357 }
2358
2359 #[test]
2360 fn lookup_cast_errors_rather_than_panics_on_an_empty_stack() -> Result<()> {
2361 let lookup = OpLookup::new();
2366 let mut segment = DynSegment::new::<()>();
2367 let result = lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site());
2368 assert!(result.is_err());
2369 Ok(())
2370 }
2371
2372 #[test]
2373 fn cast_widens_i32_to_f64_exactly() -> Result<()> {
2374 let lookup = OpLookup::new();
2375 let mut segment = DynSegment::new::<()>();
2376 segment.just(1024i32);
2377 lookup.lookup_cast("f64", &mut segment, Span::call_site(), Span::call_site())?;
2378 assert_eq!(segment.call0::<f64>()?, 1024.0);
2379 Ok(())
2380 }
2381
2382 #[test]
2383 fn cast_narrows_f64_to_i32_when_the_value_fits() -> Result<()> {
2384 let lookup = OpLookup::new();
2385 let mut segment = DynSegment::new::<()>();
2386 segment.just(1024.0f64);
2387 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2388 assert_eq!(segment.call0::<i32>()?, 1024);
2389 Ok(())
2390 }
2391
2392 #[test]
2393 fn cast_errors_when_the_value_does_not_fit_in_the_target_type() -> Result<()> {
2394 let lookup = OpLookup::new();
2399 let mut segment = DynSegment::new::<()>();
2400 segment.just(1.0e20f64);
2401 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2402 assert!(segment.call0::<i32>().is_err());
2403 Ok(())
2404 }
2405
2406 #[test]
2407 fn cast_errors_just_past_the_target_types_max_even_when_max_is_not_exactly_representable()
2408 -> Result<()> {
2409 let lookup = OpLookup::new();
2415 let mut segment = DynSegment::new::<()>();
2416 segment.just(2147483648.0f32); lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2418 assert!(segment.call0::<i32>().is_err());
2419 Ok(())
2420 }
2421
2422 #[test]
2423 fn cast_accepts_the_target_types_actual_max_value() -> Result<()> {
2424 let lookup = OpLookup::new();
2425 let mut segment = DynSegment::new::<()>();
2426 segment.just(2147483520.0f32); lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2428 assert_eq!(segment.call0::<i32>()?, 2147483520);
2429 Ok(())
2430 }
2431
2432 #[test]
2433 fn cast_errors_just_past_i64_max_from_f64() -> Result<()> {
2434 let lookup = OpLookup::new();
2437 let mut segment = DynSegment::new::<()>();
2438 segment.just(9223372036854775808.0f64); lookup.lookup_cast("i64", &mut segment, Span::call_site(), Span::call_site())?;
2440 assert!(segment.call0::<i64>().is_err());
2441 Ok(())
2442 }
2443
2444 #[test]
2445 fn cast_errors_for_an_unknown_target_type() -> Result<()> {
2446 let lookup = OpLookup::new();
2447 let mut segment = DynSegment::new::<()>();
2448 segment.just(1i32);
2449 let result = lookup.lookup_cast(
2450 "nonsense",
2451 &mut segment,
2452 Span::call_site(),
2453 Span::call_site(),
2454 );
2455 assert!(result.is_err());
2456 Ok(())
2457 }
2458
2459 #[test]
2460 fn cast_errors_for_an_unregistered_source_type() -> Result<()> {
2461 let lookup = OpLookup::new();
2464 let mut segment = DynSegment::new::<()>();
2465 segment.just("1".to_string());
2466 let result = lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site());
2467 assert!(result.is_err());
2468 Ok(())
2469 }
2470
2471 #[test]
2472 fn cast_from_bool_is_registered_for_every_integer_width() -> Result<()> {
2473 let lookup = OpLookup::new();
2475 for target in [
2476 "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
2477 ] {
2478 let mut segment = DynSegment::new::<()>();
2479 segment.just(true);
2480 lookup.lookup_cast(target, &mut segment, Span::call_site(), Span::call_site())?;
2481 }
2482 Ok(())
2483 }
2484
2485 #[test]
2486 fn cast_widens_bool_to_a_specific_integer_value() -> Result<()> {
2487 let lookup = OpLookup::new();
2489 let mut segment = DynSegment::new::<()>();
2490 segment.just(true);
2491 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2492 assert_eq!(segment.call0::<i32>()?, 1);
2493
2494 let mut segment = DynSegment::new::<()>();
2495 segment.just(false);
2496 lookup.lookup_cast("u64", &mut segment, Span::call_site(), Span::call_site())?;
2497 assert_eq!(segment.call0::<u64>()?, 0);
2498 Ok(())
2499 }
2500
2501 #[test]
2502 fn cast_bool_to_bool_is_a_no_op_identity_conversion() -> Result<()> {
2503 let lookup = OpLookup::new();
2504 let mut segment = DynSegment::new::<()>();
2505 segment.just(true);
2506 lookup.lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site())?;
2507 assert!(segment.call0::<bool>()?);
2508 Ok(())
2509 }
2510
2511 #[test]
2512 fn cast_string_to_string_is_a_no_op_identity_conversion() -> Result<()> {
2513 let lookup = OpLookup::new();
2514 let mut segment = DynSegment::new::<()>();
2515 segment.just("hello".to_string());
2516 lookup.lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())?;
2517 assert_eq!(segment.call0::<String>()?, "hello");
2518 Ok(())
2519 }
2520
2521 #[test]
2522 fn cast_errors_from_a_number_to_bool() -> Result<()> {
2523 let lookup = OpLookup::new();
2526 let mut segment = DynSegment::new::<()>();
2527 segment.just(1i32);
2528 let result = lookup.lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site());
2529 assert!(result.is_err());
2530 Ok(())
2531 }
2532
2533 #[test]
2534 fn cast_errors_from_bool_to_a_float() -> Result<()> {
2535 let lookup = OpLookup::new();
2537 let mut segment = DynSegment::new::<()>();
2538 segment.just(true);
2539 let result = lookup.lookup_cast("f64", &mut segment, Span::call_site(), Span::call_site());
2540 assert!(result.is_err());
2541 Ok(())
2542 }
2543
2544 #[test]
2545 fn cast_errors_between_string_and_bool_in_both_directions() -> Result<()> {
2546 let lookup = OpLookup::new();
2547 let mut segment = DynSegment::new::<()>();
2548 segment.just(true);
2549 assert!(
2550 lookup
2551 .lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())
2552 .is_err()
2553 );
2554
2555 let mut segment = DynSegment::new::<()>();
2556 segment.just("true".to_string());
2557 assert!(
2558 lookup
2559 .lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site())
2560 .is_err()
2561 );
2562 Ok(())
2563 }
2564
2565 #[test]
2566 fn cast_errors_between_string_and_numbers_in_both_directions() -> Result<()> {
2567 let lookup = OpLookup::new();
2568 let mut segment = DynSegment::new::<()>();
2569 segment.just(1i32);
2570 assert!(
2571 lookup
2572 .lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())
2573 .is_err()
2574 );
2575
2576 let mut segment = DynSegment::new::<()>();
2577 segment.just("1".to_string());
2578 assert!(
2579 lookup
2580 .lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())
2581 .is_err()
2582 );
2583 Ok(())
2584 }
2585
2586 #[test]
2587 fn test_left_shift_u64() -> Result<()> {
2588 let lookup = OpLookup::new();
2589 let mut segment = DynSegment::new::<()>();
2590 segment.just(1u64);
2591 segment.just(3u32);
2592 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2593 assert_eq!(segment.call0::<u64>()?, 8);
2594 Ok(())
2595 }
2596
2597 #[test]
2598 fn test_right_shift_i32() -> Result<()> {
2599 let lookup = OpLookup::new();
2600 let mut segment = DynSegment::new::<()>();
2601 segment.just(16i32);
2602 segment.just(2u32);
2603 lookup.lookup(">>", &mut segment, 2, Span::call_site(), Span::call_site())?;
2604 assert_eq!(segment.call0::<i32>()?, 4);
2605 Ok(())
2606 }
2607
2608 #[test]
2609 fn test_shift_overflow() -> Result<()> {
2610 let lookup = OpLookup::new();
2611 let mut segment = DynSegment::new::<()>();
2612 segment.just(1u32);
2613 segment.just(32u32);
2614 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2615 let result = segment.call0::<u32>();
2616 assert!(result.is_err());
2617 let err = result.unwrap_err();
2618 let message = format!("{:#}", err);
2619 assert!(
2620 message.contains("shift overflow"),
2621 "error message should mention shift overflow, got: {message}"
2622 );
2623 Ok(())
2624 }
2625
2626 #[test]
2627 fn test_shift_i32_rhs() -> Result<()> {
2628 let lookup = OpLookup::new();
2629 let mut segment = DynSegment::new::<()>();
2630 segment.just(1u32);
2631 segment.just(3i32);
2632 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2633 assert_eq!(segment.call0::<u32>()?, 8);
2634 Ok(())
2635 }
2636
2637 #[test]
2638 fn test_shift_negative_rhs_errors() -> Result<()> {
2639 let lookup = OpLookup::new();
2640 let mut segment = DynSegment::new::<()>();
2641 segment.just(1u32);
2642 segment.just(-1i32);
2643 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2644 let result = segment.call0::<u32>();
2645 assert!(result.is_err());
2646 let err = result.unwrap_err();
2647 let message = format!("{:#}", err);
2648 assert!(message.contains("shift overflow"), "got: {message}");
2649 Ok(())
2650 }
2651
2652 #[test]
2653 fn test_shift_wide_rhs_overflow_errors() -> Result<()> {
2654 let lookup = OpLookup::new();
2655 let mut segment = DynSegment::new::<()>();
2656 segment.just(1u32);
2657 segment.just(u32::MAX as u64 + 1);
2658 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2659 let result = segment.call0::<u32>();
2660 assert!(result.is_err());
2661 let err = result.unwrap_err();
2662 let message = format!("{:#}", err);
2663 assert!(message.contains("shift overflow"), "got: {message}");
2664 Ok(())
2665 }
2666
2667 #[test]
2668 fn test_shift_rejects_float_rhs() {
2669 let lookup = OpLookup::new();
2670 let mut segment = DynSegment::new::<()>();
2671 segment.just(1u32);
2672 segment.just(3.0f64);
2673 assert!(
2674 lookup
2675 .lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())
2676 .is_err()
2677 );
2678 }
2679
2680 #[test]
2681 fn test_scope_pop() -> Result<()> {
2682 let mut lookup = OpLookup::new();
2683
2684 lookup.push_scope(|name, segment, num_operands, _span| {
2685 let matches = {
2686 let top = segment.peek_stack_infos(num_operands);
2687 name == "+" && top.len() == 2 && top[0].value_type.type_id() == TypeId::of::<u32>()
2688 };
2689 if matches {
2690 segment.op2(|_a: u32, _b: u32| 100u32)?;
2691 Ok(true)
2692 } else {
2693 Ok(false)
2694 }
2695 });
2696
2697 let mut segment = DynSegment::new::<()>();
2698 segment.just(10u32);
2699 segment.just(20u32);
2700 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2701 assert_eq!(segment.call0::<u32>()?, 100);
2702
2703 lookup.pop_scope();
2704 let mut segment = DynSegment::new::<()>();
2705 segment.just(10u32);
2706 segment.just(20u32);
2707 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2708 assert_eq!(segment.call0::<u32>()?, 30);
2709
2710 Ok(())
2711 }
2712
2713 #[test]
2714 fn lookup_not_found_error_carries_span() {
2715 let lookup = OpLookup::new();
2716 let mut segment = DynSegment::new::<()>();
2717 segment.just(10u32);
2718 segment.just(20.0f64);
2719 let err = lookup
2720 .lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())
2721 .unwrap_err();
2722 assert!(
2723 err.message().starts_with("no operation"),
2724 "expected 'no operation' prefix, got: {}",
2725 err.message()
2726 );
2727 assert!(err.message().contains("`+`"));
2728 assert!(err.message().contains("`u32`"));
2729 assert!(err.message().contains("`f64`"));
2730 }
2731
2732 #[test]
2733 fn lookup_not_found_error_has_range() {
2734 let lookup = OpLookup::new();
2735 let mut segment = DynSegment::new::<()>();
2736 segment.just(10u32);
2737 segment.just(20.0f64);
2738 let err = lookup
2739 .lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())
2740 .unwrap_err();
2741 assert!(
2742 err.end_span().is_some(),
2743 "op-lookup errors should carry an end span"
2744 );
2745 }
2746
2747 #[cfg(feature = "span-diagnostics")]
2750 #[test]
2751 fn scope_fn_accepts_source_span_parameter() {
2752 let mut lookup = OpLookup::new();
2753 lookup.push_scope(
2754 |_name: &str, _seg: &mut DynSegment, _n: usize, span: crate::SourceSpan| {
2755 let _ = span;
2757 Ok(false)
2758 },
2759 );
2760 }
2762
2763 #[test]
2767 fn format_rustc_style_falls_back_for_client_added_op_error() {
2768 use crate::FormatRustcStyle;
2769 use annotate_snippets::Renderer;
2770
2771 let err = anyhow::anyhow!("custom domain error");
2772 let output = err.format_rustc_style("unused source", "unused.cel", 1, &Renderer::plain());
2773 assert_eq!(output, "custom domain error");
2774 }
2775
2776 #[cfg(feature = "span-diagnostics")]
2777 #[test]
2778 fn runtime_error_carries_span_context() {
2779 use crate::{CELParser, FormatRustcStyle, SpanContext};
2780 use annotate_snippets::Renderer;
2781
2782 let mut parser = CELParser::new(OpLookup::new());
2783 let source = "1i32 + 2147483647i32"; let mut segment = parser.parse_str(source).expect("should parse");
2785 let err = segment.call0::<i32>().expect_err("should overflow");
2786 let ctx = err
2787 .downcast_ref::<SpanContext>()
2788 .expect("expected SpanContext on runtime error");
2789 assert_eq!(ctx.span().start.line, 1);
2793 let rendered = err.format_rustc_style(source, "test.cel", 1, &Renderer::plain());
2795 assert!(
2796 rendered.contains("arithmetic overflow"),
2797 "expected 'arithmetic overflow' in rendered output, got: {rendered}"
2798 );
2799 assert!(
2800 rendered.contains('^'),
2801 "expected caret marker in rendered output, got: {rendered}"
2802 );
2803 }
2804
2805 #[test]
2806 fn tuple_shaped_signature_matches_and_dispatches() -> Result<()> {
2807 let mut lookup = OpLookup::new();
2808 lookup.register_tuple_op(TupleOpSignature {
2809 name: "greet".to_string(),
2810 shape: vec![TypeId::of::<String>(), TypeId::of::<i32>()],
2811 tuple_operand_index: 0,
2812 operand_type_ids: vec![],
2813 op_fn: |seg, _span| {
2814 seg.tuple_index(1)?;
2815 seg.op1(|_ignored: i32| true)
2816 },
2817 });
2818
2819 let mut segment = DynSegment::new::<()>();
2820 let ambient_start = segment.current_stack_offset();
2821 segment.op0(|| "hi".to_string());
2822 segment.op0(|| 7i32);
2823 segment.make_tuple(2, ambient_start)?;
2824
2825 lookup.lookup(
2826 "greet",
2827 &mut segment,
2828 1,
2829 Span::call_site(),
2830 Span::call_site(),
2831 )?;
2832 assert!(segment.call0::<bool>()?);
2833 Ok(())
2834 }
2835
2836 #[test]
2837 fn tuple_shaped_signature_does_not_match_wrong_shape() {
2838 let mut lookup = OpLookup::new();
2839 lookup.register_tuple_op(TupleOpSignature {
2840 name: "greet".to_string(),
2841 shape: vec![TypeId::of::<String>(), TypeId::of::<i32>()],
2842 tuple_operand_index: 0,
2843 operand_type_ids: vec![],
2844 op_fn: |seg, _span| {
2845 seg.tuple_index(1)?;
2846 seg.op1(|_ignored: i32| true)
2847 },
2848 });
2849
2850 let mut segment = DynSegment::new::<()>();
2851 let ambient_start = segment.current_stack_offset();
2852 segment.op0(|| 1i32);
2853 segment.op0(|| 2i32);
2854 segment.make_tuple(2, ambient_start).unwrap();
2855
2856 let result = lookup.lookup(
2857 "greet",
2858 &mut segment,
2859 1,
2860 Span::call_site(),
2861 Span::call_site(),
2862 );
2863 assert!(
2864 result.is_err(),
2865 "shape (i32, i32) should not match (String, i32)"
2866 );
2867 }
2868
2869 #[test]
2870 fn tuple_shaped_signature_with_empty_shape_does_not_match_non_tuple() {
2871 let mut lookup = OpLookup::new();
2875 lookup.register_tuple_op(TupleOpSignature {
2876 name: "unit_greet".to_string(),
2877 shape: vec![],
2878 tuple_operand_index: 0,
2879 operand_type_ids: vec![],
2880 op_fn: |seg, _span| seg.op1(|_ignored: i32| true),
2881 });
2882
2883 let mut segment = DynSegment::new::<()>();
2884 segment.op0(|| 42i32);
2885
2886 let result = lookup.lookup(
2887 "unit_greet",
2888 &mut segment,
2889 1,
2890 Span::call_site(),
2891 Span::call_site(),
2892 );
2893 assert!(
2894 result.is_err(),
2895 "empty-shape tuple signature must not match a non-tuple operand"
2896 );
2897 }
2898
2899 #[test]
2900 fn builtin_operand_types_reports_a_binary_arithmetic_overload() {
2901 let sigs = builtin_operand_types("+");
2902 assert!(
2903 sigs.iter().any(|s| s.arity == 2
2904 && s.lhs == TypeId::of::<i32>()
2905 && s.rhs == TypeId::of::<i32>())
2906 );
2907 }
2908
2909 #[test]
2910 fn builtin_operand_types_reports_a_unary_overload() {
2911 let sigs = builtin_operand_types("!");
2912 assert!(
2913 sigs.iter()
2914 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<bool>())
2915 );
2916 }
2917
2918 #[test]
2919 fn builtin_operand_types_includes_unary_negation_but_only_for_signed_and_float_types() {
2920 let sigs = builtin_operand_types("-");
2921 assert!(
2922 sigs.iter()
2923 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<i32>())
2924 );
2925 assert!(
2926 sigs.iter()
2927 .any(|s| s.arity == 2 && s.lhs == TypeId::of::<i32>())
2928 );
2929 assert!(
2930 !sigs
2931 .iter()
2932 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<u32>()),
2933 "unsigned types have no unary negation overload"
2934 );
2935 }
2936
2937 #[test]
2938 fn builtin_operand_types_covers_heterogeneous_shift_signatures() {
2939 let sigs = builtin_operand_types("<<");
2940 assert!(
2941 sigs.iter().any(|s| s.arity == 2
2942 && s.lhs == TypeId::of::<u64>()
2943 && s.rhs == TypeId::of::<u32>())
2944 );
2945 }
2946
2947 #[test]
2948 fn builtin_operand_types_is_empty_for_an_unregistered_name() {
2949 assert!(builtin_operand_types("not_an_operator").is_empty());
2950 }
2951
2952 #[test]
2953 fn builtin_operand_types_is_empty_for_a_runtime_only_tuple_op() {
2954 assert!(builtin_operand_types("greet").is_empty());
2958 }
2959
2960 #[test]
2961 fn builtin_scalar_type_resolves_every_documented_name() {
2962 for name in BUILTIN_SCALAR_NAMES {
2963 let scalar =
2964 builtin_scalar_type(name).unwrap_or_else(|| panic!("expected `{name}` to resolve"));
2965 assert_eq!(scalar.type_name, *name);
2966 }
2967 assert!(builtin_scalar_type("not_a_type").is_none());
2968 }
2969
2970 #[test]
2971 fn builtin_scalar_type_i32_matches_std_any_type_id() {
2972 let scalar = builtin_scalar_type("i32").unwrap();
2973 assert_eq!((scalar.value_type)().type_id(), TypeId::of::<i32>());
2974 assert_eq!((scalar.value_type)().size(), std::mem::size_of::<i32>());
2975 assert_eq!((scalar.value_type)().align(), std::mem::align_of::<i32>());
2976 }
2977
2978 #[test]
2979 fn builtin_scalar_type_push_arg_declares_a_readable_argument() {
2980 let scalar = builtin_scalar_type("i32").unwrap();
2981 let mut segment = DynSegment::new::<()>();
2982 (scalar.value_type)()
2983 .push_argument(&mut segment, 0)
2984 .unwrap();
2985 let value = 42i32;
2986 let result: i32 = segment.call_dyn(&[&value]).unwrap();
2987 assert_eq!(result, 42);
2988 }
2989
2990 #[test]
2991 fn builtin_generic_type_resolves_every_range_family_member_for_every_numeric_type() {
2992 for generic in [
2993 "Range",
2994 "RangeInclusive",
2995 "RangeFrom",
2996 "RangeTo",
2997 "RangeToInclusive",
2998 ] {
2999 for arg in [
3000 "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128",
3001 "isize", "f32", "f64",
3002 ] {
3003 let resolved = builtin_generic_type(generic, arg)
3004 .unwrap_or_else(|| panic!("expected `{generic}({arg})` to resolve"));
3005 assert_eq!(resolved.type_name, format!("{generic}({arg})"));
3006 }
3007 }
3008 }
3009
3010 #[test]
3011 fn builtin_generic_type_range_inclusive_f64_matches_std_any_type_id() {
3012 let resolved = builtin_generic_type("RangeInclusive", "f64").unwrap();
3013 assert_eq!(
3014 (resolved.value_type)().type_id(),
3015 TypeId::of::<std::ops::RangeInclusive<f64>>()
3016 );
3017 assert_eq!(
3018 (resolved.value_type)().size(),
3019 std::mem::size_of::<std::ops::RangeInclusive<f64>>()
3020 );
3021 }
3022
3023 #[test]
3024 fn builtin_generic_type_is_none_for_unknown_generic_or_argument() {
3025 assert!(builtin_generic_type("NotAGeneric", "f64").is_none());
3026 assert!(builtin_generic_type("Range", "not_a_type").is_none());
3027 assert!(builtin_generic_type("Range", "bool").is_none());
3028 assert!(builtin_generic_type("Range", "String").is_none());
3029 }
3030
3031 #[test]
3032 fn builtin_generic_type_0_resolves_range_full() {
3033 let resolved = builtin_generic_type_0("RangeFull").unwrap();
3034 assert_eq!(resolved.type_name, "RangeFull");
3035 assert_eq!(
3036 (resolved.value_type)().type_id(),
3037 TypeId::of::<std::ops::RangeFull>()
3038 );
3039 assert!(builtin_generic_type_0("NotAGeneric").is_none());
3040 }
3041
3042 #[test]
3043 fn builtin_generic_type_push_arg_declares_a_readable_argument() {
3044 let resolved = builtin_generic_type("RangeInclusive", "i32").unwrap();
3045 let mut segment = DynSegment::new::<()>();
3046 (resolved.value_type)()
3047 .push_argument(&mut segment, 0)
3048 .unwrap();
3049 let value = 1i32..=5i32;
3050 let result: std::ops::RangeInclusive<i32> = segment.call_dyn(&[&value]).unwrap();
3051 assert_eq!(result, 1..=5);
3052 }
3053
3054 #[test]
3055 fn isolate_scopes_removes_pushed_scopes_until_restored() {
3056 let mut lookup = OpLookup::new();
3057 lookup.push_scope(|name, segment, arity, _span| {
3058 if name == "custom" && arity == 0 {
3059 segment.just(1i32);
3060 Ok(true)
3061 } else {
3062 Ok(false)
3063 }
3064 });
3065
3066 let mut segment = DynSegment::new::<()>();
3067 let isolated = lookup.isolate_scopes();
3068 let err = lookup.lookup(
3069 "custom",
3070 &mut segment,
3071 0,
3072 proc_macro2::Span::call_site(),
3073 proc_macro2::Span::call_site(),
3074 );
3075 assert!(
3076 err.is_err(),
3077 "custom scope must not be reachable while isolated"
3078 );
3079
3080 lookup.restore_scopes(isolated);
3081 let mut segment = DynSegment::new::<()>();
3082 lookup
3083 .lookup(
3084 "custom",
3085 &mut segment,
3086 0,
3087 proc_macro2::Span::call_site(),
3088 proc_macro2::Span::call_site(),
3089 )
3090 .unwrap();
3091 assert_eq!(segment.call0::<i32>().unwrap(), 1);
3092 }
3093
3094 #[test]
3095 fn isolate_scopes_leaves_library_scopes_reachable() {
3096 let mut lookup = OpLookup::new();
3097 let mut segment = DynSegment::new::<()>();
3098 let isolated = lookup.isolate_scopes();
3099 lookup
3100 .lookup(
3101 "range_full",
3102 &mut segment,
3103 0,
3104 proc_macro2::Span::call_site(),
3105 proc_macro2::Span::call_site(),
3106 )
3107 .expect("range_full is a library scope and must survive isolation");
3108 lookup.restore_scopes(isolated);
3109 assert_eq!(segment.peek_stack_infos(1).len(), 1);
3110 }
3111
3112 #[test]
3113 fn range_i32_constructs_a_range() -> Result<()> {
3114 let lookup = OpLookup::new();
3115 let mut segment = DynSegment::new::<()>();
3116 segment.just(1i32);
3117 segment.just(5i32);
3118 lookup.lookup(
3119 "range",
3120 &mut segment,
3121 2,
3122 Span::call_site(),
3123 Span::call_site(),
3124 )?;
3125 assert_eq!(segment.call0::<std::ops::Range<i32>>()?, 1i32..5i32);
3126 Ok(())
3127 }
3128
3129 #[test]
3130 fn range_f64_constructs_a_range() -> Result<()> {
3131 let lookup = OpLookup::new();
3132 let mut segment = DynSegment::new::<()>();
3133 segment.just(1.5f64);
3134 segment.just(5.5f64);
3135 lookup.lookup(
3136 "range",
3137 &mut segment,
3138 2,
3139 Span::call_site(),
3140 Span::call_site(),
3141 )?;
3142 assert_eq!(segment.call0::<std::ops::Range<f64>>()?, 1.5f64..5.5f64);
3143 Ok(())
3144 }
3145
3146 #[test]
3147 fn range_inclusive_i32_constructs_a_range_inclusive() -> Result<()> {
3148 let lookup = OpLookup::new();
3149 let mut segment = DynSegment::new::<()>();
3150 segment.just(1i32);
3151 segment.just(5i32);
3152 lookup.lookup(
3153 "range_inclusive",
3154 &mut segment,
3155 2,
3156 Span::call_site(),
3157 Span::call_site(),
3158 )?;
3159 assert_eq!(
3160 segment.call0::<std::ops::RangeInclusive<i32>>()?,
3161 1i32..=5i32
3162 );
3163 Ok(())
3164 }
3165
3166 #[test]
3167 fn range_rejects_mismatched_operand_types() {
3168 let lookup = OpLookup::new();
3169 let mut segment = DynSegment::new::<()>();
3170 segment.just(1i32);
3171 segment.just(5.0f64);
3172 let result = lookup.lookup(
3173 "range",
3174 &mut segment,
3175 2,
3176 Span::call_site(),
3177 Span::call_site(),
3178 );
3179 assert!(result.is_err(), "expected a type-mismatch error, got Ok");
3180 }
3181
3182 #[test]
3183 fn range_from_i32_constructs_a_range_from() -> Result<()> {
3184 let lookup = OpLookup::new();
3185 let mut segment = DynSegment::new::<()>();
3186 segment.just(3i32);
3187 lookup.lookup(
3188 "range_from",
3189 &mut segment,
3190 1,
3191 Span::call_site(),
3192 Span::call_site(),
3193 )?;
3194 assert_eq!(segment.call0::<std::ops::RangeFrom<i32>>()?, 3i32..);
3195 Ok(())
3196 }
3197
3198 #[test]
3199 fn range_to_i32_constructs_a_range_to() -> Result<()> {
3200 let lookup = OpLookup::new();
3201 let mut segment = DynSegment::new::<()>();
3202 segment.just(7i32);
3203 lookup.lookup(
3204 "range_to",
3205 &mut segment,
3206 1,
3207 Span::call_site(),
3208 Span::call_site(),
3209 )?;
3210 assert_eq!(segment.call0::<std::ops::RangeTo<i32>>()?, ..7i32);
3211 Ok(())
3212 }
3213
3214 #[test]
3215 fn range_to_inclusive_i32_constructs_a_range_to_inclusive() -> Result<()> {
3216 let lookup = OpLookup::new();
3217 let mut segment = DynSegment::new::<()>();
3218 segment.just(7i32);
3219 lookup.lookup(
3220 "range_to_inclusive",
3221 &mut segment,
3222 1,
3223 Span::call_site(),
3224 Span::call_site(),
3225 )?;
3226 assert_eq!(segment.call0::<std::ops::RangeToInclusive<i32>>()?, ..=7i32);
3227 Ok(())
3228 }
3229
3230 #[test]
3231 fn range_full_constructs_a_range_full() -> Result<()> {
3232 let lookup = OpLookup::new();
3233 let mut segment = DynSegment::new::<()>();
3234 lookup.lookup(
3235 "range_full",
3236 &mut segment,
3237 0,
3238 Span::call_site(),
3239 Span::call_site(),
3240 )?;
3241 segment.call0::<std::ops::RangeFull>()?;
3242 Ok(())
3243 }
3244}