1use anyhow::{Result, anyhow};
31use cel_runtime::DynSegment;
32use once_cell::sync::Lazy;
33use phf::phf_map;
34use std::any::TypeId;
35
36use crate::SourceSpan;
37
38#[cfg(feature = "span-diagnostics")]
42#[inline]
43fn span_err(span: SourceSpan, e: anyhow::Error) -> anyhow::Error {
44 e.context(crate::SpanContext::new(span))
45}
46
47#[cfg(not(feature = "span-diagnostics"))]
48#[inline]
49fn span_err(_span: SourceSpan, e: anyhow::Error) -> anyhow::Error {
50 e
51}
52
53pub type OpFn = fn(&mut DynSegment, SourceSpan) -> Result<()>;
62
63pub struct TupleOpSignature {
86 pub name: String,
88 pub shape: Vec<TypeId>,
91 pub tuple_operand_index: usize,
93 pub operand_type_ids: Vec<TypeId>,
96 pub op_fn: OpFn,
98}
99
100pub type ScopeFn =
113 Box<dyn Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync>;
114
115#[derive(Clone, Copy)]
120struct OpSignature {
121 type_id_index: usize,
123 rhs_type_id_index: usize,
125 arity: u8,
127 op_fn: OpFn,
129}
130
131impl OpSignature {
132 fn lhs_type_id(&self) -> TypeId {
134 TYPE_IDS[self.type_id_index]
135 }
136
137 fn rhs_type_id(&self) -> TypeId {
139 TYPE_IDS[self.rhs_type_id_index]
140 }
141}
142
143static TYPE_IDS: Lazy<Vec<TypeId>> = Lazy::new(|| {
147 vec![
148 TypeId::of::<u8>(),
149 TypeId::of::<u16>(),
150 TypeId::of::<u32>(),
151 TypeId::of::<u64>(),
152 TypeId::of::<u128>(),
153 TypeId::of::<usize>(),
154 TypeId::of::<i8>(),
155 TypeId::of::<i16>(),
156 TypeId::of::<i32>(),
157 TypeId::of::<i64>(),
158 TypeId::of::<i128>(),
159 TypeId::of::<isize>(),
160 TypeId::of::<f32>(),
161 TypeId::of::<f64>(),
162 TypeId::of::<bool>(),
163 TypeId::of::<String>(),
164 ]
165});
166
167const TYPE_U8: usize = 0;
169const TYPE_U16: usize = 1;
170const TYPE_U32: usize = 2;
171const TYPE_U64: usize = 3;
172const TYPE_U128: usize = 4;
173const TYPE_USIZE: usize = 5;
174const TYPE_I8: usize = 6;
175const TYPE_I16: usize = 7;
176const TYPE_I32: usize = 8;
177const TYPE_I64: usize = 9;
178const TYPE_I128: usize = 10;
179const TYPE_ISIZE: usize = 11;
180const TYPE_F32: usize = 12;
181const TYPE_F64: usize = 13;
182const TYPE_BOOL: usize = 14;
183const TYPE_STR: usize = 15;
184
185macro_rules! sig {
188 ($type_idx:expr, $arity:expr, $closure:expr) => {
189 OpSignature {
190 type_id_index: $type_idx,
191 rhs_type_id_index: $type_idx,
192 arity: $arity,
193 op_fn: $closure,
194 }
195 };
196}
197
198macro_rules! sig_het {
199 ($lhs_idx:expr, $rhs_idx:expr, $closure:expr) => {
200 OpSignature {
201 type_id_index: $lhs_idx,
202 rhs_type_id_index: $rhs_idx,
203 arity: 2,
204 op_fn: $closure,
205 }
206 };
207}
208
209static ADD_SIGNATURES: &[OpSignature] = &[
211 sig!(TYPE_U8, 2, |seg, _span| seg
212 .op2(|a: u8, b: u8| a.wrapping_add(b))),
213 sig!(TYPE_U16, 2, |seg, _span| seg
214 .op2(|a: u16, b: u16| a.wrapping_add(b))),
215 sig!(TYPE_U32, 2, |seg, _span| seg
216 .op2(|a: u32, b: u32| a.wrapping_add(b))),
217 sig!(TYPE_U64, 2, |seg, _span| seg
218 .op2(|a: u64, b: u64| a.wrapping_add(b))),
219 sig!(TYPE_U128, 2, |seg, _span| seg
220 .op2(|a: u128, b: u128| a.wrapping_add(b))),
221 sig!(TYPE_USIZE, 2, |seg, _span| seg
222 .op2(|a: usize, b: usize| a.wrapping_add(b))),
223 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
224 .checked_add(b)
225 .ok_or_else(|| anyhow!("arithmetic overflow"))
226 .map_err(|e| span_err(span, e)))),
227 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
228 .checked_add(b)
229 .ok_or_else(|| anyhow!("arithmetic overflow"))
230 .map_err(|e| span_err(span, e)))),
231 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
232 .checked_add(b)
233 .ok_or_else(|| anyhow!("arithmetic overflow"))
234 .map_err(|e| span_err(span, e)))),
235 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
236 .checked_add(b)
237 .ok_or_else(|| anyhow!("arithmetic overflow"))
238 .map_err(|e| span_err(span, e)))),
239 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
240 move |a: i128, b: i128| a
241 .checked_add(b)
242 .ok_or_else(|| anyhow!("arithmetic overflow"))
243 .map_err(|e| span_err(span, e))
244 )),
245 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
246 move |a: isize, b: isize| a
247 .checked_add(b)
248 .ok_or_else(|| anyhow!("arithmetic overflow"))
249 .map_err(|e| span_err(span, e))
250 )),
251 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a + b)),
252 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a + b)),
253 sig!(TYPE_STR, 2, |seg, _span| seg
254 .op2(|a: String, b: String| a + &b)),
255];
256
257static SUB_SIGNATURES: &[OpSignature] = &[
259 sig!(TYPE_U8, 2, |seg, _span| seg
261 .op2(|a: u8, b: u8| a.wrapping_sub(b))),
262 sig!(TYPE_U16, 2, |seg, _span| seg
263 .op2(|a: u16, b: u16| a.wrapping_sub(b))),
264 sig!(TYPE_U32, 2, |seg, _span| seg
265 .op2(|a: u32, b: u32| a.wrapping_sub(b))),
266 sig!(TYPE_U64, 2, |seg, _span| seg
267 .op2(|a: u64, b: u64| a.wrapping_sub(b))),
268 sig!(TYPE_U128, 2, |seg, _span| seg
269 .op2(|a: u128, b: u128| a.wrapping_sub(b))),
270 sig!(TYPE_USIZE, 2, |seg, _span| seg
271 .op2(|a: usize, b: usize| a.wrapping_sub(b))),
272 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
273 .checked_sub(b)
274 .ok_or_else(|| anyhow!("arithmetic overflow"))
275 .map_err(|e| span_err(span, e)))),
276 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
277 .checked_sub(b)
278 .ok_or_else(|| anyhow!("arithmetic overflow"))
279 .map_err(|e| span_err(span, e)))),
280 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
281 .checked_sub(b)
282 .ok_or_else(|| anyhow!("arithmetic overflow"))
283 .map_err(|e| span_err(span, e)))),
284 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
285 .checked_sub(b)
286 .ok_or_else(|| anyhow!("arithmetic overflow"))
287 .map_err(|e| span_err(span, e)))),
288 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
289 move |a: i128, b: i128| a
290 .checked_sub(b)
291 .ok_or_else(|| anyhow!("arithmetic overflow"))
292 .map_err(|e| span_err(span, e))
293 )),
294 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
295 move |a: isize, b: isize| a
296 .checked_sub(b)
297 .ok_or_else(|| anyhow!("arithmetic overflow"))
298 .map_err(|e| span_err(span, e))
299 )),
300 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a - b)),
301 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a - b)),
302 sig!(TYPE_I8, 1, |seg, span| seg.op1r(move |a: i8| a
304 .checked_neg()
305 .ok_or_else(|| anyhow!("arithmetic overflow"))
306 .map_err(|e| span_err(span, e)))),
307 sig!(TYPE_I16, 1, |seg, span| seg.op1r(move |a: i16| a
308 .checked_neg()
309 .ok_or_else(|| anyhow!("arithmetic overflow"))
310 .map_err(|e| span_err(span, e)))),
311 sig!(TYPE_I32, 1, |seg, span| seg.op1r(move |a: i32| a
312 .checked_neg()
313 .ok_or_else(|| anyhow!("arithmetic overflow"))
314 .map_err(|e| span_err(span, e)))),
315 sig!(TYPE_I64, 1, |seg, span| seg.op1r(move |a: i64| a
316 .checked_neg()
317 .ok_or_else(|| anyhow!("arithmetic overflow"))
318 .map_err(|e| span_err(span, e)))),
319 sig!(TYPE_I128, 1, |seg, span| seg.op1r(move |a: i128| a
320 .checked_neg()
321 .ok_or_else(|| anyhow!("arithmetic overflow"))
322 .map_err(|e| span_err(span, e)))),
323 sig!(TYPE_ISIZE, 1, |seg, span| seg.op1r(move |a: isize| a
324 .checked_neg()
325 .ok_or_else(|| anyhow!("arithmetic overflow"))
326 .map_err(|e| span_err(span, e)))),
327 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| -a)),
328 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| -a)),
329];
330
331static MUL_SIGNATURES: &[OpSignature] = &[
333 sig!(TYPE_U8, 2, |seg, _span| seg
334 .op2(|a: u8, b: u8| a.wrapping_mul(b))),
335 sig!(TYPE_U16, 2, |seg, _span| seg
336 .op2(|a: u16, b: u16| a.wrapping_mul(b))),
337 sig!(TYPE_U32, 2, |seg, _span| seg
338 .op2(|a: u32, b: u32| a.wrapping_mul(b))),
339 sig!(TYPE_U64, 2, |seg, _span| seg
340 .op2(|a: u64, b: u64| a.wrapping_mul(b))),
341 sig!(TYPE_U128, 2, |seg, _span| seg
342 .op2(|a: u128, b: u128| a.wrapping_mul(b))),
343 sig!(TYPE_USIZE, 2, |seg, _span| seg
344 .op2(|a: usize, b: usize| a.wrapping_mul(b))),
345 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
346 .checked_mul(b)
347 .ok_or_else(|| anyhow!("arithmetic overflow"))
348 .map_err(|e| span_err(span, e)))),
349 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
350 .checked_mul(b)
351 .ok_or_else(|| anyhow!("arithmetic overflow"))
352 .map_err(|e| span_err(span, e)))),
353 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
354 .checked_mul(b)
355 .ok_or_else(|| anyhow!("arithmetic overflow"))
356 .map_err(|e| span_err(span, e)))),
357 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
358 .checked_mul(b)
359 .ok_or_else(|| anyhow!("arithmetic overflow"))
360 .map_err(|e| span_err(span, e)))),
361 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
362 move |a: i128, b: i128| a
363 .checked_mul(b)
364 .ok_or_else(|| anyhow!("arithmetic overflow"))
365 .map_err(|e| span_err(span, e))
366 )),
367 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
368 move |a: isize, b: isize| a
369 .checked_mul(b)
370 .ok_or_else(|| anyhow!("arithmetic overflow"))
371 .map_err(|e| span_err(span, e))
372 )),
373 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a * b)),
374 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a * b)),
375];
376
377static DIV_SIGNATURES: &[OpSignature] = &[
382 sig!(TYPE_U8, 2, |seg, span| seg.op2r(move |a: u8, b: u8| a
383 .checked_div(b)
384 .ok_or_else(|| anyhow!("division by zero"))
385 .map_err(|e| span_err(span, e)))),
386 sig!(TYPE_U16, 2, |seg, span| seg.op2r(move |a: u16, b: u16| a
387 .checked_div(b)
388 .ok_or_else(|| anyhow!("division by zero"))
389 .map_err(|e| span_err(span, e)))),
390 sig!(TYPE_U32, 2, |seg, span| seg.op2r(move |a: u32, b: u32| a
391 .checked_div(b)
392 .ok_or_else(|| anyhow!("division by zero"))
393 .map_err(|e| span_err(span, e)))),
394 sig!(TYPE_U64, 2, |seg, span| seg.op2r(move |a: u64, b: u64| a
395 .checked_div(b)
396 .ok_or_else(|| anyhow!("division by zero"))
397 .map_err(|e| span_err(span, e)))),
398 sig!(TYPE_U128, 2, |seg, span| seg.op2r(
399 move |a: u128, b: u128| a
400 .checked_div(b)
401 .ok_or_else(|| anyhow!("division by zero"))
402 .map_err(|e| span_err(span, e))
403 )),
404 sig!(TYPE_USIZE, 2, |seg, span| seg.op2r(
405 move |a: usize, b: usize| a
406 .checked_div(b)
407 .ok_or_else(|| anyhow!("division by zero"))
408 .map_err(|e| span_err(span, e))
409 )),
410 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
411 .checked_div(b)
412 .ok_or_else(|| anyhow!("division by zero"))
413 .map_err(|e| span_err(span, e)))),
414 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
415 .checked_div(b)
416 .ok_or_else(|| anyhow!("division by zero"))
417 .map_err(|e| span_err(span, e)))),
418 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
419 .checked_div(b)
420 .ok_or_else(|| anyhow!("division by zero"))
421 .map_err(|e| span_err(span, e)))),
422 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
423 .checked_div(b)
424 .ok_or_else(|| anyhow!("division by zero"))
425 .map_err(|e| span_err(span, e)))),
426 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
427 move |a: i128, b: i128| a
428 .checked_div(b)
429 .ok_or_else(|| anyhow!("division by zero"))
430 .map_err(|e| span_err(span, e))
431 )),
432 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
433 move |a: isize, b: isize| a
434 .checked_div(b)
435 .ok_or_else(|| anyhow!("division by zero"))
436 .map_err(|e| span_err(span, e))
437 )),
438 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a / b)),
439 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a / b)),
440];
441
442static MOD_SIGNATURES: &[OpSignature] = &[
447 sig!(TYPE_U8, 2, |seg, span| seg.op2r(move |a: u8, b: u8| a
448 .checked_rem(b)
449 .ok_or_else(|| anyhow!("division by zero"))
450 .map_err(|e| span_err(span, e)))),
451 sig!(TYPE_U16, 2, |seg, span| seg.op2r(move |a: u16, b: u16| a
452 .checked_rem(b)
453 .ok_or_else(|| anyhow!("division by zero"))
454 .map_err(|e| span_err(span, e)))),
455 sig!(TYPE_U32, 2, |seg, span| seg.op2r(move |a: u32, b: u32| a
456 .checked_rem(b)
457 .ok_or_else(|| anyhow!("division by zero"))
458 .map_err(|e| span_err(span, e)))),
459 sig!(TYPE_U64, 2, |seg, span| seg.op2r(move |a: u64, b: u64| a
460 .checked_rem(b)
461 .ok_or_else(|| anyhow!("division by zero"))
462 .map_err(|e| span_err(span, e)))),
463 sig!(TYPE_U128, 2, |seg, span| seg.op2r(
464 move |a: u128, b: u128| a
465 .checked_rem(b)
466 .ok_or_else(|| anyhow!("division by zero"))
467 .map_err(|e| span_err(span, e))
468 )),
469 sig!(TYPE_USIZE, 2, |seg, span| seg.op2r(
470 move |a: usize, b: usize| a
471 .checked_rem(b)
472 .ok_or_else(|| anyhow!("division by zero"))
473 .map_err(|e| span_err(span, e))
474 )),
475 sig!(TYPE_I8, 2, |seg, span| seg.op2r(move |a: i8, b: i8| a
476 .checked_rem(b)
477 .ok_or_else(|| anyhow!("division by zero"))
478 .map_err(|e| span_err(span, e)))),
479 sig!(TYPE_I16, 2, |seg, span| seg.op2r(move |a: i16, b: i16| a
480 .checked_rem(b)
481 .ok_or_else(|| anyhow!("division by zero"))
482 .map_err(|e| span_err(span, e)))),
483 sig!(TYPE_I32, 2, |seg, span| seg.op2r(move |a: i32, b: i32| a
484 .checked_rem(b)
485 .ok_or_else(|| anyhow!("division by zero"))
486 .map_err(|e| span_err(span, e)))),
487 sig!(TYPE_I64, 2, |seg, span| seg.op2r(move |a: i64, b: i64| a
488 .checked_rem(b)
489 .ok_or_else(|| anyhow!("division by zero"))
490 .map_err(|e| span_err(span, e)))),
491 sig!(TYPE_I128, 2, |seg, span| seg.op2r(
492 move |a: i128, b: i128| a
493 .checked_rem(b)
494 .ok_or_else(|| anyhow!("division by zero"))
495 .map_err(|e| span_err(span, e))
496 )),
497 sig!(TYPE_ISIZE, 2, |seg, span| seg.op2r(
498 move |a: isize, b: isize| a
499 .checked_rem(b)
500 .ok_or_else(|| anyhow!("division by zero"))
501 .map_err(|e| span_err(span, e))
502 )),
503 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a % b)),
504 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a % b)),
505];
506
507static BITWISE_AND_SIGNATURES: &[OpSignature] = &[
509 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a & b)),
510 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a & b)),
511 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a & b)),
512 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a & b)),
513 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a & b)),
514 sig!(TYPE_USIZE, 2, |seg, _span| seg
515 .op2(|a: usize, b: usize| a & b)),
516 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a & b)),
517 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a & b)),
518 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a & b)),
519 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a & b)),
520 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a & b)),
521 sig!(TYPE_ISIZE, 2, |seg, _span| seg
522 .op2(|a: isize, b: isize| a & b)),
523];
524
525static BITWISE_OR_SIGNATURES: &[OpSignature] = &[
527 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a | b)),
528 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a | b)),
529 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a | b)),
530 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a | b)),
531 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a | b)),
532 sig!(TYPE_USIZE, 2, |seg, _span| seg
533 .op2(|a: usize, b: usize| a | b)),
534 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a | b)),
535 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a | b)),
536 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a | b)),
537 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a | b)),
538 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a | b)),
539 sig!(TYPE_ISIZE, 2, |seg, _span| seg
540 .op2(|a: isize, b: isize| a | b)),
541];
542
543static BITWISE_XOR_SIGNATURES: &[OpSignature] = &[
545 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a ^ b)),
546 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a ^ b)),
547 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a ^ b)),
548 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a ^ b)),
549 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a ^ b)),
550 sig!(TYPE_USIZE, 2, |seg, _span| seg
551 .op2(|a: usize, b: usize| a ^ b)),
552 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a ^ b)),
553 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a ^ b)),
554 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a ^ b)),
555 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a ^ b)),
556 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a ^ b)),
557 sig!(TYPE_ISIZE, 2, |seg, _span| seg
558 .op2(|a: isize, b: isize| a ^ b)),
559];
560
561macro_rules! shl_push {
573 ($v:ident, $lhs_idx:expr, $lhs_ty:ty) => {
574 $v.push(sig_het!($lhs_idx, TYPE_U8, |seg, span| seg.op2r(
575 move |a: $lhs_ty, b: u8| a
576 .checked_shl(u32::from(b))
577 .ok_or_else(|| anyhow!("shift overflow"))
578 .map_err(|e| span_err(span, e))
579 )));
580 $v.push(sig_het!($lhs_idx, TYPE_U16, |seg, span| seg.op2r(
581 move |a: $lhs_ty, b: u16| a
582 .checked_shl(u32::from(b))
583 .ok_or_else(|| anyhow!("shift overflow"))
584 .map_err(|e| span_err(span, e))
585 )));
586 $v.push(sig_het!($lhs_idx, TYPE_U32, |seg, span| seg.op2r(
587 move |a: $lhs_ty, b: u32| a
588 .checked_shl(b)
589 .ok_or_else(|| anyhow!("shift overflow"))
590 .map_err(|e| span_err(span, e))
591 )));
592 $v.push(sig_het!($lhs_idx, TYPE_U64, |seg, span| seg.op2r(
593 move |a: $lhs_ty, b: u64| u32::try_from(b)
594 .ok()
595 .and_then(|r| a.checked_shl(r))
596 .ok_or_else(|| anyhow!("shift overflow"))
597 .map_err(|e| span_err(span, e))
598 )));
599 $v.push(sig_het!($lhs_idx, TYPE_U128, |seg, span| seg.op2r(
600 move |a: $lhs_ty, b: u128| u32::try_from(b)
601 .ok()
602 .and_then(|r| a.checked_shl(r))
603 .ok_or_else(|| anyhow!("shift overflow"))
604 .map_err(|e| span_err(span, e))
605 )));
606 $v.push(sig_het!($lhs_idx, TYPE_USIZE, |seg, span| seg.op2r(
607 move |a: $lhs_ty, b: usize| u32::try_from(b)
608 .ok()
609 .and_then(|r| a.checked_shl(r))
610 .ok_or_else(|| anyhow!("shift overflow"))
611 .map_err(|e| span_err(span, e))
612 )));
613 $v.push(sig_het!($lhs_idx, TYPE_I8, |seg, span| seg.op2r(
614 move |a: $lhs_ty, b: i8| u32::try_from(b)
615 .ok()
616 .and_then(|r| a.checked_shl(r))
617 .ok_or_else(|| anyhow!("shift overflow"))
618 .map_err(|e| span_err(span, e))
619 )));
620 $v.push(sig_het!($lhs_idx, TYPE_I16, |seg, span| seg.op2r(
621 move |a: $lhs_ty, b: i16| u32::try_from(b)
622 .ok()
623 .and_then(|r| a.checked_shl(r))
624 .ok_or_else(|| anyhow!("shift overflow"))
625 .map_err(|e| span_err(span, e))
626 )));
627 $v.push(sig_het!($lhs_idx, TYPE_I32, |seg, span| seg.op2r(
628 move |a: $lhs_ty, b: i32| u32::try_from(b)
629 .ok()
630 .and_then(|r| a.checked_shl(r))
631 .ok_or_else(|| anyhow!("shift overflow"))
632 .map_err(|e| span_err(span, e))
633 )));
634 $v.push(sig_het!($lhs_idx, TYPE_I64, |seg, span| seg.op2r(
635 move |a: $lhs_ty, b: i64| u32::try_from(b)
636 .ok()
637 .and_then(|r| a.checked_shl(r))
638 .ok_or_else(|| anyhow!("shift overflow"))
639 .map_err(|e| span_err(span, e))
640 )));
641 $v.push(sig_het!($lhs_idx, TYPE_I128, |seg, span| seg.op2r(
642 move |a: $lhs_ty, b: i128| u32::try_from(b)
643 .ok()
644 .and_then(|r| a.checked_shl(r))
645 .ok_or_else(|| anyhow!("shift overflow"))
646 .map_err(|e| span_err(span, e))
647 )));
648 $v.push(sig_het!($lhs_idx, TYPE_ISIZE, |seg, span| seg.op2r(
649 move |a: $lhs_ty, b: isize| u32::try_from(b)
650 .ok()
651 .and_then(|r| a.checked_shl(r))
652 .ok_or_else(|| anyhow!("shift overflow"))
653 .map_err(|e| span_err(span, e))
654 )));
655 };
656}
657
658macro_rules! shr_push {
659 ($v:ident, $lhs_idx:expr, $lhs_ty:ty) => {
660 $v.push(sig_het!($lhs_idx, TYPE_U8, |seg, span| seg.op2r(
661 move |a: $lhs_ty, b: u8| a
662 .checked_shr(u32::from(b))
663 .ok_or_else(|| anyhow!("shift overflow"))
664 .map_err(|e| span_err(span, e))
665 )));
666 $v.push(sig_het!($lhs_idx, TYPE_U16, |seg, span| seg.op2r(
667 move |a: $lhs_ty, b: u16| a
668 .checked_shr(u32::from(b))
669 .ok_or_else(|| anyhow!("shift overflow"))
670 .map_err(|e| span_err(span, e))
671 )));
672 $v.push(sig_het!($lhs_idx, TYPE_U32, |seg, span| seg.op2r(
673 move |a: $lhs_ty, b: u32| a
674 .checked_shr(b)
675 .ok_or_else(|| anyhow!("shift overflow"))
676 .map_err(|e| span_err(span, e))
677 )));
678 $v.push(sig_het!($lhs_idx, TYPE_U64, |seg, span| seg.op2r(
679 move |a: $lhs_ty, b: u64| u32::try_from(b)
680 .ok()
681 .and_then(|r| a.checked_shr(r))
682 .ok_or_else(|| anyhow!("shift overflow"))
683 .map_err(|e| span_err(span, e))
684 )));
685 $v.push(sig_het!($lhs_idx, TYPE_U128, |seg, span| seg.op2r(
686 move |a: $lhs_ty, b: u128| u32::try_from(b)
687 .ok()
688 .and_then(|r| a.checked_shr(r))
689 .ok_or_else(|| anyhow!("shift overflow"))
690 .map_err(|e| span_err(span, e))
691 )));
692 $v.push(sig_het!($lhs_idx, TYPE_USIZE, |seg, span| seg.op2r(
693 move |a: $lhs_ty, b: usize| u32::try_from(b)
694 .ok()
695 .and_then(|r| a.checked_shr(r))
696 .ok_or_else(|| anyhow!("shift overflow"))
697 .map_err(|e| span_err(span, e))
698 )));
699 $v.push(sig_het!($lhs_idx, TYPE_I8, |seg, span| seg.op2r(
700 move |a: $lhs_ty, b: i8| u32::try_from(b)
701 .ok()
702 .and_then(|r| a.checked_shr(r))
703 .ok_or_else(|| anyhow!("shift overflow"))
704 .map_err(|e| span_err(span, e))
705 )));
706 $v.push(sig_het!($lhs_idx, TYPE_I16, |seg, span| seg.op2r(
707 move |a: $lhs_ty, b: i16| u32::try_from(b)
708 .ok()
709 .and_then(|r| a.checked_shr(r))
710 .ok_or_else(|| anyhow!("shift overflow"))
711 .map_err(|e| span_err(span, e))
712 )));
713 $v.push(sig_het!($lhs_idx, TYPE_I32, |seg, span| seg.op2r(
714 move |a: $lhs_ty, b: i32| u32::try_from(b)
715 .ok()
716 .and_then(|r| a.checked_shr(r))
717 .ok_or_else(|| anyhow!("shift overflow"))
718 .map_err(|e| span_err(span, e))
719 )));
720 $v.push(sig_het!($lhs_idx, TYPE_I64, |seg, span| seg.op2r(
721 move |a: $lhs_ty, b: i64| u32::try_from(b)
722 .ok()
723 .and_then(|r| a.checked_shr(r))
724 .ok_or_else(|| anyhow!("shift overflow"))
725 .map_err(|e| span_err(span, e))
726 )));
727 $v.push(sig_het!($lhs_idx, TYPE_I128, |seg, span| seg.op2r(
728 move |a: $lhs_ty, b: i128| u32::try_from(b)
729 .ok()
730 .and_then(|r| a.checked_shr(r))
731 .ok_or_else(|| anyhow!("shift overflow"))
732 .map_err(|e| span_err(span, e))
733 )));
734 $v.push(sig_het!($lhs_idx, TYPE_ISIZE, |seg, span| seg.op2r(
735 move |a: $lhs_ty, b: isize| u32::try_from(b)
736 .ok()
737 .and_then(|r| a.checked_shr(r))
738 .ok_or_else(|| anyhow!("shift overflow"))
739 .map_err(|e| span_err(span, e))
740 )));
741 };
742}
743
744static LEFT_SHIFT_SIGNATURES: Lazy<Vec<OpSignature>> = Lazy::new(|| {
747 let mut v = Vec::with_capacity(144);
748 shl_push!(v, TYPE_U8, u8);
749 shl_push!(v, TYPE_U16, u16);
750 shl_push!(v, TYPE_U32, u32);
751 shl_push!(v, TYPE_U64, u64);
752 shl_push!(v, TYPE_U128, u128);
753 shl_push!(v, TYPE_USIZE, usize);
754 shl_push!(v, TYPE_I8, i8);
755 shl_push!(v, TYPE_I16, i16);
756 shl_push!(v, TYPE_I32, i32);
757 shl_push!(v, TYPE_I64, i64);
758 shl_push!(v, TYPE_I128, i128);
759 shl_push!(v, TYPE_ISIZE, isize);
760 v
761});
762
763static RIGHT_SHIFT_SIGNATURES: Lazy<Vec<OpSignature>> = Lazy::new(|| {
765 let mut v = Vec::with_capacity(144);
766 shr_push!(v, TYPE_U8, u8);
767 shr_push!(v, TYPE_U16, u16);
768 shr_push!(v, TYPE_U32, u32);
769 shr_push!(v, TYPE_U64, u64);
770 shr_push!(v, TYPE_U128, u128);
771 shr_push!(v, TYPE_USIZE, usize);
772 shr_push!(v, TYPE_I8, i8);
773 shr_push!(v, TYPE_I16, i16);
774 shr_push!(v, TYPE_I32, i32);
775 shr_push!(v, TYPE_I64, i64);
776 shr_push!(v, TYPE_I128, i128);
777 shr_push!(v, TYPE_ISIZE, isize);
778 v
779});
780
781static LOGICAL_NOT_SIGNATURES: &[OpSignature] =
783 &[sig!(TYPE_BOOL, 1, |seg, _span| seg.op1(|a: bool| !a))];
784
785static EQUAL_SIGNATURES: &[OpSignature] = &[
787 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a == b)),
788 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a == b)),
789 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a == b)),
790 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a == b)),
791 sig!(TYPE_U128, 2, |seg, _span| seg
792 .op2(|a: u128, b: u128| a == b)),
793 sig!(TYPE_USIZE, 2, |seg, _span| seg
794 .op2(|a: usize, b: usize| a == b)),
795 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a == b)),
796 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a == b)),
797 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a == b)),
798 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a == b)),
799 sig!(TYPE_I128, 2, |seg, _span| seg
800 .op2(|a: i128, b: i128| a == b)),
801 sig!(TYPE_ISIZE, 2, |seg, _span| seg
802 .op2(|a: isize, b: isize| a == b)),
803 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a == b)),
804 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a == b)),
805 sig!(TYPE_BOOL, 2, |seg, _span| seg
806 .op2(|a: bool, b: bool| a == b)),
807 sig!(TYPE_STR, 2, |seg, _span| seg
808 .op2(|a: String, b: String| a == b)),
809];
810
811static NOT_EQUAL_SIGNATURES: &[OpSignature] = &[
813 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a != b)),
814 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a != b)),
815 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a != b)),
816 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a != b)),
817 sig!(TYPE_U128, 2, |seg, _span| seg
818 .op2(|a: u128, b: u128| a != b)),
819 sig!(TYPE_USIZE, 2, |seg, _span| seg
820 .op2(|a: usize, b: usize| a != b)),
821 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a != b)),
822 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a != b)),
823 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a != b)),
824 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a != b)),
825 sig!(TYPE_I128, 2, |seg, _span| seg
826 .op2(|a: i128, b: i128| a != b)),
827 sig!(TYPE_ISIZE, 2, |seg, _span| seg
828 .op2(|a: isize, b: isize| a != b)),
829 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a != b)),
830 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a != b)),
831 sig!(TYPE_BOOL, 2, |seg, _span| seg
832 .op2(|a: bool, b: bool| a != b)),
833 sig!(TYPE_STR, 2, |seg, _span| seg
834 .op2(|a: String, b: String| a != b)),
835];
836
837static LESS_THAN_SIGNATURES: &[OpSignature] = &[
839 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a < b)),
840 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a < b)),
841 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a < b)),
842 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a < b)),
843 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a < b)),
844 sig!(TYPE_USIZE, 2, |seg, _span| seg
845 .op2(|a: usize, b: usize| a < b)),
846 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a < b)),
847 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a < b)),
848 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a < b)),
849 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a < b)),
850 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a < b)),
851 sig!(TYPE_ISIZE, 2, |seg, _span| seg
852 .op2(|a: isize, b: isize| a < b)),
853 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a < b)),
854 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a < b)),
855 sig!(TYPE_STR, 2, |seg, _span| seg
856 .op2(|a: String, b: String| a < b)),
857];
858
859static LESS_THAN_OR_EQUAL_SIGNATURES: &[OpSignature] = &[
861 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a <= b)),
862 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a <= b)),
863 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a <= b)),
864 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a <= b)),
865 sig!(TYPE_U128, 2, |seg, _span| seg
866 .op2(|a: u128, b: u128| a <= b)),
867 sig!(TYPE_USIZE, 2, |seg, _span| seg
868 .op2(|a: usize, b: usize| a <= b)),
869 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a <= b)),
870 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a <= b)),
871 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a <= b)),
872 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a <= b)),
873 sig!(TYPE_I128, 2, |seg, _span| seg
874 .op2(|a: i128, b: i128| a <= b)),
875 sig!(TYPE_ISIZE, 2, |seg, _span| seg
876 .op2(|a: isize, b: isize| a <= b)),
877 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a <= b)),
878 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a <= b)),
879 sig!(TYPE_STR, 2, |seg, _span| seg
880 .op2(|a: String, b: String| a <= b)),
881];
882
883static GREATER_THAN_SIGNATURES: &[OpSignature] = &[
885 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a > b)),
886 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a > b)),
887 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a > b)),
888 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a > b)),
889 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a > b)),
890 sig!(TYPE_USIZE, 2, |seg, _span| seg
891 .op2(|a: usize, b: usize| a > b)),
892 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a > b)),
893 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a > b)),
894 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a > b)),
895 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a > b)),
896 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a > b)),
897 sig!(TYPE_ISIZE, 2, |seg, _span| seg
898 .op2(|a: isize, b: isize| a > b)),
899 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a > b)),
900 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a > b)),
901 sig!(TYPE_STR, 2, |seg, _span| seg
902 .op2(|a: String, b: String| a > b)),
903];
904
905static GREATER_THAN_OR_EQUAL_SIGNATURES: &[OpSignature] = &[
907 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a >= b)),
908 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a >= b)),
909 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a >= b)),
910 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a >= b)),
911 sig!(TYPE_U128, 2, |seg, _span| seg
912 .op2(|a: u128, b: u128| a >= b)),
913 sig!(TYPE_USIZE, 2, |seg, _span| seg
914 .op2(|a: usize, b: usize| a >= b)),
915 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a >= b)),
916 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a >= b)),
917 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a >= b)),
918 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a >= b)),
919 sig!(TYPE_I128, 2, |seg, _span| seg
920 .op2(|a: i128, b: i128| a >= b)),
921 sig!(TYPE_ISIZE, 2, |seg, _span| seg
922 .op2(|a: isize, b: isize| a >= b)),
923 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a >= b)),
924 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a >= b)),
925 sig!(TYPE_STR, 2, |seg, _span| seg
926 .op2(|a: String, b: String| a >= b)),
927];
928
929static RANGE_SIGNATURES: &[OpSignature] = &[
933 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a..b)),
934 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a..b)),
935 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a..b)),
936 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a..b)),
937 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a..b)),
938 sig!(TYPE_USIZE, 2, |seg, _span| seg
939 .op2(|a: usize, b: usize| a..b)),
940 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a..b)),
941 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a..b)),
942 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a..b)),
943 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a..b)),
944 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a..b)),
945 sig!(TYPE_ISIZE, 2, |seg, _span| seg
946 .op2(|a: isize, b: isize| a..b)),
947 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a..b)),
948 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a..b)),
949];
950
951static RANGE_INCLUSIVE_SIGNATURES: &[OpSignature] = &[
952 sig!(TYPE_U8, 2, |seg, _span| seg.op2(|a: u8, b: u8| a..=b)),
953 sig!(TYPE_U16, 2, |seg, _span| seg.op2(|a: u16, b: u16| a..=b)),
954 sig!(TYPE_U32, 2, |seg, _span| seg.op2(|a: u32, b: u32| a..=b)),
955 sig!(TYPE_U64, 2, |seg, _span| seg.op2(|a: u64, b: u64| a..=b)),
956 sig!(TYPE_U128, 2, |seg, _span| seg.op2(|a: u128, b: u128| a..=b)),
957 sig!(TYPE_USIZE, 2, |seg, _span| seg
958 .op2(|a: usize, b: usize| a..=b)),
959 sig!(TYPE_I8, 2, |seg, _span| seg.op2(|a: i8, b: i8| a..=b)),
960 sig!(TYPE_I16, 2, |seg, _span| seg.op2(|a: i16, b: i16| a..=b)),
961 sig!(TYPE_I32, 2, |seg, _span| seg.op2(|a: i32, b: i32| a..=b)),
962 sig!(TYPE_I64, 2, |seg, _span| seg.op2(|a: i64, b: i64| a..=b)),
963 sig!(TYPE_I128, 2, |seg, _span| seg.op2(|a: i128, b: i128| a..=b)),
964 sig!(TYPE_ISIZE, 2, |seg, _span| seg
965 .op2(|a: isize, b: isize| a..=b)),
966 sig!(TYPE_F32, 2, |seg, _span| seg.op2(|a: f32, b: f32| a..=b)),
967 sig!(TYPE_F64, 2, |seg, _span| seg.op2(|a: f64, b: f64| a..=b)),
968];
969
970static RANGE_FROM_SIGNATURES: &[OpSignature] = &[
971 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| a..)),
972 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| a..)),
973 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| a..)),
974 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| a..)),
975 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| a..)),
976 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| a..)),
977 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| a..)),
978 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| a..)),
979 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| a..)),
980 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| a..)),
981 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| a..)),
982 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| a..)),
983 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| a..)),
984 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| a..)),
985];
986
987static RANGE_TO_SIGNATURES: &[OpSignature] = &[
988 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| ..a)),
989 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| ..a)),
990 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| ..a)),
991 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| ..a)),
992 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| ..a)),
993 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| ..a)),
994 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| ..a)),
995 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| ..a)),
996 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| ..a)),
997 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| ..a)),
998 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| ..a)),
999 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| ..a)),
1000 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| ..a)),
1001 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| ..a)),
1002];
1003
1004static RANGE_TO_INCLUSIVE_SIGNATURES: &[OpSignature] = &[
1005 sig!(TYPE_U8, 1, |seg, _span| seg.op1(|a: u8| ..=a)),
1006 sig!(TYPE_U16, 1, |seg, _span| seg.op1(|a: u16| ..=a)),
1007 sig!(TYPE_U32, 1, |seg, _span| seg.op1(|a: u32| ..=a)),
1008 sig!(TYPE_U64, 1, |seg, _span| seg.op1(|a: u64| ..=a)),
1009 sig!(TYPE_U128, 1, |seg, _span| seg.op1(|a: u128| ..=a)),
1010 sig!(TYPE_USIZE, 1, |seg, _span| seg.op1(|a: usize| ..=a)),
1011 sig!(TYPE_I8, 1, |seg, _span| seg.op1(|a: i8| ..=a)),
1012 sig!(TYPE_I16, 1, |seg, _span| seg.op1(|a: i16| ..=a)),
1013 sig!(TYPE_I32, 1, |seg, _span| seg.op1(|a: i32| ..=a)),
1014 sig!(TYPE_I64, 1, |seg, _span| seg.op1(|a: i64| ..=a)),
1015 sig!(TYPE_I128, 1, |seg, _span| seg.op1(|a: i128| ..=a)),
1016 sig!(TYPE_ISIZE, 1, |seg, _span| seg.op1(|a: isize| ..=a)),
1017 sig!(TYPE_F32, 1, |seg, _span| seg.op1(|a: f32| ..=a)),
1018 sig!(TYPE_F64, 1, |seg, _span| seg.op1(|a: f64| ..=a)),
1019];
1020
1021static BUILTINS: phf::Map<&'static str, &'static [OpSignature]> = phf_map! {
1025 "+" => ADD_SIGNATURES,
1026 "-" => SUB_SIGNATURES,
1027 "*" => MUL_SIGNATURES,
1028 "/" => DIV_SIGNATURES,
1029 "%" => MOD_SIGNATURES,
1030 "&" => BITWISE_AND_SIGNATURES,
1031 "|" => BITWISE_OR_SIGNATURES,
1032 "^" => BITWISE_XOR_SIGNATURES,
1033 "!" => LOGICAL_NOT_SIGNATURES,
1034 "==" => EQUAL_SIGNATURES,
1035 "!=" => NOT_EQUAL_SIGNATURES,
1036 "<" => LESS_THAN_SIGNATURES,
1037 "<=" => LESS_THAN_OR_EQUAL_SIGNATURES,
1038 ">" => GREATER_THAN_SIGNATURES,
1039 ">=" => GREATER_THAN_OR_EQUAL_SIGNATURES,
1040 "range" => RANGE_SIGNATURES,
1041 "range_inclusive" => RANGE_INCLUSIVE_SIGNATURES,
1042 "range_from" => RANGE_FROM_SIGNATURES,
1043 "range_to" => RANGE_TO_SIGNATURES,
1044 "range_to_inclusive" => RANGE_TO_INCLUSIVE_SIGNATURES,
1045};
1046
1047#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1053pub struct OperandTypes {
1054 pub arity: u8,
1056 pub lhs: TypeId,
1058 pub rhs: TypeId,
1060}
1061
1062pub fn builtin_operand_types(name: &str) -> Vec<OperandTypes> {
1085 let Some(signatures) = signatures_for(name) else {
1086 return Vec::new();
1087 };
1088 signatures
1089 .iter()
1090 .map(|sig| OperandTypes {
1091 arity: sig.arity,
1092 lhs: sig.lhs_type_id(),
1093 rhs: sig.rhs_type_id(),
1094 })
1095 .collect()
1096}
1097
1098fn signatures_for(name: &str) -> Option<&'static [OpSignature]> {
1102 match name {
1103 "<<" => Some(&LEFT_SHIFT_SIGNATURES),
1104 ">>" => Some(&RIGHT_SHIFT_SIGNATURES),
1105 _ => BUILTINS.get(name).copied(),
1106 }
1107}
1108
1109#[derive(Clone, Copy)]
1150struct CastSignature {
1151 source_type_id_index: usize,
1153 op_fn: OpFn,
1155}
1156
1157impl CastSignature {
1158 fn source_type_id(&self) -> TypeId {
1160 TYPE_IDS[self.source_type_id_index]
1161 }
1162}
1163
1164macro_rules! try_from_cast_push {
1166 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1167 $v.push(CastSignature {
1168 source_type_id_index: $src_idx,
1169 op_fn: |seg, span| {
1170 seg.op1r(move |x: $src_ty| -> Result<$tgt_ty> {
1171 <$tgt_ty>::try_from(x).map_err(|_| {
1172 span_err(
1173 span,
1174 anyhow!(concat!("value does not fit in `", stringify!($tgt_ty), "`")),
1175 )
1176 })
1177 })
1178 },
1179 });
1180 };
1181}
1182
1183macro_rules! all_int_to_int_casts {
1185 ($v:ident, $tgt_ty:ty) => {
1186 try_from_cast_push!($v, TYPE_U8, u8, $tgt_ty);
1187 try_from_cast_push!($v, TYPE_U16, u16, $tgt_ty);
1188 try_from_cast_push!($v, TYPE_U32, u32, $tgt_ty);
1189 try_from_cast_push!($v, TYPE_U64, u64, $tgt_ty);
1190 try_from_cast_push!($v, TYPE_U128, u128, $tgt_ty);
1191 try_from_cast_push!($v, TYPE_USIZE, usize, $tgt_ty);
1192 try_from_cast_push!($v, TYPE_I8, i8, $tgt_ty);
1193 try_from_cast_push!($v, TYPE_I16, i16, $tgt_ty);
1194 try_from_cast_push!($v, TYPE_I32, i32, $tgt_ty);
1195 try_from_cast_push!($v, TYPE_I64, i64, $tgt_ty);
1196 try_from_cast_push!($v, TYPE_I128, i128, $tgt_ty);
1197 try_from_cast_push!($v, TYPE_ISIZE, isize, $tgt_ty);
1198 };
1199}
1200
1201macro_rules! as_cast_push {
1203 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1204 $v.push(CastSignature {
1205 source_type_id_index: $src_idx,
1206 op_fn: |seg, _span| seg.op1(|x: $src_ty| x as $tgt_ty),
1207 });
1208 };
1209}
1210
1211macro_rules! all_int_to_float_casts {
1213 ($v:ident, $tgt_ty:ty) => {
1214 as_cast_push!($v, TYPE_U8, u8, $tgt_ty);
1215 as_cast_push!($v, TYPE_U16, u16, $tgt_ty);
1216 as_cast_push!($v, TYPE_U32, u32, $tgt_ty);
1217 as_cast_push!($v, TYPE_U64, u64, $tgt_ty);
1218 as_cast_push!($v, TYPE_U128, u128, $tgt_ty);
1219 as_cast_push!($v, TYPE_USIZE, usize, $tgt_ty);
1220 as_cast_push!($v, TYPE_I8, i8, $tgt_ty);
1221 as_cast_push!($v, TYPE_I16, i16, $tgt_ty);
1222 as_cast_push!($v, TYPE_I32, i32, $tgt_ty);
1223 as_cast_push!($v, TYPE_I64, i64, $tgt_ty);
1224 as_cast_push!($v, TYPE_I128, i128, $tgt_ty);
1225 as_cast_push!($v, TYPE_ISIZE, isize, $tgt_ty);
1226 };
1227}
1228
1229macro_rules! float_to_int_cast_push {
1246 ($v:ident, $src_idx:expr, $src_ty:ty, $tgt_ty:ty) => {
1247 $v.push(CastSignature {
1248 source_type_id_index: $src_idx,
1249 op_fn: |seg, span| {
1250 seg.op1r(move |x: $src_ty| -> Result<$tgt_ty> {
1251 if !x.is_finite() {
1252 return Err(span_err(span, anyhow!("value is not finite")));
1253 }
1254 let is_unsigned = <$tgt_ty>::MIN == 0;
1255 let bits = <$tgt_ty>::BITS as i32;
1256 let exponent = if is_unsigned { bits } else { bits - 1 };
1257 let upper_exclusive: $src_ty = (2.0 as $src_ty).powi(exponent);
1258 if x < <$tgt_ty>::MIN as $src_ty || x >= upper_exclusive {
1259 return Err(span_err(
1260 span,
1261 anyhow!(concat!("value does not fit in `", stringify!($tgt_ty), "`")),
1262 ));
1263 }
1264 Ok(x as $tgt_ty)
1265 })
1266 },
1267 });
1268 };
1269}
1270
1271macro_rules! all_float_to_int_casts {
1273 ($v:ident, $tgt_ty:ty) => {
1274 float_to_int_cast_push!($v, TYPE_F32, f32, $tgt_ty);
1275 float_to_int_cast_push!($v, TYPE_F64, f64, $tgt_ty);
1276 };
1277}
1278
1279macro_rules! int_cast_sources {
1280 ($name:ident, $tgt_ty:ty) => {
1281 static $name: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1282 let mut v = Vec::with_capacity(15);
1283 all_int_to_int_casts!(v, $tgt_ty);
1284 all_float_to_int_casts!(v, $tgt_ty);
1285 as_cast_push!(v, TYPE_BOOL, bool, $tgt_ty);
1286 v
1287 });
1288 };
1289}
1290
1291int_cast_sources!(U8_CAST_SOURCES, u8);
1292int_cast_sources!(U16_CAST_SOURCES, u16);
1293int_cast_sources!(U32_CAST_SOURCES, u32);
1294int_cast_sources!(U64_CAST_SOURCES, u64);
1295int_cast_sources!(U128_CAST_SOURCES, u128);
1296int_cast_sources!(USIZE_CAST_SOURCES, usize);
1297int_cast_sources!(I8_CAST_SOURCES, i8);
1298int_cast_sources!(I16_CAST_SOURCES, i16);
1299int_cast_sources!(I32_CAST_SOURCES, i32);
1300int_cast_sources!(I64_CAST_SOURCES, i64);
1301int_cast_sources!(I128_CAST_SOURCES, i128);
1302int_cast_sources!(ISIZE_CAST_SOURCES, isize);
1303
1304static F32_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1305 let mut v = vec![
1306 CastSignature {
1312 source_type_id_index: TYPE_F64,
1313 op_fn: |seg, span| {
1314 seg.op1r(move |x: f64| -> Result<f32> {
1315 if !x.is_finite() {
1316 return Err(span_err(span, anyhow!("value is not finite")));
1317 }
1318 if x.abs() > f32::MAX as f64 {
1319 return Err(span_err(span, anyhow!("value does not fit in `f32`")));
1320 }
1321 Ok(x as f32)
1322 })
1323 },
1324 },
1325 CastSignature {
1326 source_type_id_index: TYPE_F32,
1327 op_fn: |seg, _span| seg.op1(|x: f32| x),
1328 },
1329 ];
1330 all_int_to_float_casts!(v, f32);
1331 v
1332});
1333
1334static F64_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1335 let mut v = vec![
1336 CastSignature {
1338 source_type_id_index: TYPE_F32,
1339 op_fn: |seg, _span| seg.op1(|x: f32| x as f64),
1340 },
1341 CastSignature {
1342 source_type_id_index: TYPE_F64,
1343 op_fn: |seg, _span| seg.op1(|x: f64| x),
1344 },
1345 ];
1346 all_int_to_float_casts!(v, f64);
1347 v
1348});
1349
1350static BOOL_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1354 vec![CastSignature {
1355 source_type_id_index: TYPE_BOOL,
1356 op_fn: |seg, _span| seg.op1(|x: bool| x),
1357 }]
1358});
1359
1360static STRING_CAST_SOURCES: Lazy<Vec<CastSignature>> = Lazy::new(|| {
1364 vec![CastSignature {
1365 source_type_id_index: TYPE_STR,
1366 op_fn: |seg, _span| seg.op1(|x: String| x),
1367 }]
1368});
1369
1370fn signatures_for_cast(target_name: &str) -> Option<&'static [CastSignature]> {
1376 match target_name {
1377 "u8" => Some(&U8_CAST_SOURCES),
1378 "u16" => Some(&U16_CAST_SOURCES),
1379 "u32" => Some(&U32_CAST_SOURCES),
1380 "u64" => Some(&U64_CAST_SOURCES),
1381 "u128" => Some(&U128_CAST_SOURCES),
1382 "usize" => Some(&USIZE_CAST_SOURCES),
1383 "i8" => Some(&I8_CAST_SOURCES),
1384 "i16" => Some(&I16_CAST_SOURCES),
1385 "i32" => Some(&I32_CAST_SOURCES),
1386 "i64" => Some(&I64_CAST_SOURCES),
1387 "i128" => Some(&I128_CAST_SOURCES),
1388 "isize" => Some(&ISIZE_CAST_SOURCES),
1389 "f32" => Some(&F32_CAST_SOURCES),
1390 "f64" => Some(&F64_CAST_SOURCES),
1391 "bool" => Some(&BOOL_CAST_SOURCES),
1392 "String" => Some(&STRING_CAST_SOURCES),
1393 _ => None,
1394 }
1395}
1396
1397pub fn cast_source_types(target_name: &str) -> impl Iterator<Item = TypeId> {
1414 signatures_for_cast(target_name)
1415 .into_iter()
1416 .flat_map(|sigs| sigs.iter().map(CastSignature::source_type_id))
1417}
1418
1419pub(crate) struct BuiltinScalarType {
1424 pub(crate) type_name: &'static str,
1425 pub(crate) value_type: fn() -> cel_runtime::value_type::ValueType,
1427}
1428
1429fn complete_builtin_leaf<T: Clone + PartialEq + std::fmt::Debug + 'static>()
1431-> cel_runtime::value_type::ValueType {
1432 use cel_runtime::value_type::{Leaf, ValueType};
1433 ValueType::cloneable_leaf::<T>()
1434 .with_equality::<Leaf<T>>()
1435 .expect("same concrete leaf")
1436 .with_debug::<Leaf<T>>()
1437 .expect("same concrete leaf")
1438}
1439
1440macro_rules! builtin_scalars {
1444 ($($name:literal => $ty:ty),* $(,)?) => {
1445 #[cfg(test)]
1448 pub(crate) const BUILTIN_SCALAR_NAMES: &[&str] = &[$($name),*];
1449
1450 pub(crate) fn builtin_value_type(id: TypeId) -> Option<cel_runtime::ValueType> {
1452 $(if id == TypeId::of::<$ty>() {
1453 return Some(complete_builtin_leaf::<$ty>());
1454 })*
1455 None
1456 }
1457
1458 pub(crate) fn builtin_scalar_type(name: &str) -> Option<BuiltinScalarType> {
1464 Some(match name {
1465 $($name => BuiltinScalarType {
1466 type_name: $name,
1467 value_type: complete_builtin_leaf::<$ty>,
1468 },)*
1469 _ => return None,
1470 })
1471 }
1472 };
1473}
1474
1475builtin_scalars! {
1476 "u8" => u8,
1477 "u16" => u16,
1478 "u32" => u32,
1479 "u64" => u64,
1480 "u128" => u128,
1481 "usize" => usize,
1482 "i8" => i8,
1483 "i16" => i16,
1484 "i32" => i32,
1485 "i64" => i64,
1486 "i128" => i128,
1487 "isize" => isize,
1488 "f32" => f32,
1489 "f64" => f64,
1490 "bool" => bool,
1491 "String" => String,
1492}
1493
1494macro_rules! builtin_generic_types {
1500 ($($generic:literal => { $($arg_name:literal => $ty:ty),* $(,)? }),* $(,)?) => {
1501 pub(crate) fn builtin_generic_type(
1508 generic_name: &str,
1509 arg_type_name: &str,
1510 ) -> Option<BuiltinScalarType> {
1511 match generic_name {
1512 $($generic => match arg_type_name {
1513 $($arg_name => Some(BuiltinScalarType {
1514 type_name: concat!($generic, "(", $arg_name, ")"),
1515 value_type: complete_builtin_leaf::<$ty>,
1516 }),)*
1517 _ => None,
1518 },)*
1519 _ => None,
1520 }
1521 }
1522 };
1523}
1524
1525builtin_generic_types! {
1526 "Range" => {
1527 "u8" => std::ops::Range<u8>, "u16" => std::ops::Range<u16>,
1528 "u32" => std::ops::Range<u32>, "u64" => std::ops::Range<u64>,
1529 "u128" => std::ops::Range<u128>, "usize" => std::ops::Range<usize>,
1530 "i8" => std::ops::Range<i8>, "i16" => std::ops::Range<i16>,
1531 "i32" => std::ops::Range<i32>, "i64" => std::ops::Range<i64>,
1532 "i128" => std::ops::Range<i128>, "isize" => std::ops::Range<isize>,
1533 "f32" => std::ops::Range<f32>, "f64" => std::ops::Range<f64>,
1534 },
1535 "RangeInclusive" => {
1536 "u8" => std::ops::RangeInclusive<u8>, "u16" => std::ops::RangeInclusive<u16>,
1537 "u32" => std::ops::RangeInclusive<u32>, "u64" => std::ops::RangeInclusive<u64>,
1538 "u128" => std::ops::RangeInclusive<u128>, "usize" => std::ops::RangeInclusive<usize>,
1539 "i8" => std::ops::RangeInclusive<i8>, "i16" => std::ops::RangeInclusive<i16>,
1540 "i32" => std::ops::RangeInclusive<i32>, "i64" => std::ops::RangeInclusive<i64>,
1541 "i128" => std::ops::RangeInclusive<i128>, "isize" => std::ops::RangeInclusive<isize>,
1542 "f32" => std::ops::RangeInclusive<f32>, "f64" => std::ops::RangeInclusive<f64>,
1543 },
1544 "RangeFrom" => {
1545 "u8" => std::ops::RangeFrom<u8>, "u16" => std::ops::RangeFrom<u16>,
1546 "u32" => std::ops::RangeFrom<u32>, "u64" => std::ops::RangeFrom<u64>,
1547 "u128" => std::ops::RangeFrom<u128>, "usize" => std::ops::RangeFrom<usize>,
1548 "i8" => std::ops::RangeFrom<i8>, "i16" => std::ops::RangeFrom<i16>,
1549 "i32" => std::ops::RangeFrom<i32>, "i64" => std::ops::RangeFrom<i64>,
1550 "i128" => std::ops::RangeFrom<i128>, "isize" => std::ops::RangeFrom<isize>,
1551 "f32" => std::ops::RangeFrom<f32>, "f64" => std::ops::RangeFrom<f64>,
1552 },
1553 "RangeTo" => {
1554 "u8" => std::ops::RangeTo<u8>, "u16" => std::ops::RangeTo<u16>,
1555 "u32" => std::ops::RangeTo<u32>, "u64" => std::ops::RangeTo<u64>,
1556 "u128" => std::ops::RangeTo<u128>, "usize" => std::ops::RangeTo<usize>,
1557 "i8" => std::ops::RangeTo<i8>, "i16" => std::ops::RangeTo<i16>,
1558 "i32" => std::ops::RangeTo<i32>, "i64" => std::ops::RangeTo<i64>,
1559 "i128" => std::ops::RangeTo<i128>, "isize" => std::ops::RangeTo<isize>,
1560 "f32" => std::ops::RangeTo<f32>, "f64" => std::ops::RangeTo<f64>,
1561 },
1562 "RangeToInclusive" => {
1563 "u8" => std::ops::RangeToInclusive<u8>, "u16" => std::ops::RangeToInclusive<u16>,
1564 "u32" => std::ops::RangeToInclusive<u32>, "u64" => std::ops::RangeToInclusive<u64>,
1565 "u128" => std::ops::RangeToInclusive<u128>, "usize" => std::ops::RangeToInclusive<usize>,
1566 "i8" => std::ops::RangeToInclusive<i8>, "i16" => std::ops::RangeToInclusive<i16>,
1567 "i32" => std::ops::RangeToInclusive<i32>, "i64" => std::ops::RangeToInclusive<i64>,
1568 "i128" => std::ops::RangeToInclusive<i128>, "isize" => std::ops::RangeToInclusive<isize>,
1569 "f32" => std::ops::RangeToInclusive<f32>, "f64" => std::ops::RangeToInclusive<f64>,
1570 },
1571}
1572
1573pub(crate) fn builtin_generic_type_0(name: &str) -> Option<BuiltinScalarType> {
1578 match name {
1579 "RangeFull" => Some(BuiltinScalarType {
1580 type_name: "RangeFull",
1581 value_type: complete_builtin_leaf::<std::ops::RangeFull>,
1582 }),
1583 _ => None,
1584 }
1585}
1586
1587struct BuiltinScope;
1591
1592impl BuiltinScope {
1593 fn lookup(
1599 &self,
1600 name: &str,
1601 segment: &mut DynSegment,
1602 num_operands: usize,
1603 span: SourceSpan,
1604 ) -> Result<bool> {
1605 let stack_infos = segment.peek_stack_infos(num_operands);
1606 let Some(signatures) = signatures_for(name) else {
1607 return Ok(false);
1608 };
1609 for sig in signatures {
1610 let arity = sig.arity as usize;
1611 let matches = arity == stack_infos.len()
1612 && stack_infos[0].value_type.type_id() == sig.lhs_type_id()
1613 && (arity < 2 || stack_infos[1].value_type.type_id() == sig.rhs_type_id());
1614 if matches {
1615 (sig.op_fn)(segment, span)?;
1616 return Ok(true);
1617 }
1618 }
1619 Ok(false)
1620 }
1621}
1622
1623fn range_full_scope(
1632 name: &str,
1633 segment: &mut DynSegment,
1634 num_operands: usize,
1635 _span: SourceSpan,
1636) -> Result<bool> {
1637 match (name, num_operands) {
1638 ("range_full", 0) => {
1639 segment.op0(|| std::ops::RangeFull);
1640 Ok(true)
1641 }
1642 _ => Ok(false),
1643 }
1644}
1645
1646pub struct OpLookup {
1668 pub(crate) registered: crate::registered_ops::RegisteredOps,
1669 scopes: Vec<ScopeFn>,
1670 library_scope_count: usize,
1671 builtin_scope: BuiltinScope,
1672 tuple_signatures: Vec<TupleOpSignature>,
1673}
1674
1675impl OpLookup {
1676 pub fn new() -> Self {
1687 let mut lookup = OpLookup {
1688 registered: Default::default(),
1689 scopes: Vec::new(),
1690 library_scope_count: 0,
1691 builtin_scope: BuiltinScope,
1692 tuple_signatures: Vec::new(),
1693 };
1694 lookup.push_library_scope(range_full_scope);
1695 lookup
1696 }
1697
1698 pub fn register_tuple_op(&mut self, signature: TupleOpSignature) {
1725 self.tuple_signatures.push(signature);
1726 }
1727
1728 fn lookup_tuple_signature(
1734 &self,
1735 name: &str,
1736 segment: &mut DynSegment,
1737 num_operands: usize,
1738 span: SourceSpan,
1739 ) -> Result<bool> {
1740 let stack_infos = segment.peek_stack_infos(num_operands);
1741 for sig in &self.tuple_signatures {
1742 if sig.name != name || sig.tuple_operand_index >= stack_infos.len() {
1743 continue;
1744 }
1745 let tuple_info = &stack_infos[sig.tuple_operand_index];
1746 let shape_matches = tuple_info
1747 .value_type
1748 .tuple_elements()
1749 .is_some_and(|elements| {
1750 elements.len() == sig.shape.len()
1751 && elements
1752 .iter()
1753 .zip(&sig.shape)
1754 .all(|(a, t)| a.value_type.type_id() == *t)
1755 });
1756 if !shape_matches {
1757 continue;
1758 }
1759 let others_match = stack_infos.iter().enumerate().all(|(i, info)| {
1760 i == sig.tuple_operand_index
1761 || sig.operand_type_ids.get(i) == Some(&info.value_type.type_id())
1762 });
1763 if others_match {
1764 (sig.op_fn)(segment, span)?;
1765 return Ok(true);
1766 }
1767 }
1768 Ok(false)
1769 }
1770
1771 pub fn push_scope<F>(&mut self, scope: F)
1801 where
1802 F: Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync + 'static,
1803 {
1804 self.scopes.push(Box::new(scope));
1805 }
1806
1807 pub fn pop_scope(&mut self) -> Option<ScopeFn> {
1818 debug_assert!(
1819 self.scopes.len() > self.library_scope_count,
1820 "pop_scope must not remove a library scope — use isolate_scopes/restore_scopes semantics instead"
1821 );
1822 self.scopes.pop()
1823 }
1824
1825 pub fn push_library_scope<F>(&mut self, scope: F)
1842 where
1843 F: Fn(&str, &mut DynSegment, usize, SourceSpan) -> Result<bool> + Send + Sync + 'static,
1844 {
1845 debug_assert!(
1846 self.scopes.len() == self.library_scope_count,
1847 "push_library_scope must not be called after a transient scope — use isolate_scopes/restore_scopes semantics instead"
1848 );
1849 self.scopes.push(Box::new(scope));
1850 self.library_scope_count = self.scopes.len();
1851 }
1852
1853 pub fn isolate_scopes(&mut self) -> Vec<ScopeFn> {
1896 self.scopes.split_off(self.library_scope_count)
1897 }
1898
1899 pub fn restore_scopes(&mut self, scopes: Vec<ScopeFn>) {
1930 self.scopes.truncate(self.library_scope_count);
1931 self.scopes.extend(scopes);
1932 }
1933
1934 pub fn lookup(
1963 &self,
1964 name: &str,
1965 segment: &mut DynSegment,
1966 num_operands: usize,
1967 start: proc_macro2::Span,
1968 end: proc_macro2::Span,
1969 ) -> std::result::Result<(), crate::ParseError> {
1970 self.lookup_scoped(
1971 name,
1972 segment,
1973 num_operands,
1974 crate::ExprSpan { start, end },
1975 false,
1976 false,
1977 )
1978 }
1979
1980 pub fn lookup_member(
2005 &self,
2006 name: &str,
2007 segment: &mut DynSegment,
2008 arity: usize,
2009 start: proc_macro2::Span,
2010 end: proc_macro2::Span,
2011 ) -> crate::Result<()> {
2012 self.lookup_scoped(
2013 name,
2014 segment,
2015 arity,
2016 crate::ExprSpan { start, end },
2017 true,
2018 false,
2019 )
2020 }
2021
2022 pub(crate) fn lookup_direct_operation_scoped(
2031 &self,
2032 name: &str,
2033 segment: &mut DynSegment,
2034 arity: usize,
2035 span: SourceSpan,
2036 library_only: bool,
2037 ) -> Result<bool> {
2038 if arity > 0 {
2039 let scopes = if library_only {
2040 &self.scopes[..self.library_scope_count]
2041 } else {
2042 &self.scopes
2043 };
2044 for scope in scopes.iter().rev() {
2045 if scope(name, segment, arity, span)? {
2046 return Ok(true);
2047 }
2048 }
2049 }
2050 self.lookup_registered_operation(name, segment, arity, span)
2051 }
2052
2053 pub(crate) fn lookup_scoped(
2058 &self,
2059 name: &str,
2060 segment: &mut DynSegment,
2061 num_operands: usize,
2062 span: crate::ExprSpan,
2063 member: bool,
2064 library_only: bool,
2065 ) -> crate::Result<()> {
2066 let crate::ExprSpan { start, end } = span;
2067 let source_span = SourceSpan::from_proc_macro2_range(start, end);
2068 let scopes = if library_only {
2069 &self.scopes[..self.library_scope_count]
2070 } else {
2071 &self.scopes
2072 };
2073 for scope in scopes.iter().rev().filter(|_| !member) {
2074 match scope(name, segment, num_operands, source_span) {
2075 Ok(true) => return Ok(()),
2076 Ok(false) => {}
2077 Err(e) => return Err(crate::ParseError::new_range(e.to_string(), start, end)),
2078 }
2079 }
2080
2081 let registered = if member {
2082 self.lookup_direct_operation_scoped(
2083 name,
2084 segment,
2085 num_operands,
2086 source_span,
2087 library_only,
2088 )
2089 } else {
2090 self.lookup_registered(name, segment, num_operands, source_span, library_only)
2091 };
2092 match registered {
2093 Ok(true) => return Ok(()),
2094 Ok(false) => {}
2095 Err(error) => return Err(crate::ParseError::new_range(error.to_string(), start, end)),
2096 }
2097
2098 match self.lookup_tuple_signature(name, segment, num_operands, source_span) {
2099 Ok(true) => return Ok(()),
2100 Ok(false) => {}
2101 Err(e) => {
2102 return Err(crate::ParseError::new_range(
2103 format!("operation error: {}", e),
2104 start,
2105 end,
2106 ));
2107 }
2108 }
2109
2110 match self
2111 .builtin_scope
2112 .lookup(name, segment, num_operands, source_span)
2113 {
2114 Ok(true) => return Ok(()),
2115 Ok(false) => {}
2116 Err(e) => {
2117 return Err(crate::ParseError::new_range(
2118 format!("operation error: {}", e),
2119 start,
2120 end,
2121 ));
2122 }
2123 }
2124
2125 if !member
2126 && name == "()"
2127 && num_operands > 0
2128 && segment
2129 .peek_stack_infos(num_operands)
2130 .first()
2131 .is_some_and(|info| {
2132 info.value_type.type_id() == TypeId::of::<cel_runtime::DynClosure>()
2133 })
2134 {
2135 return segment
2136 .invoke_closure(num_operands - 1)
2137 .map_err(|error| crate::ParseError::new_range(error.to_string(), start, end));
2138 }
2139
2140 if num_operands == 0 {
2141 return Err(crate::ParseError::new(
2142 format!("undefined identifier: `{name}`"),
2143 start,
2144 ));
2145 }
2146 let infos = segment.peek_stack_infos(num_operands);
2147 let mut type_names = String::new();
2148 for (i, info) in infos.iter().enumerate() {
2149 if i > 0 {
2150 type_names.push_str(", ");
2151 }
2152 type_names.push('`');
2153 type_names.push_str(info.value_type.type_name());
2154 type_names.push('`');
2155 }
2156 Err(crate::ParseError::new_range(
2157 format!("no operation `{name}` for types [{type_names}]"),
2158 start,
2159 end,
2160 ))
2161 }
2162
2163 pub fn lookup_cast(
2197 &self,
2198 type_name: &str,
2199 segment: &mut DynSegment,
2200 start: proc_macro2::Span,
2201 end: proc_macro2::Span,
2202 ) -> std::result::Result<(), crate::ParseError> {
2203 let source_span = SourceSpan::from_proc_macro2_range(start, end);
2204 let Some(signatures) = signatures_for_cast(type_name) else {
2205 return Err(crate::ParseError::new_range(
2206 format!("unknown type `{type_name}`"),
2207 start,
2208 end,
2209 ));
2210 };
2211 let Some(operand) = segment.peek_stack_infos(1).first() else {
2212 return Err(crate::ParseError::new_range(
2213 "cast requires an operand on the stack".to_string(),
2214 start,
2215 end,
2216 ));
2217 };
2218 let source_type_id = operand.value_type.type_id();
2219 for sig in signatures {
2220 if sig.source_type_id() == source_type_id {
2221 (sig.op_fn)(segment, source_span).map_err(|e| {
2222 crate::ParseError::new_range(format!("cast error: {}", e), start, end)
2223 })?;
2224 return Ok(());
2225 }
2226 }
2227 Err(crate::ParseError::new_range(
2228 format!(
2229 "no cast from `{}` to `{type_name}`",
2230 operand.value_type.type_name()
2231 ),
2232 start,
2233 end,
2234 ))
2235 }
2236}
2237
2238impl Default for OpLookup {
2239 fn default() -> Self {
2240 Self::new()
2241 }
2242}
2243
2244#[cfg(test)]
2245mod tests {
2246 use super::*;
2247 use proc_macro2::Span;
2248
2249 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
2250
2251 #[test]
2252 fn test_addition_u32() -> Result<()> {
2253 let lookup = OpLookup::new();
2254 let mut segment = DynSegment::new::<()>();
2255 segment.just(10u32);
2256 segment.just(20u32);
2257 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2258 assert_eq!(segment.call0::<u32>()?, 30);
2259 Ok(())
2260 }
2261
2262 #[test]
2263 fn test_subtraction_i32() -> Result<()> {
2264 let lookup = OpLookup::new();
2265 let mut segment = DynSegment::new::<()>();
2266 segment.just(50i32);
2267 segment.just(20i32);
2268 lookup.lookup("-", &mut segment, 2, Span::call_site(), Span::call_site())?;
2269 assert_eq!(segment.call0::<i32>()?, 30);
2270 Ok(())
2271 }
2272
2273 #[test]
2274 fn test_arithmetic_overflow() -> Result<()> {
2275 let lookup = OpLookup::new();
2276 let mut segment = DynSegment::new::<()>();
2277 segment.just(i32::MAX);
2278 segment.just(1i32);
2279 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2280 let result = segment.call0::<i32>();
2281 assert!(result.is_err());
2282 let err = result.unwrap_err();
2283 let message = format!("{:#}", err);
2284 assert!(
2285 message.contains("arithmetic overflow"),
2286 "error message should mention arithmetic overflow, got: {message}"
2287 );
2288 Ok(())
2289 }
2290
2291 #[test]
2292 fn test_division_by_zero() -> Result<()> {
2293 let lookup = OpLookup::new();
2294 let mut segment = DynSegment::new::<()>();
2295 segment.just(10i32);
2296 segment.just(0i32);
2297 lookup.lookup("/", &mut segment, 2, Span::call_site(), Span::call_site())?;
2298 let result = segment.call0::<i32>();
2299 assert!(result.is_err());
2300 let err = result.unwrap_err();
2301 let message = format!("{:#}", err);
2302 assert!(
2303 message.contains("division by zero"),
2304 "error message should mention division by zero, got: {message}"
2305 );
2306 Ok(())
2307 }
2308
2309 #[test]
2310 fn test_modulo_by_zero() -> Result<()> {
2311 let lookup = OpLookup::new();
2312 let mut segment = DynSegment::new::<()>();
2313 segment.just(10u32);
2314 segment.just(0u32);
2315 lookup.lookup("%", &mut segment, 2, Span::call_site(), Span::call_site())?;
2316 let result = segment.call0::<u32>();
2317 assert!(result.is_err());
2318 let err = result.unwrap_err();
2319 let message = format!("{:#}", err);
2320 assert!(
2321 message.contains("division by zero"),
2322 "error message should mention division by zero, got: {message}"
2323 );
2324 Ok(())
2325 }
2326
2327 #[test]
2328 fn test_multiplication_f64() -> Result<()> {
2329 let lookup = OpLookup::new();
2330 let mut segment = DynSegment::new::<()>();
2331 segment.just(3.5f64);
2332 segment.just(2.0f64);
2333 lookup.lookup("*", &mut segment, 2, Span::call_site(), Span::call_site())?;
2334 assert_eq!(segment.call0::<f64>()?, 7.0);
2335 Ok(())
2336 }
2337
2338 #[test]
2339 fn test_comparison_less_than() -> Result<()> {
2340 let lookup = OpLookup::new();
2341 let mut segment = DynSegment::new::<()>();
2342 segment.just(10u32);
2343 segment.just(20u32);
2344 lookup.lookup("<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2345 assert!(segment.call0::<bool>()?);
2346 Ok(())
2347 }
2348
2349 #[test]
2350 fn test_bitwise_and() -> Result<()> {
2351 let lookup = OpLookup::new();
2352 let mut segment = DynSegment::new::<()>();
2353 segment.just(0b1010u32);
2354 segment.just(0b1100u32);
2355 lookup.lookup("&", &mut segment, 2, Span::call_site(), Span::call_site())?;
2356 assert_eq!(segment.call0::<u32>()?, 0b1000);
2357 Ok(())
2358 }
2359
2360 #[test]
2361 fn test_unary_negation() -> Result<()> {
2362 let lookup = OpLookup::new();
2363 let mut segment = DynSegment::new::<()>();
2364 segment.just(42i32);
2365 lookup.lookup("-", &mut segment, 1, Span::call_site(), Span::call_site())?;
2366 assert_eq!(segment.call0::<i32>()?, -42);
2367 Ok(())
2368 }
2369
2370 #[test]
2371 fn test_logical_not() -> Result<()> {
2372 let lookup = OpLookup::new();
2373 let mut segment = DynSegment::new::<()>();
2374 segment.just(true);
2375 lookup.lookup("!", &mut segment, 1, Span::call_site(), Span::call_site())?;
2376 assert!(!segment.call0::<bool>()?);
2377 Ok(())
2378 }
2379
2380 #[test]
2381 fn test_unregistered_operation() {
2382 let lookup = OpLookup::new();
2383 let mut segment = DynSegment::new::<()>();
2384 segment.just(10u32);
2385 segment.just(20u32);
2386 let result = lookup.lookup(
2387 "unknown_op",
2388 &mut segment,
2389 2,
2390 Span::call_site(),
2391 Span::call_site(),
2392 );
2393 assert!(result.is_err());
2394 }
2395
2396 #[test]
2397 fn test_custom_scope() -> Result<()> {
2398 let mut lookup = OpLookup::new();
2399
2400 lookup.push_scope(|name, segment, num_operands, _span| {
2401 let matches = {
2402 let top = segment.peek_stack_infos(num_operands);
2403 name == "double"
2404 && top.len() == 1
2405 && top[0].value_type.type_id() == TypeId::of::<u32>()
2406 };
2407 if matches {
2408 segment.op1(|a: u32| a * 2)?;
2409 Ok(true)
2410 } else {
2411 Ok(false)
2412 }
2413 });
2414
2415 let mut segment = DynSegment::new::<()>();
2416 segment.just(21u32);
2417 lookup.lookup(
2418 "double",
2419 &mut segment,
2420 1,
2421 Span::call_site(),
2422 Span::call_site(),
2423 )?;
2424 assert_eq!(segment.call0::<u32>()?, 42);
2425
2426 Ok(())
2427 }
2428
2429 #[test]
2430 fn test_scope_override() -> Result<()> {
2431 let mut lookup = OpLookup::new();
2432
2433 lookup.push_scope(|name, segment, num_operands, _span| {
2434 let matches = {
2435 let top = segment.peek_stack_infos(num_operands);
2436 name == "+" && top.len() == 2 && top[0].value_type.type_id() == TypeId::of::<u32>()
2437 };
2438 if matches {
2439 segment.op2(|_a: u32, _b: u32| 100u32)?;
2440 Ok(true)
2441 } else {
2442 Ok(false)
2443 }
2444 });
2445
2446 let mut segment = DynSegment::new::<()>();
2447 segment.just(10u32);
2448 segment.just(20u32);
2449 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2450 assert_eq!(segment.call0::<u32>()?, 100);
2451
2452 Ok(())
2453 }
2454
2455 #[test]
2456 fn lookup_cast_errors_rather_than_panics_on_an_empty_stack() -> Result<()> {
2457 let lookup = OpLookup::new();
2462 let mut segment = DynSegment::new::<()>();
2463 let result = lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site());
2464 assert!(result.is_err());
2465 Ok(())
2466 }
2467
2468 #[test]
2469 fn cast_widens_i32_to_f64_exactly() -> Result<()> {
2470 let lookup = OpLookup::new();
2471 let mut segment = DynSegment::new::<()>();
2472 segment.just(1024i32);
2473 lookup.lookup_cast("f64", &mut segment, Span::call_site(), Span::call_site())?;
2474 assert_eq!(segment.call0::<f64>()?, 1024.0);
2475 Ok(())
2476 }
2477
2478 #[test]
2479 fn cast_narrows_f64_to_i32_when_the_value_fits() -> Result<()> {
2480 let lookup = OpLookup::new();
2481 let mut segment = DynSegment::new::<()>();
2482 segment.just(1024.0f64);
2483 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2484 assert_eq!(segment.call0::<i32>()?, 1024);
2485 Ok(())
2486 }
2487
2488 #[test]
2489 fn cast_errors_when_the_value_does_not_fit_in_the_target_type() -> Result<()> {
2490 let lookup = OpLookup::new();
2495 let mut segment = DynSegment::new::<()>();
2496 segment.just(1.0e20f64);
2497 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2498 assert!(segment.call0::<i32>().is_err());
2499 Ok(())
2500 }
2501
2502 #[test]
2503 fn cast_errors_just_past_the_target_types_max_even_when_max_is_not_exactly_representable()
2504 -> Result<()> {
2505 let lookup = OpLookup::new();
2511 let mut segment = DynSegment::new::<()>();
2512 segment.just(2147483648.0f32); lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2514 assert!(segment.call0::<i32>().is_err());
2515 Ok(())
2516 }
2517
2518 #[test]
2519 fn cast_accepts_the_target_types_actual_max_value() -> Result<()> {
2520 let lookup = OpLookup::new();
2521 let mut segment = DynSegment::new::<()>();
2522 segment.just(2147483520.0f32); lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2524 assert_eq!(segment.call0::<i32>()?, 2147483520);
2525 Ok(())
2526 }
2527
2528 #[test]
2529 fn cast_errors_just_past_i64_max_from_f64() -> Result<()> {
2530 let lookup = OpLookup::new();
2533 let mut segment = DynSegment::new::<()>();
2534 segment.just(9223372036854775808.0f64); lookup.lookup_cast("i64", &mut segment, Span::call_site(), Span::call_site())?;
2536 assert!(segment.call0::<i64>().is_err());
2537 Ok(())
2538 }
2539
2540 #[test]
2541 fn cast_errors_for_an_unknown_target_type() -> Result<()> {
2542 let lookup = OpLookup::new();
2543 let mut segment = DynSegment::new::<()>();
2544 segment.just(1i32);
2545 let result = lookup.lookup_cast(
2546 "nonsense",
2547 &mut segment,
2548 Span::call_site(),
2549 Span::call_site(),
2550 );
2551 assert!(result.is_err());
2552 Ok(())
2553 }
2554
2555 #[test]
2556 fn cast_errors_for_an_unregistered_source_type() -> Result<()> {
2557 let lookup = OpLookup::new();
2560 let mut segment = DynSegment::new::<()>();
2561 segment.just("1".to_string());
2562 let result = lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site());
2563 assert!(result.is_err());
2564 Ok(())
2565 }
2566
2567 #[test]
2568 fn cast_from_bool_is_registered_for_every_integer_width() -> Result<()> {
2569 let lookup = OpLookup::new();
2571 for target in [
2572 "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
2573 ] {
2574 let mut segment = DynSegment::new::<()>();
2575 segment.just(true);
2576 lookup.lookup_cast(target, &mut segment, Span::call_site(), Span::call_site())?;
2577 }
2578 Ok(())
2579 }
2580
2581 #[test]
2582 fn cast_widens_bool_to_a_specific_integer_value() -> Result<()> {
2583 let lookup = OpLookup::new();
2585 let mut segment = DynSegment::new::<()>();
2586 segment.just(true);
2587 lookup.lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())?;
2588 assert_eq!(segment.call0::<i32>()?, 1);
2589
2590 let mut segment = DynSegment::new::<()>();
2591 segment.just(false);
2592 lookup.lookup_cast("u64", &mut segment, Span::call_site(), Span::call_site())?;
2593 assert_eq!(segment.call0::<u64>()?, 0);
2594 Ok(())
2595 }
2596
2597 #[test]
2598 fn cast_bool_to_bool_is_a_no_op_identity_conversion() -> Result<()> {
2599 let lookup = OpLookup::new();
2600 let mut segment = DynSegment::new::<()>();
2601 segment.just(true);
2602 lookup.lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site())?;
2603 assert!(segment.call0::<bool>()?);
2604 Ok(())
2605 }
2606
2607 #[test]
2608 fn cast_string_to_string_is_a_no_op_identity_conversion() -> Result<()> {
2609 let lookup = OpLookup::new();
2610 let mut segment = DynSegment::new::<()>();
2611 segment.just("hello".to_string());
2612 lookup.lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())?;
2613 assert_eq!(segment.call0::<String>()?, "hello");
2614 Ok(())
2615 }
2616
2617 #[test]
2618 fn cast_errors_from_a_number_to_bool() -> Result<()> {
2619 let lookup = OpLookup::new();
2622 let mut segment = DynSegment::new::<()>();
2623 segment.just(1i32);
2624 let result = lookup.lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site());
2625 assert!(result.is_err());
2626 Ok(())
2627 }
2628
2629 #[test]
2630 fn cast_errors_from_bool_to_a_float() -> Result<()> {
2631 let lookup = OpLookup::new();
2633 let mut segment = DynSegment::new::<()>();
2634 segment.just(true);
2635 let result = lookup.lookup_cast("f64", &mut segment, Span::call_site(), Span::call_site());
2636 assert!(result.is_err());
2637 Ok(())
2638 }
2639
2640 #[test]
2641 fn cast_errors_between_string_and_bool_in_both_directions() -> Result<()> {
2642 let lookup = OpLookup::new();
2643 let mut segment = DynSegment::new::<()>();
2644 segment.just(true);
2645 assert!(
2646 lookup
2647 .lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())
2648 .is_err()
2649 );
2650
2651 let mut segment = DynSegment::new::<()>();
2652 segment.just("true".to_string());
2653 assert!(
2654 lookup
2655 .lookup_cast("bool", &mut segment, Span::call_site(), Span::call_site())
2656 .is_err()
2657 );
2658 Ok(())
2659 }
2660
2661 #[test]
2662 fn cast_errors_between_string_and_numbers_in_both_directions() -> Result<()> {
2663 let lookup = OpLookup::new();
2664 let mut segment = DynSegment::new::<()>();
2665 segment.just(1i32);
2666 assert!(
2667 lookup
2668 .lookup_cast("String", &mut segment, Span::call_site(), Span::call_site())
2669 .is_err()
2670 );
2671
2672 let mut segment = DynSegment::new::<()>();
2673 segment.just("1".to_string());
2674 assert!(
2675 lookup
2676 .lookup_cast("i32", &mut segment, Span::call_site(), Span::call_site())
2677 .is_err()
2678 );
2679 Ok(())
2680 }
2681
2682 #[test]
2683 fn test_left_shift_u64() -> Result<()> {
2684 let lookup = OpLookup::new();
2685 let mut segment = DynSegment::new::<()>();
2686 segment.just(1u64);
2687 segment.just(3u32);
2688 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2689 assert_eq!(segment.call0::<u64>()?, 8);
2690 Ok(())
2691 }
2692
2693 #[test]
2694 fn test_right_shift_i32() -> Result<()> {
2695 let lookup = OpLookup::new();
2696 let mut segment = DynSegment::new::<()>();
2697 segment.just(16i32);
2698 segment.just(2u32);
2699 lookup.lookup(">>", &mut segment, 2, Span::call_site(), Span::call_site())?;
2700 assert_eq!(segment.call0::<i32>()?, 4);
2701 Ok(())
2702 }
2703
2704 #[test]
2705 fn test_shift_overflow() -> Result<()> {
2706 let lookup = OpLookup::new();
2707 let mut segment = DynSegment::new::<()>();
2708 segment.just(1u32);
2709 segment.just(32u32);
2710 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2711 let result = segment.call0::<u32>();
2712 assert!(result.is_err());
2713 let err = result.unwrap_err();
2714 let message = format!("{:#}", err);
2715 assert!(
2716 message.contains("shift overflow"),
2717 "error message should mention shift overflow, got: {message}"
2718 );
2719 Ok(())
2720 }
2721
2722 #[test]
2723 fn test_shift_i32_rhs() -> Result<()> {
2724 let lookup = OpLookup::new();
2725 let mut segment = DynSegment::new::<()>();
2726 segment.just(1u32);
2727 segment.just(3i32);
2728 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2729 assert_eq!(segment.call0::<u32>()?, 8);
2730 Ok(())
2731 }
2732
2733 #[test]
2734 fn test_shift_negative_rhs_errors() -> Result<()> {
2735 let lookup = OpLookup::new();
2736 let mut segment = DynSegment::new::<()>();
2737 segment.just(1u32);
2738 segment.just(-1i32);
2739 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2740 let result = segment.call0::<u32>();
2741 assert!(result.is_err());
2742 let err = result.unwrap_err();
2743 let message = format!("{:#}", err);
2744 assert!(message.contains("shift overflow"), "got: {message}");
2745 Ok(())
2746 }
2747
2748 #[test]
2749 fn test_shift_wide_rhs_overflow_errors() -> Result<()> {
2750 let lookup = OpLookup::new();
2751 let mut segment = DynSegment::new::<()>();
2752 segment.just(1u32);
2753 segment.just(u32::MAX as u64 + 1);
2754 lookup.lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())?;
2755 let result = segment.call0::<u32>();
2756 assert!(result.is_err());
2757 let err = result.unwrap_err();
2758 let message = format!("{:#}", err);
2759 assert!(message.contains("shift overflow"), "got: {message}");
2760 Ok(())
2761 }
2762
2763 #[test]
2764 fn test_shift_rejects_float_rhs() {
2765 let lookup = OpLookup::new();
2766 let mut segment = DynSegment::new::<()>();
2767 segment.just(1u32);
2768 segment.just(3.0f64);
2769 assert!(
2770 lookup
2771 .lookup("<<", &mut segment, 2, Span::call_site(), Span::call_site())
2772 .is_err()
2773 );
2774 }
2775
2776 #[test]
2777 fn test_scope_pop() -> Result<()> {
2778 let mut lookup = OpLookup::new();
2779
2780 lookup.push_scope(|name, segment, num_operands, _span| {
2781 let matches = {
2782 let top = segment.peek_stack_infos(num_operands);
2783 name == "+" && top.len() == 2 && top[0].value_type.type_id() == TypeId::of::<u32>()
2784 };
2785 if matches {
2786 segment.op2(|_a: u32, _b: u32| 100u32)?;
2787 Ok(true)
2788 } else {
2789 Ok(false)
2790 }
2791 });
2792
2793 let mut segment = DynSegment::new::<()>();
2794 segment.just(10u32);
2795 segment.just(20u32);
2796 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2797 assert_eq!(segment.call0::<u32>()?, 100);
2798
2799 lookup.pop_scope();
2800 let mut segment = DynSegment::new::<()>();
2801 segment.just(10u32);
2802 segment.just(20u32);
2803 lookup.lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())?;
2804 assert_eq!(segment.call0::<u32>()?, 30);
2805
2806 Ok(())
2807 }
2808
2809 #[test]
2810 fn lookup_not_found_error_carries_span() {
2811 let lookup = OpLookup::new();
2812 let mut segment = DynSegment::new::<()>();
2813 segment.just(10u32);
2814 segment.just(20.0f64);
2815 let err = lookup
2816 .lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())
2817 .unwrap_err();
2818 assert!(
2819 err.message().starts_with("no operation"),
2820 "expected 'no operation' prefix, got: {}",
2821 err.message()
2822 );
2823 assert!(err.message().contains("`+`"));
2824 assert!(err.message().contains("`u32`"));
2825 assert!(err.message().contains("`f64`"));
2826 }
2827
2828 #[test]
2829 fn lookup_not_found_error_has_range() {
2830 let lookup = OpLookup::new();
2831 let mut segment = DynSegment::new::<()>();
2832 segment.just(10u32);
2833 segment.just(20.0f64);
2834 let err = lookup
2835 .lookup("+", &mut segment, 2, Span::call_site(), Span::call_site())
2836 .unwrap_err();
2837 assert!(
2838 err.end_span().is_some(),
2839 "op-lookup errors should carry an end span"
2840 );
2841 }
2842
2843 #[cfg(feature = "span-diagnostics")]
2846 #[test]
2847 fn scope_fn_accepts_source_span_parameter() {
2848 let mut lookup = OpLookup::new();
2849 lookup.push_scope(
2850 |name: &str, segment: &mut DynSegment, arity: usize, span: crate::SourceSpan| {
2851 if name != "value" || arity != 0 {
2852 return Ok(false);
2853 }
2854 assert_eq!(span, crate::SourceSpan::new(1, 4, 1, 9));
2855 segment.just(span.start.column);
2856 Ok(true)
2857 },
2858 );
2859 let span = crate::ExprSpan::for_text(" value");
2860 let mut segment = DynSegment::new::<()>();
2861 lookup
2862 .lookup("value", &mut segment, 0, span.start, span.end)
2863 .unwrap();
2864 assert_eq!(segment.call0::<usize>().unwrap(), 4);
2865 }
2866
2867 #[test]
2871 fn format_rustc_style_falls_back_for_client_added_op_error() {
2872 use crate::FormatRustcStyle;
2873 use annotate_snippets::Renderer;
2874
2875 let mut lookup = OpLookup::new();
2876 lookup.push_scope(|name, segment, arity, _| {
2877 if name != "failure" || arity != 0 {
2878 return Ok(false);
2879 }
2880 segment.just(0i32);
2881 segment
2882 .op1r(|_: i32| -> anyhow::Result<i32> { anyhow::bail!("custom domain error") })?;
2883 Ok(true)
2884 });
2885 let mut segment = crate::CELParser::new(lookup).parse_str("failure").unwrap();
2886 let err = segment.call0::<i32>().unwrap_err();
2887 let output = err.format_rustc_style("unused source", "unused.cel", 1, &Renderer::plain());
2888 assert_eq!(output, "custom domain error");
2889 }
2890
2891 #[cfg(feature = "span-diagnostics")]
2892 #[test]
2893 fn runtime_error_carries_span_context() {
2894 use crate::{CELParser, FormatRustcStyle, SpanContext};
2895 use annotate_snippets::Renderer;
2896
2897 let mut parser = CELParser::new(OpLookup::new());
2898 let source = "1i32 + 2147483647i32"; let mut segment = parser.parse_str(source).expect("should parse");
2900 let err = segment.call0::<i32>().expect_err("should overflow");
2901 let ctx = err
2902 .downcast_ref::<SpanContext>()
2903 .expect("expected SpanContext on runtime error");
2904 assert_eq!(ctx.span().start.line, 1);
2908 let rendered = err.format_rustc_style(source, "test.cel", 1, &Renderer::plain());
2910 assert!(
2911 rendered.contains("arithmetic overflow"),
2912 "expected 'arithmetic overflow' in rendered output, got: {rendered}"
2913 );
2914 assert!(
2915 rendered.contains('^'),
2916 "expected caret marker in rendered output, got: {rendered}"
2917 );
2918 }
2919
2920 #[test]
2921 fn tuple_shaped_signature_matches_and_dispatches() -> Result<()> {
2922 let mut lookup = OpLookup::new();
2923 lookup.register_tuple_op(TupleOpSignature {
2924 name: "greet".to_string(),
2925 shape: vec![TypeId::of::<String>(), TypeId::of::<i32>()],
2926 tuple_operand_index: 0,
2927 operand_type_ids: vec![],
2928 op_fn: |seg, _span| {
2929 seg.tuple_index(1)?;
2930 seg.op1(|_ignored: i32| true)
2931 },
2932 });
2933
2934 let mut segment = DynSegment::new::<()>();
2935 let ambient_start = segment.current_stack_offset();
2936 segment.op0(|| "hi".to_string());
2937 segment.op0(|| 7i32);
2938 segment.make_tuple(2, ambient_start)?;
2939
2940 lookup.lookup(
2941 "greet",
2942 &mut segment,
2943 1,
2944 Span::call_site(),
2945 Span::call_site(),
2946 )?;
2947 assert!(segment.call0::<bool>()?);
2948 Ok(())
2949 }
2950
2951 #[test]
2952 fn tuple_shaped_signature_does_not_match_wrong_shape() {
2953 let mut lookup = OpLookup::new();
2954 lookup.register_tuple_op(TupleOpSignature {
2955 name: "greet".to_string(),
2956 shape: vec![TypeId::of::<String>(), TypeId::of::<i32>()],
2957 tuple_operand_index: 0,
2958 operand_type_ids: vec![],
2959 op_fn: |seg, _span| {
2960 seg.tuple_index(1)?;
2961 seg.op1(|_ignored: i32| true)
2962 },
2963 });
2964
2965 let mut segment = DynSegment::new::<()>();
2966 let ambient_start = segment.current_stack_offset();
2967 segment.op0(|| 1i32);
2968 segment.op0(|| 2i32);
2969 segment.make_tuple(2, ambient_start).unwrap();
2970
2971 let result = lookup.lookup(
2972 "greet",
2973 &mut segment,
2974 1,
2975 Span::call_site(),
2976 Span::call_site(),
2977 );
2978 assert!(
2979 result.is_err(),
2980 "shape (i32, i32) should not match (String, i32)"
2981 );
2982 }
2983
2984 #[test]
2985 fn tuple_shaped_signature_with_empty_shape_does_not_match_non_tuple() {
2986 let mut lookup = OpLookup::new();
2990 lookup.register_tuple_op(TupleOpSignature {
2991 name: "unit_greet".to_string(),
2992 shape: vec![],
2993 tuple_operand_index: 0,
2994 operand_type_ids: vec![],
2995 op_fn: |seg, _span| seg.op1(|_ignored: i32| true),
2996 });
2997
2998 let mut segment = DynSegment::new::<()>();
2999 segment.op0(|| 42i32);
3000
3001 let result = lookup.lookup(
3002 "unit_greet",
3003 &mut segment,
3004 1,
3005 Span::call_site(),
3006 Span::call_site(),
3007 );
3008 assert!(
3009 result.is_err(),
3010 "empty-shape tuple signature must not match a non-tuple operand"
3011 );
3012 }
3013
3014 #[test]
3015 fn builtin_operand_types_reports_a_binary_arithmetic_overload() {
3016 let sigs = builtin_operand_types("+");
3017 assert!(
3018 sigs.iter().any(|s| s.arity == 2
3019 && s.lhs == TypeId::of::<i32>()
3020 && s.rhs == TypeId::of::<i32>())
3021 );
3022 }
3023
3024 #[test]
3025 fn builtin_operand_types_reports_a_unary_overload() {
3026 let sigs = builtin_operand_types("!");
3027 assert!(
3028 sigs.iter()
3029 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<bool>())
3030 );
3031 }
3032
3033 #[test]
3034 fn builtin_operand_types_includes_unary_negation_but_only_for_signed_and_float_types() {
3035 let sigs = builtin_operand_types("-");
3036 assert!(
3037 sigs.iter()
3038 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<i32>())
3039 );
3040 assert!(
3041 sigs.iter()
3042 .any(|s| s.arity == 2 && s.lhs == TypeId::of::<i32>())
3043 );
3044 assert!(
3045 !sigs
3046 .iter()
3047 .any(|s| s.arity == 1 && s.lhs == TypeId::of::<u32>()),
3048 "unsigned types have no unary negation overload"
3049 );
3050 }
3051
3052 #[test]
3053 fn builtin_operand_types_covers_heterogeneous_shift_signatures() {
3054 let integers = [
3055 TypeId::of::<u8>(),
3056 TypeId::of::<u16>(),
3057 TypeId::of::<u32>(),
3058 TypeId::of::<u64>(),
3059 TypeId::of::<u128>(),
3060 TypeId::of::<usize>(),
3061 TypeId::of::<i8>(),
3062 TypeId::of::<i16>(),
3063 TypeId::of::<i32>(),
3064 TypeId::of::<i64>(),
3065 TypeId::of::<i128>(),
3066 TypeId::of::<isize>(),
3067 ];
3068 for operator in ["<<", ">>"] {
3069 let signatures = builtin_operand_types(operator);
3070 assert_eq!(signatures.len(), integers.len() * integers.len());
3071 for lhs in integers {
3072 for rhs in integers {
3073 assert_eq!(
3074 signatures
3075 .iter()
3076 .filter(|s| s.arity == 2 && s.lhs == lhs && s.rhs == rhs)
3077 .count(),
3078 1
3079 );
3080 }
3081 }
3082 }
3083 }
3084
3085 #[test]
3086 fn builtin_operand_types_is_empty_for_an_unregistered_name() {
3087 assert!(builtin_operand_types("not_an_operator").is_empty());
3088 }
3089
3090 #[test]
3091 fn builtin_operand_types_is_empty_for_a_runtime_only_tuple_op() {
3092 assert!(builtin_operand_types("greet").is_empty());
3096 }
3097
3098 #[test]
3099 fn builtin_scalar_type_resolves_every_documented_name() {
3100 for name in BUILTIN_SCALAR_NAMES {
3101 let scalar =
3102 builtin_scalar_type(name).unwrap_or_else(|| panic!("expected `{name}` to resolve"));
3103 assert_eq!(scalar.type_name, *name);
3104 }
3105 assert!(builtin_scalar_type("not_a_type").is_none());
3106 }
3107
3108 #[test]
3109 fn builtin_scalar_type_i32_matches_std_any_type_id() {
3110 let scalar = builtin_scalar_type("i32").unwrap();
3111 assert_eq!((scalar.value_type)().type_id(), TypeId::of::<i32>());
3112 assert_eq!((scalar.value_type)().size(), std::mem::size_of::<i32>());
3113 assert_eq!((scalar.value_type)().align(), std::mem::align_of::<i32>());
3114 }
3115
3116 #[test]
3117 fn builtin_scalar_type_push_arg_declares_a_readable_argument() {
3118 let scalar = builtin_scalar_type("i32").unwrap();
3119 let mut segment = DynSegment::new::<()>();
3120 (scalar.value_type)()
3121 .push_argument(&mut segment, 0)
3122 .unwrap();
3123 let value = 42i32;
3124 let result: i32 = segment.call_dyn(&[&value]).unwrap();
3125 assert_eq!(result, 42);
3126 }
3127
3128 #[test]
3129 fn builtin_generic_type_resolves_every_range_family_member_for_every_numeric_type() {
3130 macro_rules! check {
3131 ($($ty:ty),* $(,)?) => {$(
3132 for (generic, identity, size, align) in [
3133 ("Range", TypeId::of::<std::ops::Range<$ty>>(), std::mem::size_of::<std::ops::Range<$ty>>(), std::mem::align_of::<std::ops::Range<$ty>>()),
3134 ("RangeInclusive", TypeId::of::<std::ops::RangeInclusive<$ty>>(), std::mem::size_of::<std::ops::RangeInclusive<$ty>>(), std::mem::align_of::<std::ops::RangeInclusive<$ty>>()),
3135 ("RangeFrom", TypeId::of::<std::ops::RangeFrom<$ty>>(), std::mem::size_of::<std::ops::RangeFrom<$ty>>(), std::mem::align_of::<std::ops::RangeFrom<$ty>>()),
3136 ("RangeTo", TypeId::of::<std::ops::RangeTo<$ty>>(), std::mem::size_of::<std::ops::RangeTo<$ty>>(), std::mem::align_of::<std::ops::RangeTo<$ty>>()),
3137 ("RangeToInclusive", TypeId::of::<std::ops::RangeToInclusive<$ty>>(), std::mem::size_of::<std::ops::RangeToInclusive<$ty>>(), std::mem::align_of::<std::ops::RangeToInclusive<$ty>>()),
3138 ] {
3139 let resolved = builtin_generic_type(generic, stringify!($ty)).unwrap();
3140 let descriptor = (resolved.value_type)();
3141 assert_eq!(resolved.type_name, format!("{generic}({})", stringify!($ty)));
3142 assert_eq!(descriptor.type_id(), identity);
3143 assert_eq!(descriptor.size(), size);
3144 assert_eq!(descriptor.align(), align);
3145 }
3146 )*};
3147 }
3148 check!(
3149 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
3150 );
3151 }
3152
3153 #[test]
3154 fn builtin_generic_type_range_inclusive_f64_matches_std_any_type_id() {
3155 let resolved = builtin_generic_type("RangeInclusive", "f64").unwrap();
3156 assert_eq!(
3157 (resolved.value_type)().type_id(),
3158 TypeId::of::<std::ops::RangeInclusive<f64>>()
3159 );
3160 assert_eq!(
3161 (resolved.value_type)().size(),
3162 std::mem::size_of::<std::ops::RangeInclusive<f64>>()
3163 );
3164 }
3165
3166 #[test]
3167 fn builtin_generic_type_is_none_for_unknown_generic_or_argument() {
3168 assert!(builtin_generic_type("NotAGeneric", "f64").is_none());
3169 assert!(builtin_generic_type("Range", "not_a_type").is_none());
3170 assert!(builtin_generic_type("Range", "bool").is_none());
3171 assert!(builtin_generic_type("Range", "String").is_none());
3172 }
3173
3174 #[test]
3175 fn builtin_generic_type_0_resolves_range_full() {
3176 let resolved = builtin_generic_type_0("RangeFull").unwrap();
3177 assert_eq!(resolved.type_name, "RangeFull");
3178 assert_eq!(
3179 (resolved.value_type)().type_id(),
3180 TypeId::of::<std::ops::RangeFull>()
3181 );
3182 assert!(builtin_generic_type_0("NotAGeneric").is_none());
3183 }
3184
3185 #[test]
3186 fn builtin_generic_type_push_arg_declares_a_readable_argument() {
3187 let resolved = builtin_generic_type("RangeInclusive", "i32").unwrap();
3188 let mut segment = DynSegment::new::<()>();
3189 (resolved.value_type)()
3190 .push_argument(&mut segment, 0)
3191 .unwrap();
3192 let value = 1i32..=5i32;
3193 let result: std::ops::RangeInclusive<i32> = segment.call_dyn(&[&value]).unwrap();
3194 assert_eq!(result, 1..=5);
3195 }
3196
3197 #[test]
3198 fn isolate_scopes_removes_pushed_scopes_until_restored() {
3199 let mut lookup = OpLookup::new();
3200 lookup.push_scope(|name, segment, arity, _span| {
3201 if name == "custom" && arity == 0 {
3202 segment.just(1i32);
3203 Ok(true)
3204 } else {
3205 Ok(false)
3206 }
3207 });
3208
3209 let mut segment = DynSegment::new::<()>();
3210 let isolated = lookup.isolate_scopes();
3211 let err = lookup.lookup(
3212 "custom",
3213 &mut segment,
3214 0,
3215 proc_macro2::Span::call_site(),
3216 proc_macro2::Span::call_site(),
3217 );
3218 assert!(
3219 err.is_err(),
3220 "custom scope must not be reachable while isolated"
3221 );
3222
3223 lookup.restore_scopes(isolated);
3224 let mut segment = DynSegment::new::<()>();
3225 lookup
3226 .lookup(
3227 "custom",
3228 &mut segment,
3229 0,
3230 proc_macro2::Span::call_site(),
3231 proc_macro2::Span::call_site(),
3232 )
3233 .unwrap();
3234 assert_eq!(segment.call0::<i32>().unwrap(), 1);
3235 }
3236
3237 #[test]
3238 fn isolate_scopes_leaves_library_scopes_reachable() {
3239 let mut lookup = OpLookup::new();
3240 lookup.push_library_scope(|name, segment, arity, _| {
3241 if name != "persistent" || arity != 0 {
3242 return Ok(false);
3243 }
3244 segment.just(42i32);
3245 Ok(true)
3246 });
3247 let mut segment = DynSegment::new::<()>();
3248 let isolated = lookup.isolate_scopes();
3249 lookup
3250 .lookup(
3251 "persistent",
3252 &mut segment,
3253 0,
3254 proc_macro2::Span::call_site(),
3255 proc_macro2::Span::call_site(),
3256 )
3257 .expect("installed library callbacks survive isolation");
3258 assert_eq!(segment.call0::<i32>().unwrap(), 42);
3259 lookup.restore_scopes(isolated);
3260 let mut segment = DynSegment::new::<()>();
3261 lookup
3262 .lookup(
3263 "persistent",
3264 &mut segment,
3265 0,
3266 Span::call_site(),
3267 Span::call_site(),
3268 )
3269 .unwrap();
3270 assert_eq!(segment.call0::<i32>().unwrap(), 42);
3271 }
3272
3273 #[test]
3274 fn range_i32_constructs_a_range() -> Result<()> {
3275 let lookup = OpLookup::new();
3276 let mut segment = DynSegment::new::<()>();
3277 segment.just(1i32);
3278 segment.just(5i32);
3279 lookup.lookup(
3280 "range",
3281 &mut segment,
3282 2,
3283 Span::call_site(),
3284 Span::call_site(),
3285 )?;
3286 assert_eq!(segment.call0::<std::ops::Range<i32>>()?, 1i32..5i32);
3287 Ok(())
3288 }
3289
3290 #[test]
3291 fn range_f64_constructs_a_range() -> Result<()> {
3292 let lookup = OpLookup::new();
3293 let mut segment = DynSegment::new::<()>();
3294 segment.just(1.5f64);
3295 segment.just(5.5f64);
3296 lookup.lookup(
3297 "range",
3298 &mut segment,
3299 2,
3300 Span::call_site(),
3301 Span::call_site(),
3302 )?;
3303 assert_eq!(segment.call0::<std::ops::Range<f64>>()?, 1.5f64..5.5f64);
3304 Ok(())
3305 }
3306
3307 #[test]
3308 fn range_inclusive_i32_constructs_a_range_inclusive() -> Result<()> {
3309 let lookup = OpLookup::new();
3310 let mut segment = DynSegment::new::<()>();
3311 segment.just(1i32);
3312 segment.just(5i32);
3313 lookup.lookup(
3314 "range_inclusive",
3315 &mut segment,
3316 2,
3317 Span::call_site(),
3318 Span::call_site(),
3319 )?;
3320 assert_eq!(
3321 segment.call0::<std::ops::RangeInclusive<i32>>()?,
3322 1i32..=5i32
3323 );
3324 Ok(())
3325 }
3326
3327 #[test]
3328 fn range_rejects_mismatched_operand_types() {
3329 let lookup = OpLookup::new();
3330 let mut segment = DynSegment::new::<()>();
3331 segment.just(1i32);
3332 segment.just(5.0f64);
3333 let result = lookup.lookup(
3334 "range",
3335 &mut segment,
3336 2,
3337 Span::call_site(),
3338 Span::call_site(),
3339 );
3340 assert!(result.is_err(), "expected a type-mismatch error, got Ok");
3341 }
3342
3343 #[test]
3344 fn range_from_i32_constructs_a_range_from() -> Result<()> {
3345 let lookup = OpLookup::new();
3346 let mut segment = DynSegment::new::<()>();
3347 segment.just(3i32);
3348 lookup.lookup(
3349 "range_from",
3350 &mut segment,
3351 1,
3352 Span::call_site(),
3353 Span::call_site(),
3354 )?;
3355 assert_eq!(segment.call0::<std::ops::RangeFrom<i32>>()?, 3i32..);
3356 Ok(())
3357 }
3358
3359 #[test]
3360 fn range_to_i32_constructs_a_range_to() -> Result<()> {
3361 let lookup = OpLookup::new();
3362 let mut segment = DynSegment::new::<()>();
3363 segment.just(7i32);
3364 lookup.lookup(
3365 "range_to",
3366 &mut segment,
3367 1,
3368 Span::call_site(),
3369 Span::call_site(),
3370 )?;
3371 assert_eq!(segment.call0::<std::ops::RangeTo<i32>>()?, ..7i32);
3372 Ok(())
3373 }
3374
3375 #[test]
3376 fn range_to_inclusive_i32_constructs_a_range_to_inclusive() -> Result<()> {
3377 let lookup = OpLookup::new();
3378 let mut segment = DynSegment::new::<()>();
3379 segment.just(7i32);
3380 lookup.lookup(
3381 "range_to_inclusive",
3382 &mut segment,
3383 1,
3384 Span::call_site(),
3385 Span::call_site(),
3386 )?;
3387 assert_eq!(segment.call0::<std::ops::RangeToInclusive<i32>>()?, ..=7i32);
3388 Ok(())
3389 }
3390
3391 #[test]
3392 fn range_full_constructs_a_range_full() -> Result<()> {
3393 let lookup = OpLookup::new();
3394 let mut segment = DynSegment::new::<()>();
3395 lookup.lookup(
3396 "range_full",
3397 &mut segment,
3398 0,
3399 Span::call_site(),
3400 Span::call_site(),
3401 )?;
3402 segment.call0::<std::ops::RangeFull>()?;
3403 Ok(())
3404 }
3405}