1use std::collections::HashSet;
2use std::hash::Hash;
3use std::rc::Rc;
4
5mod aggregation_method;
7mod builder;
8mod delta;
9mod implication;
10mod last;
11mod mirror;
12mod next_id;
13mod offset_or;
14mod probability;
15mod probability_aggregation;
16mod true_ratio;
17use aggregation_method::AggrMethodToWindow;
18use delta::Delta;
19use itertools::Itertools;
20use last::Last;
21use mirror::Mirror as SynSugMirror;
22use probability::Probability;
23use probability_aggregation::ProbabilityAggregation;
24use rtlola_reporting::RtLolaError;
25use true_ratio::TrueRatio;
26
27use self::implication::Implication;
28use self::offset_or::OffsetOr;
29use crate::ast::{
30 CloseSpec, EvalSpec, Expression, ExpressionKind, Input, InstanceSelection, LambdaExpr,
31 Mirror as AstMirror, NodeId, Output, RtLolaAst, SpawnSpec,
32};
33
34#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
35enum ChangeInstruction {
36 #[allow(dead_code)] ReplaceExpr(NodeId, Expression),
38 RemoveStream(NodeId),
39 AddOutput(Box<Output>),
40}
41#[derive(Debug, Clone, Eq, PartialEq, Hash)]
42enum LocalChangeInstruction {
43 ReplaceExpr(Expression),
44}
45
46#[derive(Debug, Clone)]
51struct ChangeSet {
52 _local_applied_flag: bool,
53 local_instructions: Option<LocalChangeInstruction>,
54 global_instructions: HashSet<ChangeInstruction>,
55}
56
57#[derive(Clone, Copy, Debug)]
58enum ExprOrigin {
59 SpawnWhen,
60 SpawnWith,
61 #[allow(dead_code)]
62 EvalWhen(usize),
63 EvalWith(usize),
64 CloseWhen,
65}
66
67#[allow(unused_variables)] trait SynSugar {
74 fn desugarize_expr<'a>(
80 &self,
81 exp: &'a Expression,
82 ast: &'a RtLolaAst,
83 stream: usize,
84 origin: ExprOrigin,
85 ) -> Result<ChangeSet, RtLolaError> {
86 Ok(ChangeSet::empty())
87 }
88
89 fn desugarize_stream_out<'a>(
95 &self,
96 stream: &'a Output,
97 ast: &'a RtLolaAst,
98 ) -> Result<ChangeSet, RtLolaError> {
99 Ok(ChangeSet::empty())
100 }
101 fn desugarize_stream_in<'a>(
107 &self,
108 stream: &'a Input,
109 ast: &'a RtLolaAst,
110 ) -> Result<ChangeSet, RtLolaError> {
111 Ok(ChangeSet::empty())
112 }
113 fn desugarize_stream_mirror<'a>(
119 &self,
120 stream: &'a AstMirror,
121 ast: &'a RtLolaAst,
122 ) -> Result<ChangeSet, RtLolaError> {
123 Ok(ChangeSet::empty())
124 }
125}
126
127#[allow(missing_debug_implementations)] pub struct Desugarizer {
133 sugar_transformers: Vec<Box<dyn SynSugar>>,
134}
135
136impl Desugarizer {
137 pub fn all() -> Self {
142 let all_transformers: Vec<Box<dyn SynSugar>> = vec![
143 Box::new(Implication {}),
144 Box::new(AggrMethodToWindow {}),
145 Box::new(Last {}),
146 Box::new(SynSugMirror {}),
147 Box::new(Delta {}),
148 Box::new(OffsetOr {}),
149 Box::new(TrueRatio {}),
150 Box::new(Probability {}),
151 Box::new(ProbabilityAggregation {}),
152 ];
153 Self {
154 sugar_transformers: all_transformers,
155 }
156 }
157
158 pub fn remove_syn_sugar(&self, mut ast: RtLolaAst) -> Result<RtLolaAst, RtLolaError> {
163 while {
164 let (new_ast, change_flag) = self.desugarize_fix_point(ast)?;
166 ast = new_ast;
167 change_flag
168 } {} Ok(ast)
170 }
171
172 fn desugarize_fix_point(&self, mut ast: RtLolaAst) -> Result<(RtLolaAst, bool), RtLolaError> {
173 let mut change_flag = false;
174 for current_sugar in self.sugar_transformers.iter() {
175 let mut change_set = ChangeSet::empty();
176
177 for mirror in ast.mirrors.iter() {
178 change_set += self.desugarize_mirror(mirror, &ast, current_sugar)?;
179 }
180
181 for ix in 0..ast.outputs.len() {
182 let out = &ast.outputs[ix];
183 let out_clone: Output = Output::clone(out);
184 let Output {
185 spawn, eval, close, ..
186 } = out_clone;
187 let new_spawn_spec = if let Some(spawn_spec) = spawn {
188 let SpawnSpec {
189 expression,
190 condition,
191 annotated_pacing,
192 id,
193 span,
194 } = spawn_spec;
195 let expression = expression
196 .map(|expr| {
197 let (new_expr, spawn_cs) = Self::desugarize_expression(
198 expr,
199 &ast,
200 current_sugar,
201 ix,
202 ExprOrigin::SpawnWith,
203 )?;
204 change_set += spawn_cs;
205 Ok::<_, RtLolaError>(new_expr)
206 })
207 .transpose()?;
208 let condition = condition
209 .map(|expr| {
210 let (new_expr, spawn_cond_cs) = Self::desugarize_expression(
211 expr,
212 &ast,
213 current_sugar,
214 ix,
215 ExprOrigin::SpawnWhen,
216 )?;
217 change_set += spawn_cond_cs;
218 Ok::<_, RtLolaError>(new_expr)
219 })
220 .transpose()?;
221 Some(SpawnSpec {
222 expression,
223 condition,
224 annotated_pacing,
225 id,
226 span,
227 })
228 } else {
229 None
230 };
231
232 let transformed_eval = eval
233 .into_iter()
234 .enumerate()
235 .map(|(i, eval_spec)| {
236 let EvalSpec {
237 annotated_pacing,
238 condition,
239 eval_expression,
240 id,
241 span,
242 } = eval_spec;
243 let new_eval = eval_expression
244 .map(|e| {
245 let (res, eval_cs) = Self::desugarize_expression(
246 e,
247 &ast,
248 current_sugar,
249 ix,
250 ExprOrigin::EvalWith(i),
251 )?;
252 change_set += eval_cs;
253 Ok::<_, RtLolaError>(res)
254 })
255 .transpose()?;
256 let new_condition = condition
257 .map(|e| {
258 let (res, cond_cs) = Self::desugarize_expression(
259 e,
260 &ast,
261 current_sugar,
262 ix,
263 ExprOrigin::EvalWhen(i),
264 )?;
265 change_set += cond_cs;
266 Ok::<_, RtLolaError>(res)
267 })
268 .transpose()?;
269 Ok::<_, RtLolaError>(Some(EvalSpec {
270 annotated_pacing,
271 condition: new_condition,
272 eval_expression: new_eval,
273 id,
274 span,
275 }))
276 })
277 .flatten_ok()
278 .collect::<Result<_, _>>()?;
279
280 let new_close_spec = if let Some(close_spec) = close {
281 let CloseSpec {
282 condition,
283 id,
284 span,
285 annotated_pacing,
286 } = close_spec;
287 let (new_condition, close_cs) = Self::desugarize_expression(
288 condition,
289 &ast,
290 current_sugar,
291 ix,
292 ExprOrigin::CloseWhen,
293 )?;
294 change_set += close_cs;
295 Some(CloseSpec {
296 condition: new_condition,
297 id,
298 span,
299 annotated_pacing,
300 })
301 } else {
302 None
303 };
304
305 let new_out = Output {
306 spawn: new_spawn_spec,
307 eval: transformed_eval,
308 close: new_close_spec,
309 ..out_clone
310 };
311 ast.outputs[ix] = Rc::new(new_out);
312 }
313 for input in ast.inputs.iter() {
314 change_set += self.desugarize_input(input, &ast, current_sugar)?;
315 }
316
317 for output in ast.outputs.iter() {
318 change_set += self.desugarize_output(output, &ast, current_sugar)?;
319 }
320
321 change_flag |=
322 change_set._local_applied_flag || !change_set.global_instructions.is_empty();
323 ast = self.apply_global_changes(change_set, ast);
324 }
325 Ok((ast, change_flag))
326 }
327
328 fn apply_global_changes(&self, c_s: ChangeSet, mut ast: RtLolaAst) -> RtLolaAst {
329 c_s.global_iter().for_each(|ci| match ci {
330 ChangeInstruction::AddOutput(o) => {
331 ast.outputs.push(Rc::new(*o));
332 }
333 ChangeInstruction::RemoveStream(id) => {
334 if let Some(idx) = ast.outputs.iter().position(|o| o.id == id) {
335 assert_eq!(Rc::strong_count(&ast.outputs[idx]), 1);
336 ast.outputs.remove(idx);
337 } else if let Some(idx) = ast.inputs.iter().position(|o| o.id == id) {
338 assert_eq!(Rc::strong_count(&ast.inputs[idx]), 1);
339 ast.inputs.remove(idx);
340 } else if let Some(idx) = ast.mirrors.iter().position(|o| o.id == id) {
341 assert_eq!(Rc::strong_count(&ast.mirrors[idx]), 1);
342 ast.mirrors.remove(idx);
343 } else {
344 debug_assert!(false, "id in changeset does not belong to any stream");
345 }
346 }
347 ChangeInstruction::ReplaceExpr(_, expr) => {
348 for ix in 0..ast.outputs.len() {
349 let out = &ast.outputs[ix];
350 let out_clone: Output = Output::clone(out);
351 let Output {
352 spawn, eval, close, ..
353 } = out_clone;
354 let new_spawn_spec = if let Some(spawn_spec) = spawn {
355 let SpawnSpec {
356 expression,
357 condition,
358 annotated_pacing,
359 id,
360 span,
361 } = spawn_spec;
362 let expression = expression.map(|tar_expression| {
363 Self::apply_expr_global_change(id, &expr, &tar_expression)
364 });
365 let condition = condition.map(|cond_expression| {
366 Self::apply_expr_global_change(id, &expr, &cond_expression)
367 });
368 Some(SpawnSpec {
369 expression,
370 condition,
371 annotated_pacing,
372 id,
373 span,
374 })
375 } else {
376 None
377 };
378
379 let new_eval_spec = eval
380 .into_iter()
381 .flat_map(|eval_spec| {
382 let EvalSpec {
383 eval_expression,
384 condition,
385 annotated_pacing,
386 id,
387 span,
388 } = eval_spec;
389 let eval_expression = eval_expression
390 .map(|e| Self::apply_expr_global_change(id, &expr, &e));
391 let condition =
392 condition.map(|e| Self::apply_expr_global_change(id, &expr, &e));
393 Some(EvalSpec {
394 annotated_pacing,
395 condition,
396 eval_expression,
397 id,
398 span,
399 })
400 })
401 .collect();
402
403 let new_close_spec = if let Some(close_spec) = close {
404 let CloseSpec {
405 condition,
406 id,
407 span,
408 annotated_pacing,
409 } = close_spec;
410 let new_condition = Self::apply_expr_global_change(id, &expr, &condition);
411 Some(CloseSpec {
412 condition: new_condition,
413 id,
414 span,
415 annotated_pacing,
416 })
417 } else {
418 None
419 };
420
421 let new_out = Output {
422 spawn: new_spawn_spec,
423 eval: new_eval_spec,
424 close: new_close_spec,
425 ..out_clone
426 };
427 ast.outputs[ix] = Rc::new(new_out);
428 }
429 }
430 });
431
432 ast
433 }
434
435 fn apply_expr_global_change(
436 target_id: NodeId,
437 new_expr: &Expression,
438 ast_expr: &Expression,
439 ) -> Expression {
440 if ast_expr.id == target_id {
441 return new_expr.clone();
442 }
443 let span = ast_expr.span;
444
445 use ExpressionKind::*;
446 match &ast_expr.kind {
447 Lit(_) | Ident(_) | MissingExpression => ast_expr.clone(),
448 Unary(op, inner) => Expression {
449 kind: Unary(
450 *op,
451 Box::new(Self::apply_expr_global_change(target_id, new_expr, inner)),
452 ),
453 span,
454 ..*ast_expr
455 },
456 Field(inner, ident) => Expression {
457 kind: Field(
458 Box::new(Self::apply_expr_global_change(target_id, new_expr, inner)),
459 ident.clone(),
460 ),
461 span,
462 ..*ast_expr
463 },
464 StreamAccess(inner, acc_kind) => Expression {
465 kind: StreamAccess(
466 Box::new(Self::apply_expr_global_change(target_id, new_expr, inner)),
467 *acc_kind,
468 ),
469 span,
470 ..*ast_expr
471 },
472 Offset(inner, offset) => Expression {
473 kind: Offset(
474 Box::new(Self::apply_expr_global_change(target_id, new_expr, inner)),
475 *offset,
476 ),
477 span,
478 ..*ast_expr
479 },
480 ParenthesizedExpression(inner) => Expression {
481 kind: ParenthesizedExpression(Box::new(Self::apply_expr_global_change(
482 target_id, new_expr, inner,
483 ))),
484 span,
485 ..*ast_expr
486 },
487 Binary(bin_op, left, right) => Expression {
488 kind: Binary(
489 *bin_op,
490 Box::new(Self::apply_expr_global_change(target_id, new_expr, left)),
491 Box::new(Self::apply_expr_global_change(target_id, new_expr, right)),
492 ),
493 span,
494 ..*ast_expr
495 },
496 Default(left, right) => Expression {
497 kind: Default(
498 Box::new(Self::apply_expr_global_change(target_id, new_expr, left)),
499 Box::new(Self::apply_expr_global_change(target_id, new_expr, right)),
500 ),
501 span,
502 ..*ast_expr
503 },
504 DiscreteWindowAggregation {
505 expr: left,
506 duration: right,
507 wait,
508 aggregation,
509 ..
510 } => Expression {
511 kind: DiscreteWindowAggregation {
512 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, left)),
513 duration: Box::new(Self::apply_expr_global_change(target_id, new_expr, right)),
514 wait: *wait,
515 aggregation: *aggregation,
516 },
517 span,
518 ..*ast_expr
519 },
520 SlidingWindowAggregation {
521 expr: left,
522 duration: right,
523 wait,
524 aggregation,
525 } => Expression {
526 kind: SlidingWindowAggregation {
527 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, left)),
528 duration: Box::new(Self::apply_expr_global_change(target_id, new_expr, right)),
529 wait: *wait,
530 aggregation: *aggregation,
531 },
532 span,
533 ..*ast_expr
534 },
535 InstanceAggregation {
536 expr,
537 selection,
538 aggregation,
539 } => {
540 let selection = match selection {
541 InstanceSelection::Fresh => InstanceSelection::Fresh,
542 InstanceSelection::All => InstanceSelection::All,
543 InstanceSelection::FilteredFresh(LambdaExpr {
544 parameters,
545 expr: cond,
546 }) => InstanceSelection::FilteredFresh(LambdaExpr {
547 parameters: parameters.clone(),
548 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, cond)),
549 }),
550 InstanceSelection::FilteredAll(LambdaExpr {
551 parameters,
552 expr: cond,
553 }) => InstanceSelection::FilteredAll(LambdaExpr {
554 parameters: parameters.clone(),
555 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, cond)),
556 }),
557 };
558 Expression {
559 kind: InstanceAggregation {
560 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, expr)),
561 selection,
562 aggregation: *aggregation,
563 },
564 span,
565 ..*ast_expr
566 }
567 }
568 Ite(condition, normal, alternative) => Expression {
569 kind: Ite(
570 Box::new(Self::apply_expr_global_change(
571 target_id, new_expr, condition,
572 )),
573 Box::new(Self::apply_expr_global_change(target_id, new_expr, normal)),
574 Box::new(Self::apply_expr_global_change(
575 target_id,
576 new_expr,
577 alternative,
578 )),
579 ),
580 span,
581 ..*ast_expr
582 },
583 Tuple(entries) => Expression {
584 kind: Tuple(
585 entries
586 .iter()
587 .map(|t_expr| Self::apply_expr_global_change(target_id, new_expr, t_expr))
588 .collect(),
589 ),
590 span,
591 ..*ast_expr
592 },
593 Function(name, types, entries) => Expression {
594 kind: Function(
595 name.clone(),
596 types.clone(),
597 entries
598 .iter()
599 .map(|t_expr| Self::apply_expr_global_change(target_id, new_expr, t_expr))
600 .collect(),
601 ),
602 span,
603 ..*ast_expr
604 },
605 Method(base, name, types, arguments) => Expression {
606 kind: Method(
607 Box::new(Self::apply_expr_global_change(target_id, new_expr, base)),
608 name.clone(),
609 types.clone(),
610 arguments
611 .iter()
612 .map(|t_expr| Self::apply_expr_global_change(target_id, new_expr, t_expr))
613 .collect(),
614 ),
615 span,
616 ..*ast_expr
617 },
618 Lambda(LambdaExpr {
619 parameters,
620 expr: cond,
621 }) => Expression {
622 kind: Lambda(LambdaExpr {
623 parameters: parameters.clone(),
624 expr: Box::new(Self::apply_expr_global_change(target_id, new_expr, cond)),
625 }),
626 ..*ast_expr
627 },
628 }
629 }
630
631 #[allow(clippy::borrowed_box)]
635 fn desugarize_expression(
636 ast_expr: Expression,
637 ast: &RtLolaAst,
638 current_sugar: &Box<dyn SynSugar>,
639 stream: usize,
640 origin: ExprOrigin,
641 ) -> Result<(Expression, ChangeSet), RtLolaError> {
642 let mut return_cs = ChangeSet::empty();
643 use ExpressionKind::*;
644 let Expression { kind, id, span } = ast_expr;
645 let new_expr = match kind {
646 Lit(_) | Ident(_) | MissingExpression => Expression { kind, id, span },
647 Unary(op, inner) => {
648 let (inner, cs) =
649 Self::desugarize_expression(*inner, ast, current_sugar, stream, origin)?;
650 return_cs += cs;
651 Expression {
652 kind: Unary(op, Box::new(inner)),
653 span,
654 id,
655 }
656 }
657 Field(inner, ident) => {
658 let (inner, cs) =
659 Self::desugarize_expression(*inner, ast, current_sugar, stream, origin)?;
660 return_cs += cs;
661 Expression {
662 kind: Field(Box::new(inner), ident),
663 span,
664 id,
665 }
666 }
667 StreamAccess(inner, acc_kind) => {
668 let (inner, cs) =
669 Self::desugarize_expression(*inner, ast, current_sugar, stream, origin)?;
670 return_cs += cs;
671 Expression {
672 kind: StreamAccess(Box::new(inner), acc_kind),
673 span,
674 id,
675 }
676 }
677 Offset(inner, offset) => {
678 let (inner, cs) =
679 Self::desugarize_expression(*inner, ast, current_sugar, stream, origin)?;
680 return_cs += cs;
681 Expression {
682 kind: Offset(Box::new(inner), offset),
683 span,
684 id,
685 }
686 }
687 ParenthesizedExpression(inner) => {
688 let (inner, cs) =
689 Self::desugarize_expression(*inner, ast, current_sugar, stream, origin)?;
690 return_cs += cs;
691 Expression {
692 kind: ParenthesizedExpression(Box::new(inner)),
693 span,
694 id,
695 }
696 }
697 Binary(bin_op, left, right) => {
698 let (left, lcs) =
699 Self::desugarize_expression(*left, ast, current_sugar, stream, origin)?;
700 return_cs += lcs;
701 let (right, rcs) =
702 Self::desugarize_expression(*right, ast, current_sugar, stream, origin)?;
703 return_cs += rcs;
704 Expression {
705 kind: Binary(bin_op, Box::new(left), Box::new(right)),
706 span,
707 id,
708 }
709 }
710 Default(left, right) => {
711 let (left, lcs) =
712 Self::desugarize_expression(*left, ast, current_sugar, stream, origin)?;
713 return_cs += lcs;
714 let (right, rcs) =
715 Self::desugarize_expression(*right, ast, current_sugar, stream, origin)?;
716 return_cs += rcs;
717 Expression {
718 kind: Default(Box::new(left), Box::new(right)),
719 span,
720 id,
721 }
722 }
723 DiscreteWindowAggregation {
724 expr: left,
725 duration: right,
726 wait,
727 aggregation,
728 ..
729 } => {
730 let (expr, ecs) =
731 Self::desugarize_expression(*left, ast, current_sugar, stream, origin)?;
732 return_cs += ecs;
733 let (dur, dcs) =
734 Self::desugarize_expression(*right, ast, current_sugar, stream, origin)?;
735 return_cs += dcs;
736 Expression {
737 kind: DiscreteWindowAggregation {
738 expr: Box::new(expr),
739 duration: Box::new(dur),
740 wait,
741 aggregation,
742 },
743 span,
744 id,
745 }
746 }
747 SlidingWindowAggregation {
748 expr: left,
749 duration: right,
750 wait,
751 aggregation,
752 } => {
753 let (expr, ecs) =
754 Self::desugarize_expression(*left, ast, current_sugar, stream, origin)?;
755 return_cs += ecs;
756 let (dur, dcs) =
757 Self::desugarize_expression(*right, ast, current_sugar, stream, origin)?;
758 return_cs += dcs;
759 Expression {
760 kind: SlidingWindowAggregation {
761 expr: Box::new(expr),
762 duration: Box::new(dur),
763 wait,
764 aggregation,
765 },
766 span,
767 id,
768 }
769 }
770 InstanceAggregation {
771 expr,
772 selection,
773 aggregation,
774 } => {
775 let (expr, ecs) =
776 Self::desugarize_expression(*expr, ast, current_sugar, stream, origin)?;
777 let (selection, scs) = match selection {
778 selection @ (InstanceSelection::Fresh | InstanceSelection::All) => {
779 (selection, ChangeSet::empty())
780 }
781 InstanceSelection::FilteredFresh(LambdaExpr {
782 parameters,
783 expr: cond,
784 }) => {
785 let (cond, cs) =
786 Self::desugarize_expression(*cond, ast, current_sugar, stream, origin)?;
787 (
788 InstanceSelection::FilteredFresh(LambdaExpr {
789 parameters,
790 expr: Box::new(cond),
791 }),
792 cs,
793 )
794 }
795 InstanceSelection::FilteredAll(LambdaExpr {
796 parameters,
797 expr: cond,
798 }) => {
799 let (cond, cs) =
800 Self::desugarize_expression(*cond, ast, current_sugar, stream, origin)?;
801 (
802 InstanceSelection::FilteredAll(LambdaExpr {
803 parameters,
804 expr: Box::new(cond),
805 }),
806 cs,
807 )
808 }
809 };
810 return_cs = return_cs + ecs + scs;
811 Expression {
812 kind: InstanceAggregation {
813 expr: Box::new(expr),
814 selection,
815 aggregation,
816 },
817 span,
818 id,
819 }
820 }
821 Ite(condition, normal, alternative) => {
822 let (condition, ccs) =
823 Self::desugarize_expression(*condition, ast, current_sugar, stream, origin)?;
824 return_cs += ccs;
825 let (normal, ncs) =
826 Self::desugarize_expression(*normal, ast, current_sugar, stream, origin)?;
827 return_cs += ncs;
828 let (alternative, acs) =
829 Self::desugarize_expression(*alternative, ast, current_sugar, stream, origin)?;
830 return_cs += acs;
831 Expression {
832 kind: Ite(Box::new(condition), Box::new(normal), Box::new(alternative)),
833 span,
834 id,
835 }
836 }
837 Tuple(entries) => {
838 let (v_expr, v_cs): (Vec<Expression>, Vec<ChangeSet>) = entries
839 .into_iter()
840 .map(|t_expr| {
841 Self::desugarize_expression(t_expr, ast, current_sugar, stream, origin)
842 })
843 .collect::<Result<_, _>>()?;
844 return_cs += v_cs.into_iter().fold(ChangeSet::empty(), |acc, x| acc + x);
845 Expression {
846 kind: Tuple(v_expr),
847 span,
848 id,
849 }
850 }
851 Function(name, types, entries) => {
852 let (v_expr, v_cs): (Vec<Expression>, Vec<ChangeSet>) = entries
853 .into_iter()
854 .map(|t_expr| {
855 Self::desugarize_expression(t_expr, ast, current_sugar, stream, origin)
856 })
857 .collect::<Result<_, _>>()?;
858 return_cs += v_cs.into_iter().fold(ChangeSet::empty(), |acc, x| acc + x);
859 Expression {
860 kind: Function(name, types, v_expr),
861 span,
862 id,
863 }
864 }
865 Method(base, name, types, arguments) => {
866 let (base_expr, ecs) =
867 Self::desugarize_expression(*base, ast, current_sugar, stream, origin)?;
868 return_cs += ecs;
869 let (v_expr, v_cs): (Vec<Expression>, Vec<ChangeSet>) = arguments
870 .into_iter()
871 .map(|t_expr| {
872 Self::desugarize_expression(t_expr, ast, current_sugar, stream, origin)
873 })
874 .collect::<Result<_, _>>()?;
875 return_cs += v_cs.into_iter().fold(ChangeSet::empty(), |acc, x| acc + x);
876 Expression {
877 kind: Method(Box::new(base_expr), name, types, v_expr),
878 span,
879 id,
880 }
881 }
882 Lambda(LambdaExpr {
883 parameters,
884 expr: cond,
885 }) => {
886 let (cond, ecs) =
887 Self::desugarize_expression(*cond, ast, current_sugar, stream, origin)?;
888 return_cs += ecs;
889 Expression {
890 kind: Lambda(LambdaExpr {
891 parameters,
892 expr: Box::new(cond),
893 }),
894 span,
895 id,
896 }
897 }
898 };
899
900 let mut current_level_cs = current_sugar.desugarize_expr(&new_expr, ast, stream, origin)?;
902 let return_expr = if let Some(LocalChangeInstruction::ReplaceExpr(replace_expr)) =
903 current_level_cs.extract_local_change()
904 {
905 replace_expr
906 } else {
907 new_expr
908 };
909 let final_cs = current_level_cs + return_cs;
910
911 Ok((return_expr, final_cs))
912 }
913
914 #[allow(clippy::borrowed_box)]
915 fn desugarize_input(
916 &self,
917 input: &Input,
918 ast: &RtLolaAst,
919 current_sugar: &Box<dyn SynSugar>,
920 ) -> Result<ChangeSet, RtLolaError> {
921 current_sugar.desugarize_stream_in(input, ast)
922 }
923
924 #[allow(clippy::borrowed_box)]
925 fn desugarize_mirror(
926 &self,
927 mirror: &AstMirror,
928 ast: &RtLolaAst,
929 current_sugar: &Box<dyn SynSugar>,
930 ) -> Result<ChangeSet, RtLolaError> {
931 current_sugar.desugarize_stream_mirror(mirror, ast)
932 }
933
934 #[allow(clippy::borrowed_box)]
935 fn desugarize_output(
936 &self,
937 output: &Output,
938 ast: &RtLolaAst,
939 current_sugar: &Box<dyn SynSugar>,
940 ) -> Result<ChangeSet, RtLolaError> {
941 current_sugar.desugarize_stream_out(output, ast)
942 }
943}
944
945impl Default for Desugarizer {
946 fn default() -> Self {
947 Self::all()
948 }
949}
950
951impl ChangeSet {
952 fn empty() -> Self {
954 Self {
955 _local_applied_flag: false,
956 local_instructions: None,
957 global_instructions: HashSet::new(),
958 }
959 }
960
961 #[allow(dead_code)] pub(crate) fn add_output(stream: Output) -> Self {
964 let mut cs = ChangeSet::empty();
965 cs.global_instructions
966 .insert(ChangeInstruction::AddOutput(Box::new(stream)));
967 cs
968 }
969
970 #[allow(dead_code)] pub(crate) fn replace_expression(target_id: NodeId, expr: Expression) -> Self {
973 let mut cs = Self::empty();
974 cs.global_instructions
975 .insert(ChangeInstruction::ReplaceExpr(target_id, expr));
976 cs
977 }
978
979 #[allow(dead_code)] pub(crate) fn remove_stream(target_id: NodeId) -> Self {
982 let mut cs = Self::empty();
983 cs.global_instructions
984 .insert(ChangeInstruction::RemoveStream(target_id));
985 cs
986 }
987
988 pub(crate) fn replace_current_expression(expr: Expression) -> Self {
992 let mut cs = Self::empty();
993 cs.local_instructions = Some(LocalChangeInstruction::ReplaceExpr(expr));
994 cs._local_applied_flag = true;
995 cs
996 }
997
998 pub(crate) fn replace_stream(target_stream: NodeId, new_stream: Output) -> Self {
1000 let mut cs = ChangeSet::empty();
1001 cs.global_instructions
1002 .insert(ChangeInstruction::RemoveStream(target_stream));
1003 cs.global_instructions
1004 .insert(ChangeInstruction::AddOutput(Box::new(new_stream)));
1005 cs
1006 }
1007
1008 fn global_iter<'a>(self) -> Box<dyn Iterator<Item = ChangeInstruction> + 'a> {
1010 Box::new(self.global_instructions.into_iter().sorted())
1011 }
1012
1013 fn extract_local_change(&mut self) -> Option<LocalChangeInstruction> {
1015 let mut ret = None;
1016 std::mem::swap(&mut self.local_instructions, &mut ret);
1017 ret
1018 }
1019}
1020
1021impl std::ops::Add<ChangeSet> for ChangeSet {
1022 type Output = ChangeSet;
1023
1024 fn add(self, rhs: Self) -> Self {
1025 let set = self
1026 .global_instructions
1027 .union(&rhs.global_instructions)
1028 .cloned()
1029 .collect();
1030 assert!(self.local_instructions.is_none() || rhs.local_instructions.is_none());
1031 let local = self.local_instructions.or(rhs.local_instructions);
1032 Self {
1033 _local_applied_flag: self._local_applied_flag || rhs._local_applied_flag,
1034 local_instructions: local,
1035 global_instructions: set,
1036 }
1037 }
1038}
1039
1040impl std::ops::AddAssign<ChangeSet> for ChangeSet {
1041 fn add_assign(&mut self, rhs: Self) {
1042 let set = self
1043 .global_instructions
1044 .union(&rhs.global_instructions)
1045 .cloned()
1046 .collect();
1047 assert!(self.local_instructions.is_none() || rhs.local_instructions.is_none());
1048 let local = self.local_instructions.clone().or(rhs.local_instructions);
1049 *self = Self {
1050 _local_applied_flag: self._local_applied_flag || rhs._local_applied_flag,
1051 local_instructions: local,
1052 global_instructions: set,
1053 }
1054 }
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use super::*;
1060 use crate::ast::{BinOp, Ident, LitKind, Literal, Offset, UnOp, WindowOperation};
1061
1062 #[test]
1063 fn test_impl_simpl_replace() {
1064 let spec = "input a:Bool\ninput b:Bool\noutput c eval with a -> b".to_string();
1065 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1066 let out_kind = ast.outputs[0].eval[0]
1067 .clone()
1068 .eval_expression
1069 .unwrap()
1070 .kind
1071 .clone();
1072 let inner_kind = if let ExpressionKind::Binary(op, lhs, _rhs) = out_kind {
1073 assert!(matches!(op, BinOp::Or));
1074 lhs.kind
1075 } else {
1076 unreachable!()
1077 };
1078 assert!(matches!(inner_kind, ExpressionKind::Unary(UnOp::Not, _)));
1079 }
1080
1081 #[test]
1082 fn test_impl_nested_replace() {
1083 let spec =
1084 "input a:Bool\ninput b:Bool\ninput c:Bool\noutput d eval with a -> b -> c".to_string();
1085 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1086 let out_kind = ast.outputs[0].eval[0]
1087 .clone()
1088 .eval_expression
1089 .unwrap()
1090 .kind
1091 .clone();
1092 let inner_kind = if let ExpressionKind::Binary(op, lhs, rhs) = out_kind {
1093 assert!(matches!(op, BinOp::Or));
1094 let inner = if let ExpressionKind::Unary(op, inner) = lhs.kind {
1095 assert!(matches!(op, UnOp::Not));
1096 inner
1097 } else {
1098 unreachable!()
1099 };
1100 assert_eq!(inner.to_string(), "a");
1101 rhs.kind
1102 } else {
1103 unreachable!();
1104 };
1105 let inner = if let ExpressionKind::Binary(op, lhs, rhs) = inner_kind {
1106 assert!(matches!(op, BinOp::Or));
1107 let inner = if let ExpressionKind::Unary(op, inner) = lhs.kind {
1108 assert!(matches!(op, UnOp::Not));
1109 inner
1110 } else {
1111 unreachable!()
1112 };
1113 assert_eq!(inner.to_string(), "b");
1114 rhs
1115 } else {
1116 unreachable!()
1117 };
1118 assert_eq!(inner.to_string(), "c");
1119 }
1120
1121 #[test]
1122 fn test_offsetor_replace() {
1123 let spec = "output x eval @5Hz with x.offset(by: -4, or: 5.0)".to_string();
1124 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1125 let out_kind = ast.outputs[0].eval[0]
1126 .clone()
1127 .eval_expression
1128 .unwrap()
1129 .kind
1130 .clone();
1131 let inner_kind = if let ExpressionKind::Default(inner, default) = out_kind {
1132 assert!(matches!(default.kind, ExpressionKind::Lit(_)));
1133 inner.kind
1134 } else {
1135 unreachable!()
1136 };
1137 assert!(matches!(
1138 inner_kind,
1139 ExpressionKind::Offset(_, Offset::Discrete(-4))
1140 ));
1141 }
1142
1143 #[test]
1144 fn test_offsetor_replace_nested() {
1145 let spec =
1146 "output x eval @5Hz with -x.offset(by: -4, or: x.offset(by: -1, or: 5))".to_string();
1147 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1148 let out_kind = ast.outputs[0].eval[0]
1149 .clone()
1150 .eval_expression
1151 .unwrap()
1152 .kind
1153 .clone();
1154 let inner_kind = if let ExpressionKind::Unary(UnOp::Neg, inner) = out_kind {
1155 inner.kind
1156 } else {
1157 unreachable!()
1158 };
1159 let (inner_kind, default_kind) = if let ExpressionKind::Default(inner, default) = inner_kind
1160 {
1161 (inner.kind, default.kind)
1162 } else {
1163 unreachable!()
1164 };
1165 assert!(matches!(
1166 inner_kind,
1167 ExpressionKind::Offset(_, Offset::Discrete(-4))
1168 ));
1169 let inner_kind = if let ExpressionKind::Default(inner, default) = default_kind {
1170 assert!(matches!(default.kind, ExpressionKind::Lit(_)));
1171 inner.kind
1172 } else {
1173 unreachable!()
1174 };
1175 assert!(matches!(
1176 inner_kind,
1177 ExpressionKind::Offset(_, Offset::Discrete(-1))
1178 ));
1179 }
1180
1181 #[test]
1182 fn test_aggr_replace() {
1183 let spec = "output x eval @5Hz with x.count(6s)".to_string();
1184 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1185 assert!(matches!(
1186 ast.outputs[0].eval[0].clone().eval_expression.unwrap().kind,
1187 ExpressionKind::SlidingWindowAggregation {
1188 aggregation: WindowOperation::Count,
1189 ..
1190 }
1191 ));
1192 }
1193
1194 #[test]
1195 fn test_aggr_replace_nested() {
1196 let spec = "output x eval @ 5hz with -x.sum(6s)".to_string();
1197 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1198 let out_kind = ast.outputs[0].eval[0]
1199 .clone()
1200 .eval_expression
1201 .unwrap()
1202 .kind
1203 .clone();
1204 assert!(matches!(out_kind, ExpressionKind::Unary(UnOp::Neg, _)));
1205 let inner_kind = if let ExpressionKind::Unary(UnOp::Neg, inner) = out_kind {
1206 inner.kind
1207 } else {
1208 unreachable!()
1209 };
1210 assert!(matches!(
1211 inner_kind,
1212 ExpressionKind::SlidingWindowAggregation {
1213 aggregation: WindowOperation::Sum,
1214 ..
1215 }
1216 ));
1217 }
1218
1219 #[test]
1220 fn test_aggr_replace_multiple() {
1221 let spec = "output x eval @5hz with x.avg(5s) - x.integral(2.5s)".to_string();
1222 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1223 let out_kind = ast.outputs[0].eval[0]
1224 .clone()
1225 .eval_expression
1226 .unwrap()
1227 .kind
1228 .clone();
1229 assert!(matches!(out_kind, ExpressionKind::Binary(BinOp::Sub, _, _)));
1230 let (left, right) = if let ExpressionKind::Binary(BinOp::Sub, left, right) = out_kind {
1231 (left.kind, right.kind)
1232 } else {
1233 unreachable!()
1234 };
1235 assert!(matches!(
1236 left,
1237 ExpressionKind::SlidingWindowAggregation {
1238 aggregation: WindowOperation::Average,
1239 ..
1240 }
1241 ));
1242 assert!(matches!(
1243 right,
1244 ExpressionKind::SlidingWindowAggregation {
1245 aggregation: WindowOperation::Integral,
1246 ..
1247 }
1248 ));
1249 }
1250
1251 #[test]
1252 fn test_last_replace() {
1253 let spec = "output x eval @5hz with x.last(or: 3)".to_string();
1254 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1255 let out_kind = ast.outputs[0].eval[0]
1256 .clone()
1257 .eval_expression
1258 .unwrap()
1259 .kind
1260 .clone();
1261 let (access, dft) = if let ExpressionKind::Default(access, dft) = out_kind {
1262 (access.kind, dft.kind)
1263 } else {
1264 panic!("Last should result in a top-level default access with its argument as default value")
1265 };
1266 assert!(
1267 matches!(
1268 &dft,
1269 &ExpressionKind::Lit(Literal {
1270 kind: LitKind::Numeric(ref x, None),
1271 ..
1272 }) if x == &String::from("3")
1273 ),
1274 "The argument of last should be the default expression."
1275 );
1276 let stream = if let ExpressionKind::Offset(stream, Offset::Discrete(-1)) = access {
1277 stream
1278 } else {
1279 panic!("expected an offset expression, but found {:?}", access);
1280 };
1281
1282 assert!(
1283 matches!(*stream, Expression { kind: ExpressionKind::Ident(Ident { name, .. }), ..} if name == String::from("x") )
1284 );
1285 }
1286
1287 #[test]
1288 fn test_delta_replace() {
1289 let spec = "output y eval with delta(x,dft:0)".to_string();
1290 let expected = "output y eval with x - x.offset(by: -1).defaults(to: 0)";
1291 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1292 assert_eq!(expected, format!("{}", ast).trim());
1293 }
1294
1295 #[test]
1296 fn test_delta_replace_float() {
1297 let spec = "output y eval with delta(x, or: 0.0)".to_string();
1298 let expected = "output y eval with x - x.offset(by: -1).defaults(to: 0.0)";
1299 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1300 assert_eq!(expected, format!("{}", ast).trim());
1301 }
1302
1303 #[test]
1304 fn test_mirror_replace_str_cmp() {
1305 let spec = "output x eval with 3 \noutput y mirrors x when x > 5".to_string();
1306 let expected = "output x eval with 3\noutput y eval when x > 5 with 3";
1307 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1308 assert_eq!(expected, format!("{}", ast).trim());
1309 }
1310
1311 #[test]
1312 fn test_mirror_replace_multiple_eval() {
1313 let spec = "output x eval when a > 0 with 3 eval when a < 0 with -3\noutput y mirrors x when x > 5".to_string();
1314 let expected = "output x eval when a > 0 with 3 eval when a < 0 with -3\noutput y eval when a > 0 ∧ x > 5 with 3 eval when a < 0 ∧ x > 5 with -3";
1315 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1316 assert_eq!(expected, format!("{}", ast).trim());
1317 }
1318
1319 #[test]
1320 fn test_true_ratio_aggregation() {
1321 let spec = "input a: Bool\n\
1322 output b eval @1Hz with a.aggregate(over: 1s, using: true_ratio)\n"
1323 .to_string();
1324 let expected = "input a: Bool\n\
1325 output b eval @1Hz with a'.aggregate(over: 1s, using: avg)\n\
1326 output a' eval with if a then 1.0 else 0.0";
1327 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1328 assert_eq!(expected, format!("{}", ast).trim());
1329 }
1330
1331 #[test]
1332 fn test_true_ratio_aggregation_from_output() {
1333 let spec = "input a: Bool\n\
1334 output b eval with a\n\
1335 output c eval @1Hz with b.aggregate(over: 1s, using: true_ratio)\n"
1336 .to_string();
1337 let expected = "input a: Bool\n\
1338 output b eval with a\n\
1339 output c eval @1Hz with b'.aggregate(over: 1s, using: avg)\n\
1340 output b' eval with if b then 1.0 else 0.0";
1341 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1342 assert_eq!(expected, format!("{}", ast).trim());
1343 }
1344
1345 #[test]
1346 fn test_true_ratio_parameterized() {
1347 let spec = "input a: Bool\n\
1348 output b (p) spawn with a eval @1Hz with a.aggregate(over: 1s, using: true_ratio)\n"
1349 .to_string();
1350 let expected = "input a: Bool\n\
1351 output b (p) spawn with a eval @1Hz with a'.aggregate(over: 1s, using: avg)\n\
1352 output a' eval with if a then 1.0 else 0.0";
1353 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1354 assert_eq!(expected, format!("{}", ast).trim());
1355 }
1356
1357 #[test]
1358 fn test_true_ratio_parameterized_2() {
1359 let spec = "input a: Bool\n\
1360 output b (p) spawn with a eval when a = p with a\n\
1361 output c (p) spawn with a eval @1Hz with b(p).aggregate(over: 1s, using: true_ratio)\n"
1362 .to_string();
1363 let expected = "input a: Bool\n\
1364 output b (p) spawn with a eval when a = p with a\n\
1365 output c (p) spawn with a eval @1Hz with b'(p).aggregate(over: 1s, using: avg)\n\
1366 output b' (p) spawn with a eval when a = p with if b(p) then 1.0 else 0.0";
1367 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1368 assert_eq!(expected, format!("{}", ast).trim());
1369 }
1370
1371 #[test]
1372 fn test_true_ratio_instances() {
1373 let spec = "input a: UInt8\n\
1374 output a' (p) spawn @a with a eval @a when a = p with a + p > 5\n\
1375 output b eval @1Hz with a'.aggregate(over_instances: all, using: true_ratio)\n"
1376 .to_string();
1377 let expected = "input a: UInt8\n\
1378 output a' (p) spawn @a with a eval @a when a = p with a + p > 5\n\
1379 output b eval @1Hz with a''.aggregate(over_instances: all, using: avg)\n\
1380 output a'' (p) spawn @a with a eval @a when a = p with if a'(p) then 1.0 else 0.0";
1381 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1382 assert_eq!(expected, format!("{}", ast).trim());
1383 }
1384
1385 #[test]
1386 fn test_true_ratio_name() {
1387 let spec = "input a: Boolean\n\
1388 input a': Boolean\n\
1389 input a'': Boolean\n\
1390 input a''': Boolean\n\
1391 output b eval @1Hz with a'.aggregate(over: 1s, using: true_ratio)"
1392 .to_string();
1393 let expected = "input a: Boolean\n\
1394 input a': Boolean\n\
1395 input a'': Boolean\n\
1396 input a''': Boolean\n\
1397 output b eval @1Hz with a''''.aggregate(over: 1s, using: avg)\n\
1398 output a'''' eval with if a' then 1.0 else 0.0";
1399 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1400 assert_eq!(expected, format!("{}", ast).trim());
1401 }
1402
1403 #[test]
1404 fn test_probability() {
1405 let spec = "input a : Bool\n\
1406 input b : Bool\n\
1407 output c := prob(of: a, given: b)"
1408 .to_string();
1409 let expected = "input a: Bool
1410input b: Bool
1411output c eval with if count_given' = 0.0 then 0.0 else count_both' / count_given'
1412output count_both' eval with count_both'.offset(by: -1).defaults(to: 0.0) + if a ∧ b then 1.0 else 0.0
1413output count_given' eval with count_given'.offset(by: -1).defaults(to: 0.0) + if b ∧ a = a then 1.0 else 0.0".to_string();
1414 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1415 assert_eq!(expected, format!("{}", ast).trim());
1416 }
1417
1418 #[test]
1419 fn test_probability2() {
1420 let spec = "input a : UInt64\n\
1421 input b : UInt64\n\
1422 output c := prob(of: a > 5, given: b < 10, prior: 0.5, confidence: 2.0)"
1423 .to_string();
1424 let expected = "input a: UInt64
1425input b: UInt64
1426output c eval with if (count_given' + 2.0) = 0.0 then 0.0 else (count_both' + 0.5 * 2.0) / (count_given' + 2.0)
1427output count_both' eval with count_both'.offset(by: -1).defaults(to: 0.0) + if a > 5 ∧ b < 10 then 1.0 else 0.0
1428output count_given' eval with count_given'.offset(by: -1).defaults(to: 0.0) + if b < 10 ∧ a > 5 = a > 5 then 1.0 else 0.0".to_string();
1429 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1430 assert_eq!(expected, format!("{}", ast).trim());
1431 }
1432
1433 #[test]
1434 fn test_probability3() {
1435 let spec = "input a : Bool\n\
1436 output c := prob(of: a)"
1437 .to_string();
1438 let expected = "input a: Bool
1439output c eval with if count_given' = 0.0 then 0.0 else count_both' / count_given'
1440output count_both' eval with count_both'.offset(by: -1).defaults(to: 0.0) + if a then 1.0 else 0.0
1441output count_given' eval with count_given'.offset(by: -1).defaults(to: 0.0) + if a = a then 1.0 else 0.0"
1442 .to_string();
1443 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1444 assert_eq!(expected, format!("{}", ast).trim());
1445 }
1446
1447 #[test]
1448 fn test_probability_spawn_close() {
1449 let spec = "input a : Bool\n\
1450 output c
1451 spawn when a
1452 eval when a == 0 with prob(of: a)
1453 close when a"
1454 .to_string();
1455 let expected = "input a: Bool
1456output c spawn when a eval when a = 0 with if count_given' = 0.0 then 0.0 else count_both' / count_given' close when a
1457output count_both' spawn when a eval when a = 0 with count_both'.offset(by: -1).defaults(to: 0.0) + if a then 1.0 else 0.0 close when a
1458output count_given' spawn when a eval when a = 0 with count_given'.offset(by: -1).defaults(to: 0.0) + if a = a then 1.0 else 0.0 close when a"
1459 .to_string();
1460 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1461 assert_eq!(expected, format!("{}", ast).trim());
1462 }
1463
1464 #[test]
1465 fn test_probability_parameterized() {
1466 let spec = "input a: Bool\ninput i: UInt64
1467output c(p)
1468 spawn with i
1469 eval with prob(of: a, given: p == i)"
1470 .to_string();
1471 let expected = "input a: Bool
1472input i: UInt64
1473output c (p) spawn with i eval with if count_given'(p) = 0.0 then 0.0 else count_both'(p) / count_given'(p)
1474output count_both' (p) spawn with i eval with count_both'(p).offset(by: -1).defaults(to: 0.0) + if a ∧ p = i then 1.0 else 0.0
1475output count_given' (p) spawn with i eval with count_given'(p).offset(by: -1).defaults(to: 0.0) + if p = i ∧ a = a then 1.0 else 0.0"
1476 .to_string();
1477 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1478 assert_eq!(expected, format!("{}", ast).trim());
1479 }
1480
1481 #[test]
1482 fn probability_instance_aggregation_of() {
1483 let spec = "input a : UInt64
1484 output b(p)
1485 spawn with a
1486 eval when p == a with b(p).last(or: 0) + 1
1487 output c := b.prob(of: p => p > 5)
1488 "
1489 .to_string();
1490 let expected = "input a: UInt64
1491output b (p) spawn with a eval when p = a with b(p).offset(by: -1).defaults(to: 0) + 1
1492output c eval with if b.aggregate(over_instances: all, using: #) = 0.0 then 0.0 else b.aggregate(over_instances: all(where: (p) => p > 5), using: #) / b.aggregate(over_instances: all, using: #)".to_string();
1493 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1494 assert_eq!(expected, format!("{}", ast).trim());
1495 }
1496
1497 #[test]
1498 fn probability_instance_aggregation_of_given() {
1499 let spec = "input a : UInt64
1500 output b(p)
1501 spawn with a
1502 eval when p == a with b(p).last(or: 0) + 1
1503 output c := b.prob(of: p => p > 5, given: p => p < 10)
1504 "
1505 .to_string();
1506 let expected = "input a: UInt64
1507output b (p) spawn with a eval when p = a with b(p).offset(by: -1).defaults(to: 0) + 1
1508output c eval with if b.aggregate(over_instances: all(where: (p) => p < 10), using: #) = 0.0 then 0.0 else b.aggregate(over_instances: all(where: (p) => p > 5 ∧ p < 10), using: #) / b.aggregate(over_instances: all(where: (p) => p < 10), using: #)".to_string();
1509 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1510 assert_eq!(expected, format!("{}", ast).trim());
1511 }
1512
1513 #[test]
1514 fn probability_instance_aggregation_of_given_prior() {
1515 let spec = "input a : UInt64
1516 output b(p)
1517 spawn with a
1518 eval when p == a with b(p).last(or: 0) + 1
1519 output c := b.prob(of: p => p > 5, given: p => p < 10, prior: 5.0, confidence: 10.0)
1520 "
1521 .to_string();
1522 let expected = "input a: UInt64
1523output b (p) spawn with a eval when p = a with b(p).offset(by: -1).defaults(to: 0) + 1
1524output c eval with if (b.aggregate(over_instances: all(where: (p) => p < 10), using: #) + 10.0) = 0.0 then 0.0 else (b.aggregate(over_instances: all(where: (p) => p > 5 ∧ p < 10), using: #) + 5.0 * 10.0) / (b.aggregate(over_instances: all(where: (p) => p < 10), using: #) + 10.0)".to_string();
1525 let ast = crate::parse(&crate::ParserConfig::for_string(spec)).unwrap();
1526 assert_eq!(expected, format!("{}", ast).trim());
1527 }
1528}