1use super::Expression;
2
3pub struct Expr;
19
20impl Expr {
21 pub fn int(value: i64) -> Expression {
25 Expression::IntLiteral { value }
26 }
27
28 pub fn int64(value: i64) -> Expression {
31 Expression::Int64Literal { value }
32 }
33
34 pub fn bool(value: bool) -> Expression {
36 Expression::BoolLiteral { value }
37 }
38
39 pub fn null() -> Expression {
41 Expression::Null
42 }
43
44 pub fn param(index: u32) -> Expression {
48 Expression::Param { index }
49 }
50
51 pub fn eq(left: Expression, right: Expression) -> Expression {
55 Expression::Eq {
56 left: Box::new(left),
57 right: Box::new(right),
58 }
59 }
60
61 pub fn ne(left: Expression, right: Expression) -> Expression {
63 Expression::Ne {
64 left: Box::new(left),
65 right: Box::new(right),
66 }
67 }
68
69 pub fn lt(left: Expression, right: Expression) -> Expression {
71 Expression::Lt {
72 left: Box::new(left),
73 right: Box::new(right),
74 }
75 }
76
77 pub fn le(left: Expression, right: Expression) -> Expression {
79 Expression::Le {
80 left: Box::new(left),
81 right: Box::new(right),
82 }
83 }
84
85 pub fn gt(left: Expression, right: Expression) -> Expression {
87 Expression::Gt {
88 left: Box::new(left),
89 right: Box::new(right),
90 }
91 }
92
93 pub fn ge(left: Expression, right: Expression) -> Expression {
95 Expression::Ge {
96 left: Box::new(left),
97 right: Box::new(right),
98 }
99 }
100
101 pub fn eq64(left: Expression, right: Expression) -> Expression {
105 Expression::Eq64 {
106 left: Box::new(left),
107 right: Box::new(right),
108 }
109 }
110
111 pub fn ne64(left: Expression, right: Expression) -> Expression {
113 Expression::Ne64 {
114 left: Box::new(left),
115 right: Box::new(right),
116 }
117 }
118
119 pub fn lt64(left: Expression, right: Expression) -> Expression {
121 Expression::Lt64 {
122 left: Box::new(left),
123 right: Box::new(right),
124 }
125 }
126
127 pub fn le64(left: Expression, right: Expression) -> Expression {
129 Expression::Le64 {
130 left: Box::new(left),
131 right: Box::new(right),
132 }
133 }
134
135 pub fn gt64(left: Expression, right: Expression) -> Expression {
137 Expression::Gt64 {
138 left: Box::new(left),
139 right: Box::new(right),
140 }
141 }
142
143 pub fn ge64(left: Expression, right: Expression) -> Expression {
145 Expression::Ge64 {
146 left: Box::new(left),
147 right: Box::new(right),
148 }
149 }
150
151 pub fn and(left: Expression, right: Expression) -> Expression {
155 Expression::And {
156 left: Box::new(left),
157 right: Box::new(right),
158 }
159 }
160
161 pub fn or(left: Expression, right: Expression) -> Expression {
163 Expression::Or {
164 left: Box::new(left),
165 right: Box::new(right),
166 }
167 }
168
169 pub fn not(operand: Expression) -> Expression {
171 Expression::Not {
172 operand: Box::new(operand),
173 }
174 }
175
176 pub fn is_null(operand: Expression) -> Expression {
178 Expression::IsNull {
179 operand: Box::new(operand),
180 }
181 }
182
183 pub fn is_not_null(operand: Expression) -> Expression {
185 Expression::IsNotNull {
186 operand: Box::new(operand),
187 }
188 }
189
190 pub fn add(left: Expression, right: Expression) -> Expression {
194 Expression::Add {
195 left: Box::new(left),
196 right: Box::new(right),
197 }
198 }
199
200 pub fn sub(left: Expression, right: Expression) -> Expression {
202 Expression::Sub {
203 left: Box::new(left),
204 right: Box::new(right),
205 }
206 }
207
208 pub fn mul(left: Expression, right: Expression) -> Expression {
210 Expression::Mul {
211 left: Box::new(left),
212 right: Box::new(right),
213 }
214 }
215
216 pub fn div(left: Expression, right: Expression) -> Expression {
218 Expression::Div {
219 left: Box::new(left),
220 right: Box::new(right),
221 }
222 }
223
224 pub fn add64(left: Expression, right: Expression) -> Expression {
228 Expression::Add64 {
229 left: Box::new(left),
230 right: Box::new(right),
231 }
232 }
233
234 pub fn sub64(left: Expression, right: Expression) -> Expression {
236 Expression::Sub64 {
237 left: Box::new(left),
238 right: Box::new(right),
239 }
240 }
241
242 pub fn mul64(left: Expression, right: Expression) -> Expression {
244 Expression::Mul64 {
245 left: Box::new(left),
246 right: Box::new(right),
247 }
248 }
249
250 pub fn div64(left: Expression, right: Expression) -> Expression {
252 Expression::Div64 {
253 left: Box::new(left),
254 right: Box::new(right),
255 }
256 }
257
258 pub fn host_call(function_name: impl Into<String>, args: Vec<Expression>) -> Expression {
262 Expression::HostCall {
263 function_name: function_name.into(),
264 args,
265 }
266 }
267
268 pub fn list_contains(list: Expression, element: Expression) -> Expression {
275 Expression::ListContains {
276 list: Box::new(list),
277 element: Box::new(element),
278 }
279 }
280
281 pub fn string_equals(left: Expression, right: Expression) -> Expression {
287 Self::host_call("hstringEquals", vec![left, right])
288 }
289
290 pub fn ranges_overlap(
294 start1: Expression,
295 end1: Expression,
296 start2: Expression,
297 end2: Expression,
298 ) -> Expression {
299 Self::and(Self::lt(start1, end2), Self::lt(start2, end1))
300 }
301
302 pub fn ranges_overlap64(
307 start1: Expression,
308 end1: Expression,
309 start2: Expression,
310 end2: Expression,
311 ) -> Expression {
312 Self::and(Self::lt64(start1, end2), Self::lt64(start2, end1))
313 }
314
315 pub fn if_then_else(
319 condition: Expression,
320 then_branch: Expression,
321 else_branch: Expression,
322 ) -> Expression {
323 Expression::IfThenElse {
324 condition: Box::new(condition),
325 then_branch: Box::new(then_branch),
326 else_branch: Box::new(else_branch),
327 }
328 }
329
330 pub fn if_then_else64(
333 condition: Expression,
334 then_branch: Expression,
335 else_branch: Expression,
336 ) -> Expression {
337 Expression::IfThenElse64 {
338 condition: Box::new(condition),
339 then_branch: Box::new(then_branch),
340 else_branch: Box::new(else_branch),
341 }
342 }
343
344 pub fn i64_to_i32(operand: Expression) -> Expression {
349 Expression::I64ToI32 {
350 operand: Box::new(operand),
351 }
352 }
353
354 pub fn i32_to_i64(operand: Expression) -> Expression {
357 Expression::I32ToI64 {
358 operand: Box::new(operand),
359 }
360 }
361}
362
363pub trait FieldAccessExt {
367 fn get(self, class_name: &str, field_name: &str) -> Expression;
369}
370
371impl FieldAccessExt for Expression {
372 fn get(self, class_name: &str, field_name: &str) -> Expression {
373 Expression::FieldAccess {
374 object: Box::new(self),
375 class_name: class_name.into(),
376 field_name: field_name.into(),
377 field_type: super::WasmFieldType::Object,
378 }
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 #[test]
387 fn test_int_literal() {
388 let expr = Expr::int(42);
389 assert_eq!(expr, Expression::IntLiteral { value: 42 });
390 }
391
392 #[test]
393 fn test_bool_literal() {
394 let expr = Expr::bool(true);
395 assert_eq!(expr, Expression::BoolLiteral { value: true });
396 }
397
398 #[test]
399 fn test_null() {
400 let expr = Expr::null();
401 assert_eq!(expr, Expression::Null);
402 }
403
404 #[test]
405 fn test_param() {
406 let expr = Expr::param(0);
407 assert_eq!(expr, Expression::Param { index: 0 });
408 }
409
410 #[test]
411 fn test_field_access_chaining() {
412 let expr = Expr::param(0).get("Employee", "name");
413
414 match expr {
415 Expression::FieldAccess {
416 object,
417 class_name,
418 field_name,
419 ..
420 } => {
421 assert_eq!(class_name, "Employee");
422 assert_eq!(field_name, "name");
423 assert_eq!(*object, Expression::Param { index: 0 });
424 }
425 _ => panic!("Expected FieldAccess"),
426 }
427 }
428
429 #[test]
430 fn test_eq() {
431 let expr = Expr::eq(Expr::int(1), Expr::int(2));
432
433 match expr {
434 Expression::Eq { left, right } => {
435 assert_eq!(*left, Expression::IntLiteral { value: 1 });
436 assert_eq!(*right, Expression::IntLiteral { value: 2 });
437 }
438 _ => panic!("Expected Eq"),
439 }
440 }
441
442 #[test]
443 fn test_and() {
444 let expr = Expr::and(Expr::bool(true), Expr::bool(false));
445
446 match expr {
447 Expression::And { left, right } => {
448 assert_eq!(*left, Expression::BoolLiteral { value: true });
449 assert_eq!(*right, Expression::BoolLiteral { value: false });
450 }
451 _ => panic!("Expected And"),
452 }
453 }
454
455 #[test]
456 fn test_is_not_null() {
457 let expr = Expr::is_not_null(Expr::param(0));
458
459 match expr {
460 Expression::IsNotNull { operand } => {
461 assert_eq!(*operand, Expression::Param { index: 0 });
462 }
463 _ => panic!("Expected IsNotNull"),
464 }
465 }
466
467 #[test]
468 fn test_add() {
469 let expr = Expr::add(Expr::int(10), Expr::int(20));
470
471 match expr {
472 Expression::Add { left, right } => {
473 assert_eq!(*left, Expression::IntLiteral { value: 10 });
474 assert_eq!(*right, Expression::IntLiteral { value: 20 });
475 }
476 _ => panic!("Expected Add"),
477 }
478 }
479
480 #[test]
481 fn test_host_call() {
482 let expr = Expr::host_call("test_func", vec![Expr::int(1), Expr::int(2)]);
483
484 match expr {
485 Expression::HostCall {
486 function_name,
487 args,
488 } => {
489 assert_eq!(function_name, "test_func");
490 assert_eq!(args.len(), 2);
491 }
492 _ => panic!("Expected HostCall"),
493 }
494 }
495
496 #[test]
497 fn test_string_equals() {
498 let left = Expr::param(0).get("Employee", "skill");
499 let right = Expr::param(1).get("Shift", "requiredSkill");
500 let expr = Expr::string_equals(left, right);
501
502 match expr {
503 Expression::HostCall {
504 function_name,
505 args,
506 } => {
507 assert_eq!(function_name, "hstringEquals");
508 assert_eq!(args.len(), 2);
509 }
510 _ => panic!("Expected HostCall"),
511 }
512 }
513
514 #[test]
515 fn test_ranges_overlap() {
516 let expr = Expr::ranges_overlap(Expr::int(0), Expr::int(10), Expr::int(5), Expr::int(15));
517
518 match expr {
519 Expression::And { left, right } => {
520 match *left {
522 Expression::Lt { .. } => {}
523 _ => panic!("Expected Lt in left side"),
524 }
525 match *right {
527 Expression::Lt { .. } => {}
528 _ => panic!("Expected Lt in right side"),
529 }
530 }
531 _ => panic!("Expected And"),
532 }
533 }
534
535 #[test]
536 fn test_complex_predicate_builder() {
537 let shift = Expr::param(0);
539 let employee = shift.clone().get("Shift", "employee");
540
541 let predicate = Expr::and(
542 Expr::is_not_null(employee.clone()),
543 Expr::not(Expr::string_equals(
544 employee.get("Employee", "skill"),
545 shift.get("Shift", "requiredSkill"),
546 )),
547 );
548
549 match predicate {
551 Expression::And { .. } => {}
552 _ => panic!("Expected And at top level"),
553 }
554 }
555
556 #[test]
557 fn test_nested_field_access() {
558 let expr = Expr::param(0)
560 .get("Assignment", "shift")
561 .get("Shift", "employee")
562 .get("Employee", "name");
563
564 match expr {
565 Expression::FieldAccess {
566 class_name,
567 field_name,
568 object,
569 ..
570 } => {
571 assert_eq!(class_name, "Employee");
572 assert_eq!(field_name, "name");
573
574 match *object {
575 Expression::FieldAccess { .. } => {}
576 _ => panic!("Expected nested FieldAccess"),
577 }
578 }
579 _ => panic!("Expected FieldAccess"),
580 }
581 }
582
583 #[test]
584 fn test_time_calculation() {
585 let expr = Expr::div(Expr::param(0).get("Shift", "start"), Expr::int(24));
587
588 match expr {
589 Expression::Div { left, right } => {
590 match *left {
591 Expression::FieldAccess { .. } => {}
592 _ => panic!("Expected FieldAccess"),
593 }
594 assert_eq!(*right, Expression::IntLiteral { value: 24 });
595 }
596 _ => panic!("Expected Div"),
597 }
598 }
599
600 #[test]
601 fn test_if_then_else() {
602 let expr = Expr::if_then_else(
604 Expr::gt(Expr::param(0), Expr::int(0)),
605 Expr::int(1),
606 Expr::int(0),
607 );
608
609 match expr {
610 Expression::IfThenElse {
611 condition,
612 then_branch,
613 else_branch,
614 } => {
615 match *condition {
616 Expression::Gt { .. } => {}
617 _ => panic!("Expected Gt"),
618 }
619 assert_eq!(*then_branch, Expression::IntLiteral { value: 1 });
620 assert_eq!(*else_branch, Expression::IntLiteral { value: 0 });
621 }
622 _ => panic!("Expected IfThenElse"),
623 }
624 }
625
626 #[test]
627 fn test_nested_if_then_else() {
628 let expr = Expr::if_then_else(
630 Expr::gt(Expr::param(0), Expr::int(0)),
631 Expr::if_then_else(
632 Expr::gt(Expr::param(0), Expr::int(10)),
633 Expr::int(2),
634 Expr::int(1),
635 ),
636 Expr::int(0),
637 );
638
639 match expr {
640 Expression::IfThenElse { then_branch, .. } => match *then_branch {
641 Expression::IfThenElse { .. } => {}
642 _ => panic!("Expected nested IfThenElse"),
643 },
644 _ => panic!("Expected IfThenElse"),
645 }
646 }
647
648 #[test]
649 fn test_list_contains() {
650 let list = Expr::param(0).get("Employee", "skills");
651 let element = Expr::param(1).get("Shift", "requiredSkill");
652 let expr = Expr::list_contains(list, element);
653
654 match expr {
655 Expression::ListContains { list, element } => {
656 match *list {
657 Expression::FieldAccess { field_name, .. } => {
658 assert_eq!(field_name, "skills");
659 }
660 _ => panic!("Expected FieldAccess for list"),
661 }
662 match *element {
663 Expression::FieldAccess { field_name, .. } => {
664 assert_eq!(field_name, "requiredSkill");
665 }
666 _ => panic!("Expected FieldAccess for element"),
667 }
668 }
669 _ => panic!("Expected ListContains"),
670 }
671 }
672}