1use std::{collections::BTreeMap, fmt::Debug};
8
9use anyhow::{Context, Error, anyhow};
10use log::debug;
11use pest::{
12 Parser, Span, error,
13 iterators::{Pair, Pairs},
14 pratt_parser::{Assoc, PrattParser},
15};
16use pest_derive::Parser;
17use taco_model_checker::eltl::{ELTLExpression, ELTLSpecification, ELTLSpecificationBuilder};
18
19use taco_threshold_automaton::{
20 expressions::{
21 Atomic, BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, IntegerOp,
22 IsDeclared, Location, Parameter, Variable,
23 },
24 general_threshold_automaton::{
25 Action, GeneralThresholdAutomaton,
26 builder::{
27 BuilderError, GeneralThresholdAutomatonBuilder,
28 InitializedGeneralThresholdAutomatonBuilder, RuleBuilder,
29 },
30 },
31};
32
33use crate::ParseTAWithLTL;
34
35use super::ParseTA;
36
37#[allow(missing_docs)]
39#[derive(Parser)]
40#[grammar = "./bymc_format.pest"]
41struct PestByMCParser;
42
43lazy_static::lazy_static! {
52 static ref PRATT_PARSER: PrattParser<Rule> = {
53 use pest::pratt_parser::{Op};
54
55 PrattParser::new()
57 .op(Op::infix(Rule::or, Assoc::Left))
58 .op(Op::infix(Rule::implication, Assoc::Right))
59 .op(Op::infix(Rule::and, Assoc::Left))
60 .op(Op::infix(Rule::equal, Assoc::Left) | Op::infix(Rule::n_equal, Assoc::Left))
61 .op(Op::infix(Rule::less_eq, Assoc::Left)
62 | Op::infix(Rule::less, Assoc::Left)
63 | Op::infix(Rule::greater_eq, Assoc::Left)
64 | Op::infix(Rule::greater, Assoc::Left))
65 .op(Op::infix(Rule::add, Assoc::Left) | Op::infix(Rule::sub, Assoc::Left))
66 .op(Op::infix(Rule::mul, Assoc::Left) | Op::infix(Rule::div, Assoc::Left))
67 .op(Op::prefix(Rule::negation))
68 .op(Op::prefix(Rule::not))
69 .op(Op::prefix(Rule::eventually) | Op::prefix(Rule::globally))
70 };
71}
72
73struct ByMCParsingContext<'a> {
75 define_macro: BTreeMap<String, Pair<'a, Rule>>,
77}
78
79impl<'a> ByMCParsingContext<'a> {
80 fn new() -> Self {
81 Self {
82 define_macro: BTreeMap::new(),
83 }
84 }
85
86 fn add_macro(
87 &mut self,
88 name: String,
89 pair: Pair<'a, Rule>,
90 ) -> Result<(), Box<pest::error::Error<()>>> {
91 if self.define_macro.contains_key(&name) {
92 return Err(new_parsing_error(
93 format!("Duplicate macro with name '{}', second definition: ", name),
94 pair.as_span(),
95 ));
96 }
97 self.define_macro.insert(name, pair);
98 Ok(())
99 }
100}
101
102pub struct ByMCParser;
108
109impl ParseTA for ByMCParser {
110 fn parse_ta(&self, input: &str) -> Result<GeneralThresholdAutomaton, Error> {
131 let (ta, _, _) = self.parse_ta_and_return_left_pair(input)?;
132 Ok(ta)
133 }
134}
135
136impl ParseTAWithLTL for ByMCParser {
137 fn parse_ta_and_spec(
138 &self,
139 input: &str,
140 ) -> Result<(GeneralThresholdAutomaton, ELTLSpecification), Error> {
141 let (ta, mut pairs, ctx) = self.parse_ta_and_return_left_pair(input)?;
142
143 let ltl_spec = Self::parse_specification_and_add_expressions(
144 pairs
145 .next()
146 .ok_or_else(|| anyhow!("No specification found"))?,
147 ELTLSpecificationBuilder::new(&ta),
148 &ctx,
149 )?;
150 let ltl_spec = ltl_spec.build();
151
152 Ok((ta, ltl_spec))
153 }
154}
155
156impl Default for ByMCParser {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162impl ByMCParser {
163 pub fn new() -> Self {
164 ByMCParser {}
165 }
166
167 fn parse_ta_and_return_left_pair<'a>(
168 &self,
169 input: &'a str,
170 ) -> Result<
171 (
172 GeneralThresholdAutomaton,
173 Pairs<'a, Rule>,
174 ByMCParsingContext<'a>,
175 ),
176 Error,
177 > {
178 let mut pairs = PestByMCParser::parse(Rule::bymc_spec, input)?;
179
180 let pair = pairs.next().expect("Missing: name of threshold automaton");
181 let name: String = parse_identifier_to_t(&pair);
182 let mut builder = GeneralThresholdAutomatonBuilder::new(name);
183
184 let error_msg_nxt_required = "Missing: parameters of threshold automaton";
185
186 let mut pair = pairs.next().expect(error_msg_nxt_required);
187
188 (builder, pair) =
189 Self::handle_local_vars(&mut pairs, pair, builder, error_msg_nxt_required);
190
191 (builder, pair) =
192 Self::parse_shared_variables(&mut pairs, pair, builder, error_msg_nxt_required)
193 .with_context(|| "Failed to parse shared variables:")?;
194
195 (builder, pair) = Self::parse_and_add_parameters(&mut pairs, pair, builder)
196 .with_context(|| "Failed to parse parameters: ")?;
197
198 let mut ctx = ByMCParsingContext::new();
199 (builder, pair) = Self::parse_and_add_define_macros(&mut pairs, pair, builder, &mut ctx)
200 .with_context(|| "Failed to parse macro definitions: ")?;
201
202 let mut rc = None;
204 if pair.as_rule() == Rule::assumption_list {
205 rc = Some(pair);
206 pair = pairs
207 .next()
208 .expect("Missing: locations of threshold automaton");
209 }
210
211 (builder, pair) = Self::parse_and_add_locations(&mut pairs, pair, builder)
212 .with_context(|| "Failed to parse locations: ")?;
213
214 let mut builder = builder.initialize();
216
217 builder = Self::pares_and_add_potential_resilience_conditions(rc, builder, &ctx)?;
218
219 (builder, pair) =
220 Self::parse_and_add_location_and_var_constraints(&mut pairs, pair, builder, &ctx)?;
221
222 builder = Self::parse_and_add_rules(pair, builder, &ctx)?;
223
224 Ok((builder.build(), pairs, ctx))
225 }
226
227 fn handle_local_vars<'a>(
231 pairs: &mut Pairs<'a, Rule>,
232 mut pair: Pair<'a, Rule>,
233 builder: GeneralThresholdAutomatonBuilder,
234 err_msg: &str,
235 ) -> (GeneralThresholdAutomatonBuilder, Pair<'a, Rule>) {
236 while pair.as_rule() == Rule::local_variables {
237 debug!("Local variables are not supported, skipping them!");
238 pair = pairs.next().expect(err_msg);
239 }
240
241 (builder, pair)
242 }
243
244 fn parse_shared_variables<'a>(
254 pairs: &mut Pairs<'a, Rule>,
255 mut pair: Pair<'a, Rule>,
256 mut builder: GeneralThresholdAutomatonBuilder,
257 err_msg: &str,
258 ) -> Result<(GeneralThresholdAutomatonBuilder, Pair<'a, Rule>), BuilderError> {
259 while pair.as_rule() == Rule::shared_variables {
260 let vars = parse_identifier_list_to_t::<Variable>(pair.into_inner().next().unwrap());
261 builder = builder.with_variables(vars)?;
262
263 pair = pairs.next().expect(err_msg);
264 }
265
266 Ok((builder, pair))
267 }
268
269 fn parse_and_add_parameters<'a>(
273 pairs: &mut Pairs<'a, Rule>,
274 pair: Pair<'a, Rule>,
275 builder: GeneralThresholdAutomatonBuilder,
276 ) -> Result<(GeneralThresholdAutomatonBuilder, Pair<'a, Rule>), BuilderError> {
277 debug_assert!(
278 pair.as_rule() == Rule::parameters,
279 "Got rule {:?} for {}",
280 pair.as_rule(),
281 pair.as_str(),
282 );
283
284 let mut pair = pair;
285 let mut builder = builder;
286
287 while pair.as_rule() == Rule::parameters {
288 let parameters =
289 parse_identifier_list_to_t::<Parameter>(pair.into_inner().next().unwrap());
290
291 builder = builder.with_parameters(parameters)?;
292
293 pair = pairs
294 .next()
295 .expect("Missing: locations of threshold automaton");
296 }
297
298 Ok((builder, pair))
299 }
300
301 fn parse_and_add_define_macros<'a>(
302 pairs: &mut Pairs<'a, Rule>,
303 pair: Pair<'a, Rule>,
304 builder: GeneralThresholdAutomatonBuilder,
305 ctx: &mut ByMCParsingContext<'a>,
306 ) -> Result<(GeneralThresholdAutomatonBuilder, Pair<'a, Rule>), Box<pest::error::Error<()>>>
307 {
308 let mut pair = pair;
309 while pair.as_rule() == Rule::define_statement {
310 let mut inners = pair.into_inner();
311
312 let ident_pair = inners.next().expect("Missing: define identifier");
313 let ident: String = parse_identifier_to_t(&ident_pair);
314
315 let int_pair = inners.next().expect("Missing: define integer expr");
316 ctx.add_macro(ident, int_pair)?;
317
318 pair = pairs.next().expect("Missing: location definition");
319 }
320 Ok((builder, pair))
321 }
322
323 fn parse_and_add_locations<'a>(
327 pairs: &mut Pairs<'a, Rule>,
328 pair: Pair<'a, Rule>,
329 mut builder: GeneralThresholdAutomatonBuilder,
330 ) -> Result<(GeneralThresholdAutomatonBuilder, Pair<'a, Rule>), BuilderError> {
331 debug_assert!(
332 pair.as_rule() == Rule::location_list,
333 "Got rule {:?} for {}",
334 pair.as_rule(),
335 pair.as_str(),
336 );
337
338 let locations = parse_location_list(pair);
339 builder = builder.with_locations(locations)?;
340
341 let pair = pairs.next().expect("Missing: rules of threshold automaton");
342
343 Ok((builder, pair))
344 }
345
346 fn pares_and_add_potential_resilience_conditions(
352 pair: Option<Pair<'_, Rule>>,
353 mut builder: InitializedGeneralThresholdAutomatonBuilder,
354 ctx: &ByMCParsingContext,
355 ) -> Result<InitializedGeneralThresholdAutomatonBuilder, anyhow::Error> {
356 if let Some(pair) = pair {
357 debug_assert!(
358 pair.as_rule() == Rule::assumption_list,
359 "Got rule {:?} for {}",
360 pair.as_rule(),
361 pair.as_str(),
362 );
363
364 let mut pairs = pair.into_inner();
365
366 let _n_rc = parse_integer_const(pairs.next().unwrap());
368
369 let rc = parse_boolean_expr_list(pairs.next().unwrap(), &builder, ctx)
370 .with_context(|| "Failed to parse resilience conditions (assumptions):")?;
371
372 builder = builder
373 .with_resilience_conditions(rc)
374 .with_context(|| "Failed to validate resilience conditions (assumptions): ")?;
375 }
376
377 Ok(builder)
378 }
379
380 fn parse_and_add_location_and_var_constraints<'a>(
387 pairs: &mut Pairs<'a, Rule>,
388 mut pair: Pair<'a, Rule>,
389 mut builder: InitializedGeneralThresholdAutomatonBuilder,
390 ctx: &ByMCParsingContext,
391 ) -> Result<(InitializedGeneralThresholdAutomatonBuilder, Pair<'a, Rule>), anyhow::Error> {
392 if pair.as_rule() == Rule::initial_condition_list {
393 let mut inner_pairs = pair.into_inner();
394
395 let _n_loc_constr = parse_integer_const(inner_pairs.next().unwrap());
397
398 let inner_pair = inner_pairs
399 .next()
400 .expect("Missing: initial location / variable constraints (inits)");
401 debug_assert!(
402 inner_pair.as_rule() == Rule::boolean_expr_list,
403 "Got rule {:?} for {}",
404 inner_pair.as_rule(),
405 inner_pair.as_str(),
406 );
407 inner_pairs = inner_pair.into_inner();
408
409 for init_cond in inner_pairs {
410 let span = init_cond.as_span();
411 let var_cond: Result<BooleanExpression<Variable>, _> =
412 parse_boolean_expr(init_cond.clone(), &builder, ctx);
413
414 if let Ok(var_cond) = var_cond {
415 builder = builder
416 .with_initial_variable_constraint(var_cond)
417 .with_context(
418 || "Failed to validate initial variable constraint (inits): ",
419 )?;
420 continue;
421 }
422
423 let loc_constraint: BooleanExpression<Location> =
424 parse_boolean_expr(init_cond, &builder, ctx)
425 .map_err(|err| {
426 new_parsing_error(format!("Unknown identifier: {err}"), span)
427 })
428 .with_context(
429 || "Failed to parse initial location / variable constraints (inits): ",
430 )?;
431
432 builder = builder
433 .with_initial_location_constraint(loc_constraint)
434 .with_context(
435 || "Failed to validate initial location / variable constraints (inits): ",
436 )?;
437 }
438
439 pair = pairs
440 .next()
441 .expect("Missing: rules of the threshold automaton");
442 }
443
444 Ok((builder, pair))
445 }
446
447 fn parse_and_add_rules(
449 pair: Pair<'_, Rule>,
450 builder: InitializedGeneralThresholdAutomatonBuilder,
451 ctx: &ByMCParsingContext,
452 ) -> Result<InitializedGeneralThresholdAutomatonBuilder, anyhow::Error> {
453 debug_assert!(
454 pair.as_rule() == Rule::rule_list,
455 "Got rule {:?} for {}",
456 pair.as_rule(),
457 pair.as_str()
458 );
459
460 let mut pairs = pair.into_inner();
461
462 let _n_rules = parse_integer_const(pairs.next().unwrap());
464
465 let rules = pairs
466 .map(|r| parse_rule(r, &builder, ctx))
467 .collect::<Result<Vec<_>, _>>()
468 .with_context(|| "Failed to parse rules: ")?;
469
470 builder
471 .with_rules(rules)
472 .with_context(|| "Failed to parse rules: ")
473 }
474
475 fn parse_specification_and_add_expressions<'a>(
477 pair: Pair<'_, Rule>,
478 mut builder: ELTLSpecificationBuilder<'a, GeneralThresholdAutomaton>,
479 ctx: &ByMCParsingContext,
480 ) -> Result<ELTLSpecificationBuilder<'a, GeneralThresholdAutomaton>, anyhow::Error> {
481 debug_assert!(
482 pair.as_rule() == Rule::specification_list,
483 "Got rule {:?} for {}",
484 pair.as_rule(),
485 pair.as_str()
486 );
487
488 let mut pairs = pair.into_inner();
489
490 let _n_specs = parse_integer_const(pairs.next().unwrap());
492
493 let expressions = pairs
494 .map(|spec| parse_ltl_specification(spec, &builder, ctx))
495 .collect::<Result<Vec<_>, Error>>()
496 .with_context(|| "Failed to parse specifications: ")?;
497
498 builder
499 .add_expressions(expressions)
500 .with_context(|| "Specification failed validation: ")?;
501 Ok(builder)
502 }
503}
504
505fn parse_ltl_specification<T>(
510 pair: Pair<'_, Rule>,
511 builder: &T,
512 ctx: &ByMCParsingContext,
513) -> Result<(String, ELTLExpression), Error>
514where
515 T: IsDeclared<Variable> + IsDeclared<Location> + IsDeclared<Parameter>,
516{
517 debug_assert!(
518 pair.as_rule() == Rule::specification,
519 "Expected a specification, got rule {:?} for {}",
520 pair.as_rule(),
521 pair.as_str()
522 );
523
524 let mut pairs = pair.into_inner();
525
526 let name: String = parse_identifier_to_t(&pairs.next().unwrap());
527
528 let expression = parse_ltl_expr(pairs.next().unwrap(), builder, ctx)?;
529
530 Ok((name, expression))
531}
532
533fn parse_ltl_expr<T>(
539 pair: Pair<'_, Rule>,
540 builder: &T,
541 ctx: &ByMCParsingContext,
542) -> Result<ELTLExpression, Box<pest::error::Error<()>>>
543where
544 T: IsDeclared<Variable> + IsDeclared<Location> + IsDeclared<Parameter>,
545{
546 debug_assert!(
547 pair.as_rule() == Rule::ltl_expr,
548 "Expected an LTL expression, got rule {:?} for {}",
549 pair.as_rule(),
550 pair.as_str()
551 );
552
553 let pairs = pair.into_inner();
554 PRATT_PARSER
555 .map_primary(|atom| parse_ltl_atom(atom, builder, ctx))
556 .map_infix(|lhs, op, rhs| {
557 let lhs = lhs?;
558 let rhs = rhs?;
559
560 let op = match op.as_rule() {
561 Rule::and => ELTLExpression::And(Box::new(lhs), Box::new(rhs)),
562 Rule::or => ELTLExpression::Or(Box::new(lhs), Box::new(rhs)),
563 Rule::implication => ELTLExpression::Implies(Box::new(lhs), Box::new(rhs)),
564 _ => unreachable!(
565 "Unknown rule for binary ltl operator {:?}: {}",
566 op.as_rule(),
567 op.as_str()
568 ),
569 };
570
571 Ok(op)
572 })
573 .map_prefix(|op, rhs| {
574 let rhs = rhs?;
575
576 match op.as_rule() {
577 Rule::not => Ok(ELTLExpression::Not(Box::new(rhs))),
578 Rule::globally => Ok(ELTLExpression::Globally(Box::new(rhs))),
579 Rule::eventually => Ok(ELTLExpression::Eventually(Box::new(rhs))),
580 _ => unreachable!(
581 "Unknown rule for unary ltl operator {:?}: {}",
582 op.as_rule(),
583 op.as_str()
584 ),
585 }
586 })
587 .parse(pairs)
588}
589
590fn parse_ltl_atom<T>(
595 pair: Pair<'_, Rule>,
596 builder: &T,
597 ctx: &ByMCParsingContext,
598) -> Result<ELTLExpression, Box<pest::error::Error<()>>>
599where
600 T: IsDeclared<Variable> + IsDeclared<Location> + IsDeclared<Parameter>,
601{
602 debug_assert!(
603 pair.as_rule() == Rule::ltl_atom,
604 "Expected an LTL atom, got rule {:?} for {}",
605 pair.as_rule(),
606 pair.as_str()
607 );
608
609 let mut pairs = pair.into_inner();
610 let pair = pairs.next().unwrap();
611
612 match pair.as_rule() {
613 Rule::ltl_expr => parse_ltl_expr(pair, builder, ctx),
614 Rule::ltl_unary_op => {
615 let op = pair.into_inner().next().unwrap();
616 let expr = pairs
617 .next()
618 .expect("Missing LTL expression after unary operator");
619 let expr = parse_ltl_atom(expr, builder, ctx)?;
620
621 match op.as_rule() {
622 Rule::not => Ok(ELTLExpression::Not(Box::new(expr))),
623 Rule::globally => Ok(ELTLExpression::Globally(Box::new(expr))),
624 Rule::eventually => Ok(ELTLExpression::Eventually(Box::new(expr))),
625 _ => unreachable!(
626 "Unknown rule for unary operator {:?}: {}",
627 op.as_rule(),
628 op.as_str()
629 ),
630 }
631 }
632 Rule::comparison_expr => {
633 let span = pair.as_span();
634
635 if let Ok((lhs, op, rhs)) =
639 parse_comparison_expr::<Parameter, T>(pair.clone(), builder, ctx)
640 {
641 return Ok(ELTLExpression::ParameterExpr(
642 Box::new(lhs),
643 op,
644 Box::new(rhs),
645 ));
646 }
647
648 if let Ok((lhs, op, rhs)) =
649 parse_comparison_expr::<Variable, T>(pair.clone(), builder, ctx)
650 {
651 return Ok(ELTLExpression::VariableExpr(
652 Box::new(lhs),
653 op,
654 Box::new(rhs),
655 ));
656 }
657
658 let (lhs, op, rhs) = parse_comparison_expr(pair, builder, ctx).map_err(|_err| {
659 new_parsing_error("Failed to parse inner ltl expression. This is most likely due to an unknown identifier".to_string(), span)
660 })?;
661 let expr = ELTLExpression::LocationExpr(Box::new(lhs), op, Box::new(rhs));
662 Ok(expr)
663 }
664 Rule::bool_const => {
665 let pair = pair.into_inner().next().unwrap();
666
667 match pair.as_rule() {
668 Rule::bool_true => Ok(ELTLExpression::True),
669 Rule::bool_false => Ok(ELTLExpression::False),
670 _ => unreachable!(
671 "Unknown rule for boolean constant {:?}: {}",
672 pair.as_rule(),
673 pair.as_str()
674 ),
675 }
676 }
677 _ => unreachable!(
678 "Unknown rule for ltl atom {:?}: {}",
679 pair.as_rule(),
680 pair.as_str()
681 ),
682 }
683}
684
685#[inline(always)]
687fn parse_location(pair: Pair<'_, Rule>) -> Location {
688 debug_assert!(
689 pair.as_rule() == Rule::location,
690 "Got rule {:?} for {}",
691 pair.as_rule(),
692 pair.as_str()
693 );
694
695 let id_pair = pair.into_inner().next().unwrap();
696
697 parse_identifier_to_t(&id_pair)
698
699 }
701
702fn parse_location_list(pair: Pair<'_, Rule>) -> Vec<Location> {
704 debug_assert!(
705 pair.as_rule() == Rule::location_list,
706 "Got rule {:?} for {}",
707 pair.as_rule(),
708 pair.as_str()
709 );
710
711 let mut pairs = pair.into_inner();
712
713 let n_locs = parse_integer_const(pairs.next().unwrap());
714
715 let locs = pairs.map(|loc| parse_location(loc)).collect::<Vec<_>>();
716
717 if n_locs != locs.len().try_into().unwrap() {
718 debug!(
719 "Specified number of locations {} does not match found number of locations {}",
720 n_locs,
721 locs.len()
722 )
723 }
724
725 locs
726}
727
728#[inline(always)]
730fn parse_identifier_to_t<T: for<'a> From<&'a str>>(pair: &Pair<'_, Rule>) -> T {
731 debug_assert!(
732 pair.as_rule() == Rule::identifier,
733 "Got rule {:?} for {}",
734 pair.as_rule(),
735 pair.as_str()
736 );
737
738 T::from(pair.as_str())
739}
740
741#[inline(always)]
743fn parse_identifier_list_to_t<T: for<'a> From<&'a str>>(
744 pair: Pair<'_, Rule>,
745) -> impl Iterator<Item = T> {
746 debug_assert!(
747 pair.as_rule() == Rule::identifier_list,
748 "Got rule {:?} for {}",
749 pair.as_rule(),
750 pair.as_str()
751 );
752
753 pair.into_inner().map(|id| parse_identifier_to_t::<T>(&id))
754}
755
756fn parse_reset_expression(pair: Pair<'_, Rule>) -> Vec<Action> {
758 debug_assert!(
759 pair.as_rule() == Rule::reset_expr,
760 "Got rule {:?} for {}",
761 pair.as_rule(),
762 pair.as_str()
763 );
764
765 parse_identifier_list_to_t::<Variable>(pair.into_inner().next().unwrap())
766 .map(Action::new_reset)
767 .collect()
768}
769
770fn parse_assignment_expression<T>(
772 pair: Pair<'_, Rule>,
773 builder: &T,
774 ctx: &ByMCParsingContext,
775) -> Result<Action, Box<pest::error::Error<()>>>
776where
777 T: IsDeclared<Variable> + IsDeclared<Parameter>,
778{
779 debug_assert!(
780 pair.as_rule() == Rule::assignment_expr,
781 "Got rule {:?} for {}",
782 pair.as_rule(),
783 pair.as_str()
784 );
785 let span = pair.as_span();
786
787 let mut pairs = pair.into_inner();
788 let pair = pairs.next().unwrap();
789
790 let var = parse_identifier_to_t(&pair);
791 if !builder.is_declared(&var) {
792 return Err(new_parsing_error(
793 format!("Failed to parse action: Unknown identifier: {var}"),
794 span,
795 ));
796 }
797
798 let expr = parse_integer_expr(pairs.next().unwrap(), builder, ctx)
799 .map_err(|err| new_parsing_error(format!("Failed to parse action: {err}"), span))?;
800
801 Action::new(var, expr)
802 .map_err(|err| new_parsing_error(format!("Failed to parse effect of action: {err}"), span))
803}
804
805fn parse_action_expr<T>(
807 pair: Pair<'_, Rule>,
808 builder: &T,
809 ctx: &ByMCParsingContext,
810) -> Result<Vec<Action>, Box<pest::error::Error<()>>>
811where
812 T: IsDeclared<Variable> + IsDeclared<Parameter>,
813{
814 debug_assert!(
815 pair.as_rule() == Rule::action_expr,
816 "Got rule {:?} for {}",
817 pair.as_rule(),
818 pair.as_str()
819 );
820
821 let pair = pair.into_inner().next().unwrap();
822
823 Ok(match pair.as_rule() {
824 Rule::unchanged_expr => vec![],
825 Rule::reset_expr => parse_reset_expression(pair),
826 Rule::assignment_expr => vec![parse_assignment_expression(pair, builder, ctx)?],
827 _ => unreachable!(
828 "Unknown rule for action expression {:?}: {}",
829 pair.as_rule(),
830 pair.as_str()
831 ),
832 })
833}
834
835#[inline(always)]
837fn parse_action_list<T>(
838 pair: Pair<'_, Rule>,
839 builder: &T,
840 ctx: &ByMCParsingContext,
841) -> Result<Vec<Action>, Box<pest::error::Error<()>>>
842where
843 T: IsDeclared<Variable> + IsDeclared<Parameter>,
844{
845 debug_assert!(
846 pair.as_rule() == Rule::action_list,
847 "Got rule {:?} for {}",
848 pair.as_rule(),
849 pair.as_str()
850 );
851
852 pair.into_inner()
853 .map(|action| parse_action_expr(action, builder, ctx))
854 .try_fold(vec![], |mut acc, x| {
855 acc.append(&mut x?);
856 Ok(acc)
857 })
858}
859
860#[inline(always)]
862fn parse_integer_const(pair: Pair<'_, Rule>) -> u32 {
863 debug_assert!(
864 pair.as_rule() == Rule::integer_const,
865 "Got rule {:?} for {}",
866 pair.as_rule(),
867 pair.as_str()
868 );
869 pair.as_str().parse::<u32>().unwrap()
870}
871
872#[inline(always)]
874fn parse_integer_binary_op(pair: Pair<'_, Rule>) -> IntegerOp {
875 match pair.as_rule() {
876 Rule::add => IntegerOp::Add,
877 Rule::sub => IntegerOp::Sub,
878 Rule::mul => IntegerOp::Mul,
879 Rule::div => IntegerOp::Div,
880 _ => unreachable!(
881 "Unknown rule for operator {:?}: {}",
882 pair.as_rule(),
883 pair.as_str()
884 ),
885 }
886}
887
888fn parse_integer_atom<T, U>(
894 pair: Pair<'_, Rule>,
895 builder: &U,
896 ctx: &ByMCParsingContext,
897) -> Result<IntegerExpression<T>, String>
898where
899 T: Atomic,
900 U: IsDeclared<T> + IsDeclared<Parameter>,
901{
902 debug_assert!(
903 pair.as_rule() == Rule::integer_atom,
904 "Got rule {:?} for {}",
905 pair.as_rule(),
906 pair.as_str()
907 );
908
909 let pair = pair.into_inner().next().unwrap();
910
911 match pair.as_rule() {
912 Rule::integer_const => Ok(IntegerExpression::Const(parse_integer_const(pair))),
913 Rule::identifier => {
914 let ident: String = parse_identifier_to_t(&pair);
915 if ctx.define_macro.contains_key(&ident) {
916 return parse_integer_expr(ctx.define_macro[&ident].clone(), builder, ctx);
917 }
918
919 let atom = parse_identifier_to_t(&pair);
920 if builder.is_declared(&atom) {
921 return Ok(IntegerExpression::Atom(atom));
922 }
923
924 let param = parse_identifier_to_t(&pair);
925 if builder.is_declared(¶m) {
926 return Ok(IntegerExpression::Param(param));
927 }
928
929 Err(format!("Unknown identifier: {}", pair.as_str()))
930 }
931 Rule::integer_expr => parse_integer_expr(pair, builder, ctx),
932 _ => unreachable!(
933 "Unknown integer atom {:#?} : {}",
934 pair.as_rule(),
935 pair.as_str()
936 ),
937 }
938}
939
940fn parse_integer_expr<T, U>(
942 pair: Pair<'_, Rule>,
943 builder: &U,
944 ctx: &ByMCParsingContext,
945) -> Result<IntegerExpression<T>, String>
946where
947 T: Atomic,
948 U: IsDeclared<T> + IsDeclared<Parameter>,
949{
950 debug_assert!(
951 pair.as_rule() == Rule::integer_expr,
952 "Got rule {:?} for {}",
953 pair.as_rule(),
954 pair.as_str()
955 );
956 let pairs = pair.into_inner();
957
958 PRATT_PARSER
959 .map_primary(|atom| parse_integer_atom(atom, builder, ctx))
960 .map_infix(|lhs, op, rhs| {
961 let op = parse_integer_binary_op(op);
962
963 Ok(IntegerExpression::BinaryExpr(
964 Box::new(lhs?),
965 op,
966 Box::new(rhs?),
967 ))
968 })
969 .map_prefix(|op, rhs| match op.as_rule() {
970 Rule::negation => Ok(IntegerExpression::Neg(Box::new(rhs?))),
971 _ => unreachable!(
972 "Unknown integer unary operator {:?}: {}",
973 op.as_rule(),
974 op.as_str()
975 ),
976 })
977 .parse(pairs)
978}
979
980#[inline(always)]
982fn parse_boolean_const<T: Atomic>(pair: Pair<'_, Rule>) -> BooleanExpression<T> {
983 debug_assert!(
984 pair.as_rule() == Rule::bool_const,
985 "Got rule {:?} for {}",
986 pair.as_rule(),
987 pair.as_str()
988 );
989
990 let pair = pair
991 .into_inner()
992 .next()
993 .expect("Expected boolean constant to be non-empty");
994
995 match pair.as_rule() {
996 Rule::bool_true => BooleanExpression::True,
997 Rule::bool_false => BooleanExpression::False,
998 _ => unreachable!(
999 "Unexpected rule for boolean constant {:?}: {}",
1000 pair.as_rule(),
1001 pair.as_str()
1002 ),
1003 }
1004}
1005
1006#[inline(always)]
1008fn parse_boolean_con(pair: Pair<'_, Rule>) -> BooleanConnective {
1009 match pair.as_rule() {
1010 Rule::and => BooleanConnective::And,
1011 Rule::or => BooleanConnective::Or,
1012 _ => unreachable!(
1013 "Unknown rule for boolean connective {:?}: {}",
1014 pair.as_rule(),
1015 pair.as_str()
1016 ),
1017 }
1018}
1019
1020#[inline(always)]
1022fn parse_comparison_op(pair: Pair<'_, Rule>) -> ComparisonOp {
1023 match pair.as_rule() {
1024 Rule::equal => ComparisonOp::Eq,
1025 Rule::n_equal => ComparisonOp::Neq,
1026 Rule::less_eq => ComparisonOp::Leq,
1027 Rule::less => ComparisonOp::Lt,
1028 Rule::greater_eq => ComparisonOp::Geq,
1029 Rule::greater => ComparisonOp::Gt,
1030 _ => unreachable!(
1031 "Unknown rule for comparison operator {:?}: {}",
1032 pair.as_rule(),
1033 pair.as_str()
1034 ),
1035 }
1036}
1037
1038type CompExpr<T> = (IntegerExpression<T>, ComparisonOp, IntegerExpression<T>);
1040
1041fn parse_comparison_expr<T, U>(
1043 pair: Pair<'_, Rule>,
1044 builder: &U,
1045 ctx: &ByMCParsingContext,
1046) -> Result<CompExpr<T>, String>
1047where
1048 T: Atomic,
1049 U: IsDeclared<T> + IsDeclared<Parameter>,
1050{
1051 debug_assert!(
1052 pair.as_rule() == Rule::comparison_expr,
1053 "Got rule {:?} for {}",
1054 pair.as_rule(),
1055 pair.as_str()
1056 );
1057
1058 let mut pairs = pair.into_inner();
1059
1060 let lhs = parse_integer_expr(pairs.next().unwrap(), builder, ctx)?;
1061 let op = parse_comparison_op(pairs.next().unwrap());
1062 let rhs = parse_integer_expr(pairs.next().unwrap(), builder, ctx)?;
1063
1064 Ok((lhs, op, rhs))
1065}
1066
1067fn parse_boolean_expr<T, U>(
1069 pair: Pair<'_, Rule>,
1070 builder: &U,
1071 ctx: &ByMCParsingContext,
1072) -> Result<BooleanExpression<T>, String>
1073where
1074 T: Atomic,
1075 U: IsDeclared<T> + IsDeclared<Parameter>,
1076{
1077 debug_assert!(
1078 pair.as_rule() == Rule::boolean_expr,
1079 "Got rule {:?} for {}",
1080 pair.as_rule(),
1081 pair.as_str()
1082 );
1083
1084 let pairs = pair.into_inner();
1085
1086 PRATT_PARSER
1088 .map_primary(|atom| parse_boolean_atom(atom, builder, ctx))
1089 .map_infix(|lhs, op, rhs| {
1090 let op = parse_boolean_con(op);
1091
1092 Ok(BooleanExpression::BinaryExpression(
1093 Box::new(lhs?),
1094 op,
1095 Box::new(rhs?),
1096 ))
1097 })
1098 .map_prefix(|op, rhs| match op.as_rule() {
1099 Rule::not => Ok(BooleanExpression::Not(Box::new(rhs?))),
1100 _ => unreachable!(
1101 "Unknown boolean unary operator {:?}: {}",
1102 op.as_rule(),
1103 op.as_str()
1104 ),
1105 })
1106 .parse(pairs)
1107}
1108
1109#[inline(always)]
1111fn parse_boolean_atom<T, U>(
1112 pair: Pair<'_, Rule>,
1113 builder: &U,
1114 ctx: &ByMCParsingContext,
1115) -> Result<BooleanExpression<T>, String>
1116where
1117 T: Atomic,
1118 U: IsDeclared<T> + IsDeclared<Parameter>,
1119{
1120 debug_assert!(
1121 pair.as_rule() == Rule::boolean_atom,
1122 "Got rule {:?} for {}",
1123 pair.as_rule(),
1124 pair.as_str()
1125 );
1126
1127 let pair = pair
1128 .into_inner()
1129 .next()
1130 .expect("Expected boolean atom to be non-empty");
1131
1132 match pair.as_rule() {
1133 Rule::bool_const => Ok(parse_boolean_const(pair)),
1134 Rule::boolean_expr => parse_boolean_expr(pair, builder, ctx),
1135 Rule::comparison_expr => {
1136 let (lhs, op, rhs) = parse_comparison_expr(pair, builder, ctx)?;
1137 Ok(BooleanExpression::ComparisonExpression(
1138 Box::new(lhs),
1139 op,
1140 Box::new(rhs),
1141 ))
1142 }
1143 _ => unreachable!(
1144 "Unknown rule for boolean atom {:?}: {}",
1145 pair.as_rule(),
1146 pair.as_str(),
1147 ),
1148 }
1149}
1150
1151#[inline(always)]
1153fn parse_boolean_expr_list<T, U>(
1154 pair: Pair<'_, Rule>,
1155 builder: &U,
1156 ctx: &ByMCParsingContext,
1157) -> Result<Vec<BooleanExpression<T>>, Box<pest::error::Error<()>>>
1158where
1159 T: Atomic,
1160 U: IsDeclared<T> + IsDeclared<Parameter>,
1161{
1162 debug_assert!(
1163 pair.as_rule() == Rule::boolean_expr_list,
1164 "Got rule {:?} for {}",
1165 pair.as_rule(),
1166 pair.as_str()
1167 );
1168
1169 pair.into_inner()
1170 .map(|expr| {
1171 let span = expr.as_span();
1172
1173 parse_boolean_expr(expr, builder, ctx).map_err(|err| new_parsing_error(err, span))
1174 })
1175 .collect()
1176}
1177
1178fn parse_rule<T>(
1180 pair: Pair<'_, Rule>,
1181 builder: &T,
1182 ctx: &ByMCParsingContext,
1183) -> Result<taco_threshold_automaton::general_threshold_automaton::Rule, Box<pest::error::Error<()>>>
1184where
1185 T: IsDeclared<Variable> + IsDeclared<Parameter> + IsDeclared<Location>,
1186{
1187 debug_assert!(
1188 pair.as_rule() == Rule::rule,
1189 "Got rule {:?} for {}",
1190 pair.as_rule(),
1191 pair.as_str()
1192 );
1193
1194 let mut pairs = pair.into_inner();
1195
1196 let id = parse_integer_const(pairs.next().unwrap());
1197
1198 let pair = pairs.next().unwrap();
1199 let source: Location = parse_identifier_to_t(&pair);
1200
1201 let pair = pairs.next().unwrap();
1202 let target: Location = parse_identifier_to_t(&pair);
1203
1204 let mut rule_builder = RuleBuilder::new(id, source, target);
1205
1206 let pair = pairs.next().unwrap();
1207 let span = pair.as_span();
1208
1209 let guard = parse_boolean_expr(pair, builder, ctx).map_err(|err| {
1210 new_parsing_error(format!("Failed to parse guard of rule {id}: {err}"), span)
1211 })?;
1212
1213 rule_builder = rule_builder.with_guard(guard);
1214
1215 let actions = parse_action_list(pairs.next().unwrap(), builder, ctx)?;
1216 rule_builder = rule_builder.with_actions(actions);
1217
1218 Ok(rule_builder.build())
1219}
1220
1221#[inline(always)]
1223fn new_parsing_error(message: String, span: Span<'_>) -> Box<pest::error::Error<()>> {
1224 Box::new(error::Error::new_from_span(
1225 error::ErrorVariant::CustomError { message },
1226 span,
1227 ))
1228}
1229
1230#[cfg(test)]
1231mod test {
1232 use super::*;
1233
1234 mod simple_parse_funcs {
1237 use super::*;
1238
1239 macro_rules! parse_func_test
1240 {
1241 ($name:ident, $($test_spec:expr, $expected_out:expr, $rule:expr, $parse_func:expr),+) => {
1242 #[test]
1243 fn $name() {
1244 $(
1245 let test_spec = $test_spec;
1246 let mut parsed_pairs = PestByMCParser::parse($rule, test_spec).unwrap();
1247 let exp_pair = parsed_pairs.next().unwrap();
1248 let ctx = ByMCParsingContext::new();
1249
1250 let builder = GeneralThresholdAutomatonBuilder::new("test_ta")
1251 .with_parameters(vec![Parameter::new("n"), Parameter::new("f")])
1252 .unwrap()
1253 .with_variables(vec![Variable::new("x"), Variable::new("y"), Variable::new("var1"), Variable::new("var2"), Variable::new("var3")])
1254 .unwrap()
1255 .with_locations(vec![Location::new("loc"), Location::new("loc1"), Location::new("loc2")])
1256 .unwrap()
1257 .initialize();
1258
1259 let got_expr = $parse_func(exp_pair, &builder, &ctx);
1260 let got_expr = got_expr.unwrap();
1261
1262 assert_eq!(got_expr, $expected_out);
1263
1264 assert!(parsed_pairs.next().is_none(), "Found more tokens: {:#?}", parsed_pairs);
1265 )*
1266 }
1267
1268 };
1269 }
1270
1271 #[test]
1272 fn test_parse_identifier_list_to_string() {
1273 let test_spec = "a, b, c, abcdefg, gg, __gg, gg32";
1274 let mut parsed_pairs = PestByMCParser::parse(Rule::identifier_list, test_spec).unwrap();
1275 let pair = parsed_pairs.next().unwrap();
1276
1277 let got_expr: Vec<String> = parse_identifier_list_to_t(pair).collect();
1278
1279 let expected_expr = vec![
1280 "a".to_string(),
1281 "b".to_string(),
1282 "c".to_string(),
1283 "abcdefg".to_string(),
1284 "gg".to_string(),
1285 "__gg".to_string(),
1286 "gg32".to_string(),
1287 ];
1288
1289 assert_eq!(got_expr, expected_expr);
1290
1291 assert!(
1292 parsed_pairs.next().is_none(),
1293 "Found more tokens: {parsed_pairs:#?}"
1294 );
1295 }
1296
1297 #[test]
1298 fn parse_location_list_test() {
1299 let test_spec = "locations (3) {
1300 loc1 : [0];
1301 loc2 : [1];
1302 loc3 : [2];
1303 }
1304 ";
1305
1306 let expected_locs = vec![
1307 Location::new("loc1"),
1308 Location::new("loc2"),
1309 Location::new("loc3"),
1310 ];
1311
1312 print!(
1313 "{:#?}",
1314 PestByMCParser::parse(Rule::location_list, test_spec).err()
1315 );
1316
1317 let mut parsed_pairs = PestByMCParser::parse(Rule::location_list, test_spec).unwrap();
1318 let pair = parsed_pairs.next().unwrap();
1319
1320 let got_locs = parse_location_list(pair);
1321
1322 assert_eq!(expected_locs, got_locs);
1323
1324 assert!(
1325 parsed_pairs.next().is_none(),
1326 "Found more tokens: {parsed_pairs:#?}"
1327 );
1328 }
1329
1330 parse_func_test!(
1331 parse_action_expr_reset,
1332 "reset(x, y, z)",
1333 vec![
1334 Action::new_reset(Variable::new("x")),
1335 Action::new_reset(Variable::new("y")),
1336 Action::new_reset(Variable::new("z")),
1337 ],
1338 Rule::action_expr,
1339 parse_action_expr
1340 );
1341
1342 parse_func_test!(
1343 parse_action_expr_increment,
1344 "x' := x + 1",
1345 vec![
1346 Action::new(
1347 Variable::new("x"),
1348 IntegerExpression::Atom(Variable::new("x")) + IntegerExpression::Const(1),
1349 )
1350 .unwrap()
1351 ],
1352 Rule::action_expr,
1353 parse_action_expr
1354 );
1355
1356 parse_func_test!(
1357 parse_action_expr_unchanged,
1358 "unchanged(x, y, z)",
1359 Vec::<Action>::new(),
1360 Rule::action_expr,
1361 parse_action_expr
1362 );
1363
1364 parse_func_test!(
1365 parse_action_list_mixed,
1366 "reset(a,b,c); x' := x + 1, unchanged(p,q)",
1367 vec![
1368 Action::new_reset(Variable::new("a")),
1369 Action::new_reset(Variable::new("b")),
1370 Action::new_reset(Variable::new("c")),
1371 Action::new(
1372 Variable::new("x"),
1373 IntegerExpression::Atom(Variable::new("x")) + IntegerExpression::Const(1),
1374 )
1375 .unwrap(),
1376 ],
1377 Rule::action_list,
1378 parse_action_list
1379 );
1380
1381 parse_func_test!(
1382 parse_integer_expr_const,
1383 "5",
1384 IntegerExpression::<Location>::Const(5),
1385 Rule::integer_expr,
1386 parse_integer_expr
1387 );
1388
1389 parse_func_test!(
1390 parse_integer_expr_location,
1391 "loc",
1392 IntegerExpression::<Location>::Atom(Location::new("loc")),
1393 Rule::integer_expr,
1394 parse_integer_expr
1395 );
1396
1397 parse_func_test!(
1398 parse_integer_expr_parameter,
1399 "n",
1400 IntegerExpression::<Location>::Param(Parameter::new("n")),
1401 Rule::integer_expr,
1402 parse_integer_expr
1403 );
1404
1405 parse_func_test!(
1406 parse_integer_expr_add,
1407 "5 + 5",
1408 IntegerExpression::<Location>::BinaryExpr(
1409 Box::new(IntegerExpression::Const(5)),
1410 IntegerOp::Add,
1411 Box::new(IntegerExpression::Const(5)),
1412 ),
1413 Rule::integer_expr,
1414 parse_integer_expr
1415 );
1416
1417 parse_func_test!(
1418 parse_integer_operator_precedence_1,
1419 "5 + 5 * 5",
1420 IntegerExpression::<Parameter>::BinaryExpr(
1421 Box::new(IntegerExpression::Const(5)),
1422 IntegerOp::Add,
1423 Box::new(IntegerExpression::BinaryExpr(
1424 Box::new(IntegerExpression::Const(5)),
1425 IntegerOp::Mul,
1426 Box::new(IntegerExpression::Const(5)),
1427 )),
1428 ),
1429 Rule::integer_expr,
1430 parse_integer_expr
1431 );
1432
1433 parse_func_test!(
1434 parse_integer_operator_precedence_2,
1435 "5 / 5 - 5",
1436 IntegerExpression::<Variable>::BinaryExpr(
1437 Box::new(IntegerExpression::BinaryExpr(
1438 Box::new(IntegerExpression::Const(5)),
1439 IntegerOp::Div,
1440 Box::new(IntegerExpression::Const(5)),
1441 )),
1442 IntegerOp::Sub,
1443 Box::new(IntegerExpression::Const(5)),
1444 ),
1445 Rule::integer_expr,
1446 parse_integer_expr
1447 );
1448
1449 parse_func_test!(
1450 parse_integer_un_op,
1451 "-3",
1452 IntegerExpression::<Location>::Neg(Box::new(IntegerExpression::Const(3))),
1453 Rule::integer_expr,
1454 parse_integer_expr
1455 );
1456
1457 parse_func_test!(
1458 parse_integer_mixed,
1459 "-3 + n - loc",
1460 IntegerExpression::BinaryExpr(
1461 Box::new(IntegerExpression::BinaryExpr(
1462 Box::new(IntegerExpression::<Location>::Neg(Box::new(
1463 IntegerExpression::Const(3)
1464 ))),
1465 IntegerOp::Add,
1466 Box::new(IntegerExpression::Param(Parameter::new("n")))
1467 )),
1468 IntegerOp::Sub,
1469 Box::new(IntegerExpression::Atom(Location::new("loc")))
1470 ),
1471 Rule::integer_expr,
1472 parse_integer_expr
1473 );
1474
1475 parse_func_test!(
1476 parse_boolean_const_true,
1477 "true",
1478 BooleanExpression::<Location>::True,
1479 Rule::boolean_expr,
1480 parse_boolean_expr
1481 );
1482
1483 parse_func_test!(
1484 parse_boolean_const_true1,
1485 "1",
1486 BooleanExpression::<Location>::True,
1487 Rule::boolean_expr,
1488 parse_boolean_expr
1489 );
1490
1491 parse_func_test!(
1492 parse_boolean_const_false,
1493 "false",
1494 BooleanExpression::<Location>::False,
1495 Rule::boolean_expr,
1496 parse_boolean_expr
1497 );
1498
1499 parse_func_test!(
1500 parse_boolean_comp_op_1,
1501 "x > 1",
1502 BooleanExpression::ComparisonExpression(
1503 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1504 ComparisonOp::Gt,
1505 Box::new(IntegerExpression::Const(1)),
1506 ),
1507 Rule::boolean_expr,
1508 parse_boolean_expr
1509 );
1510
1511 parse_func_test!(
1512 parse_boolean_comp_op_2,
1513 "x <= 1",
1514 BooleanExpression::ComparisonExpression(
1515 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1516 ComparisonOp::Leq,
1517 Box::new(IntegerExpression::Const(1)),
1518 ),
1519 Rule::boolean_expr,
1520 parse_boolean_expr
1521 );
1522
1523 parse_func_test!(
1524 parse_boolean_comp_op_negated,
1525 "!(x > 1)",
1526 BooleanExpression::Not(Box::new(BooleanExpression::ComparisonExpression(
1527 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1528 ComparisonOp::Gt,
1529 Box::new(IntegerExpression::Const(1)),
1530 ))),
1531 Rule::boolean_expr,
1532 parse_boolean_expr
1533 );
1534
1535 parse_func_test!(
1536 parse_boolean_comp_op_complex,
1537 "var2 >= 1 && var2 < 1 || var3 != n",
1538 BooleanExpression::BinaryExpression(
1539 Box::new(BooleanExpression::BinaryExpression(
1540 Box::new(BooleanExpression::ComparisonExpression(
1541 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1542 ComparisonOp::Geq,
1543 Box::new(IntegerExpression::Const(1)),
1544 )),
1545 BooleanConnective::And,
1546 Box::new(BooleanExpression::ComparisonExpression(
1547 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1548 ComparisonOp::Lt,
1549 Box::new(IntegerExpression::Const(1)),
1550 ))
1551 )),
1552 BooleanConnective::Or,
1553 Box::new(BooleanExpression::ComparisonExpression(
1554 Box::new(IntegerExpression::Atom(Variable::new("var3"))),
1555 ComparisonOp::Neq,
1556 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1557 ))
1558 ),
1559 Rule::boolean_expr,
1560 parse_boolean_expr
1561 );
1562
1563 parse_func_test!(
1564 parse_ltl_expr_simple_globally,
1565 "[](loc1 == 0 -> loc2 == 1)",
1566 ELTLExpression::Globally(Box::new(ELTLExpression::Implies(
1567 Box::new(ELTLExpression::LocationExpr(
1568 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1569 ComparisonOp::Eq,
1570 Box::new(IntegerExpression::Const(0))
1571 )),
1572 Box::new(ELTLExpression::LocationExpr(
1573 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1574 ComparisonOp::Eq,
1575 Box::new(IntegerExpression::Const(1))
1576 )),
1577 ))),
1578 Rule::ltl_expr,
1579 parse_ltl_expr
1580 );
1581
1582 parse_func_test!(
1583 parse_ltl_expr_simple_const,
1584 "0 == 0",
1585 ELTLExpression::ParameterExpr(
1586 Box::new(IntegerExpression::Const(0)),
1587 ComparisonOp::Eq,
1588 Box::new(IntegerExpression::Const(0))
1589 ),
1590 Rule::ltl_expr,
1591 parse_ltl_expr
1592 );
1593
1594 parse_func_test!(
1595 parse_ltl_expr_simple_true,
1596 "true",
1597 ELTLExpression::True,
1598 Rule::ltl_expr,
1599 parse_ltl_expr
1600 );
1601
1602 parse_func_test!(
1603 parse_ltl_expr_simple_false,
1604 "false",
1605 ELTLExpression::False,
1606 Rule::ltl_expr,
1607 parse_ltl_expr
1608 );
1609
1610 parse_func_test!(
1611 parse_ltl_expr_adv_globally,
1612 "[](loc1 == 0 -> loc2 == 1 + 3)",
1613 ELTLExpression::Globally(Box::new(ELTLExpression::Implies(
1614 Box::new(ELTLExpression::LocationExpr(
1615 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1616 ComparisonOp::Eq,
1617 Box::new(IntegerExpression::Const(0))
1618 )),
1619 Box::new(ELTLExpression::LocationExpr(
1620 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1621 ComparisonOp::Eq,
1622 Box::new(IntegerExpression::Const(1) + IntegerExpression::Const(3))
1623 )),
1624 ))),
1625 Rule::ltl_expr,
1626 parse_ltl_expr
1627 );
1628
1629 parse_func_test!(
1630 parse_ltl_expr_simple_eventually,
1631 "<>(loc1 == n -> !loc2 == 1)",
1632 ELTLExpression::Eventually(Box::new(ELTLExpression::Implies(
1633 Box::new(ELTLExpression::LocationExpr(
1634 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1635 ComparisonOp::Eq,
1636 Box::new(IntegerExpression::Param(Parameter::new("n")))
1637 )),
1638 Box::new(ELTLExpression::Not(Box::new(ELTLExpression::LocationExpr(
1639 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1640 ComparisonOp::Eq,
1641 Box::new(IntegerExpression::Const(1))
1642 )))),
1643 ))),
1644 Rule::ltl_expr,
1645 parse_ltl_expr
1646 );
1647
1648 parse_func_test!(
1649 parse_ltl_expr_simple_and,
1650 "[](loc1 == 0 -> loc2 == 1) && <>(loc1 == n -> !loc2 == 1)",
1651 ELTLExpression::And(
1652 Box::new(ELTLExpression::Globally(Box::new(ELTLExpression::Implies(
1653 Box::new(ELTLExpression::LocationExpr(
1654 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1655 ComparisonOp::Eq,
1656 Box::new(IntegerExpression::Const(0))
1657 )),
1658 Box::new(ELTLExpression::LocationExpr(
1659 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1660 ComparisonOp::Eq,
1661 Box::new(IntegerExpression::Const(1))
1662 )),
1663 )))),
1664 Box::new(ELTLExpression::Eventually(Box::new(
1665 ELTLExpression::Implies(
1666 Box::new(ELTLExpression::LocationExpr(
1667 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1668 ComparisonOp::Eq,
1669 Box::new(IntegerExpression::Param(Parameter::new("n")))
1670 )),
1671 Box::new(ELTLExpression::Not(Box::new(ELTLExpression::LocationExpr(
1672 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1673 ComparisonOp::Eq,
1674 Box::new(IntegerExpression::Const(1))
1675 )))),
1676 )
1677 )))
1678 ),
1679 Rule::ltl_expr,
1680 parse_ltl_expr
1681 );
1682
1683 parse_func_test!(
1684 parse_ltl_expr_simple_or,
1685 "[](loc1 == 0 -> loc2 == 1) || <>(loc1 == n -> !loc2 == 1)",
1686 ELTLExpression::Or(
1687 Box::new(ELTLExpression::Globally(Box::new(ELTLExpression::Implies(
1688 Box::new(ELTLExpression::LocationExpr(
1689 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1690 ComparisonOp::Eq,
1691 Box::new(IntegerExpression::Const(0))
1692 )),
1693 Box::new(ELTLExpression::LocationExpr(
1694 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1695 ComparisonOp::Eq,
1696 Box::new(IntegerExpression::Const(1))
1697 )),
1698 )))),
1699 Box::new(ELTLExpression::Eventually(Box::new(
1700 ELTLExpression::Implies(
1701 Box::new(ELTLExpression::LocationExpr(
1702 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1703 ComparisonOp::Eq,
1704 Box::new(IntegerExpression::Param(Parameter::new("n")))
1705 )),
1706 Box::new(ELTLExpression::Not(Box::new(ELTLExpression::LocationExpr(
1707 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1708 ComparisonOp::Eq,
1709 Box::new(IntegerExpression::Const(1))
1710 )))),
1711 )
1712 )))
1713 ),
1714 Rule::ltl_expr,
1715 parse_ltl_expr
1716 );
1717
1718 parse_func_test!(
1720 parse_ltl_expr_param_as_variable_expr,
1721 "n == 0",
1722 ELTLExpression::ParameterExpr(
1723 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1724 ComparisonOp::Eq,
1725 Box::new(IntegerExpression::Const(0))
1726 ),
1727 Rule::ltl_expr,
1728 parse_ltl_expr
1729 );
1730 }
1731
1732 mod full_spec {
1733 use taco_threshold_automaton::{
1734 BooleanVarConstraint, LocationConstraint, ParameterConstraint,
1735 };
1736
1737 use super::*;
1738
1739 #[test]
1740 fn test_parse_skeleton_spec() {
1741 let test_spec = "
1742 skel Proc {
1743 parameters n;
1744
1745 assumptions (1) {
1746 n >= 0;
1747 }
1748
1749 locations (2) {
1750 }
1751
1752 inits (3) {
1753 }
1754
1755 rules (4) {
1756 }
1757
1758 specifications (0) {
1759 }
1760 }
1761 ";
1762
1763 let expected_ta = GeneralThresholdAutomatonBuilder::new("Proc")
1764 .with_parameter(Parameter::new("n"))
1765 .unwrap()
1766 .initialize()
1767 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1768 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1769 ComparisonOp::Geq,
1770 Box::new(IntegerExpression::Const(0)),
1771 ))
1772 .unwrap()
1773 .build();
1774
1775 let parser = ByMCParser::new();
1776 let (got_ta, got_spec) = parser.parse_ta_and_spec(test_spec).unwrap();
1777
1778 assert_eq!(got_ta, expected_ta);
1779
1780 let expected_spec = ELTLSpecificationBuilder::new(&got_ta).build();
1781
1782 assert_eq!(got_spec, expected_spec);
1783 }
1784
1785 #[test]
1786 fn test_parse_ta_1() {
1787 let test_spec = "
1788 skel test_ta1 {
1789 shared var1, var2, var3;
1790 parameters n, t, f;
1791
1792 assumptions (1) {
1793 n > 3 * f;
1794 }
1795
1796 locations (2) {
1797 loc1 : [0];
1798 loc2 : [1];
1799 loc3 : [2];
1800 }
1801
1802 inits (1) {
1803 loc1 == n - f || loc2 == 0;
1804 var1 == 0;
1805 var2 == 0;
1806 var3 == 0;
1807 }
1808
1809 rules (4) {
1810 0: loc1 -> loc2
1811 when(true)
1812 do {};
1813 1: loc2 -> loc3
1814 when( var1 == 1 && var2 == n)
1815 do { reset(var3); var1' == var1 + 1 };
1816 }
1817
1818 specifications (1) {
1819 }
1820 }
1821 ";
1822
1823 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1824 .with_parameters(vec![
1825 Parameter::new("n"),
1826 Parameter::new("t"),
1827 Parameter::new("f"),
1828 ])
1829 .unwrap()
1830 .with_variables(vec![
1831 Variable::new("var1"),
1832 Variable::new("var2"),
1833 Variable::new("var3"),
1834 ])
1835 .unwrap()
1836 .with_locations(vec![
1837 Location::new("loc1"),
1838 Location::new("loc2"),
1839 Location::new("loc3"),
1840 ])
1841 .unwrap()
1842 .initialize()
1843 .with_rules(vec![
1844 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1845 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1846 .with_guard(BooleanExpression::BinaryExpression(
1847 Box::new(BooleanVarConstraint::ComparisonExpression(
1848 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1849 ComparisonOp::Eq,
1850 Box::new(IntegerExpression::Const(1)),
1851 )),
1852 BooleanConnective::And,
1853 Box::new(BooleanVarConstraint::ComparisonExpression(
1854 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1855 ComparisonOp::Eq,
1856 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1857 )),
1858 ))
1859 .with_action(Action::new_reset(Variable::new("var3")))
1860 .with_action(
1861 Action::new(
1862 Variable::new("var1"),
1863 IntegerExpression::Atom(Variable::new("var1"))
1864 + IntegerExpression::Const(1),
1865 )
1866 .unwrap(),
1867 )
1868 .build(),
1869 ])
1870 .unwrap()
1871 .with_initial_location_constraint(LocationConstraint::BinaryExpression(
1872 Box::new(LocationConstraint::ComparisonExpression(
1873 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1874 ComparisonOp::Eq,
1875 Box::new(IntegerExpression::BinaryExpr(
1876 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1877 IntegerOp::Sub,
1878 Box::new(IntegerExpression::Param(Parameter::new("f"))),
1879 )),
1880 )),
1881 BooleanConnective::Or,
1882 Box::new(LocationConstraint::ComparisonExpression(
1883 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1884 ComparisonOp::Eq,
1885 Box::new(IntegerExpression::Const(0)),
1886 )),
1887 ))
1888 .unwrap()
1889 .with_initial_variable_constraints(vec![
1890 BooleanVarConstraint::ComparisonExpression(
1891 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1892 ComparisonOp::Eq,
1893 Box::new(IntegerExpression::Const(0)),
1894 ),
1895 BooleanVarConstraint::ComparisonExpression(
1896 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1897 ComparisonOp::Eq,
1898 Box::new(IntegerExpression::Const(0)),
1899 ),
1900 BooleanVarConstraint::ComparisonExpression(
1901 Box::new(IntegerExpression::Atom(Variable::new("var3"))),
1902 ComparisonOp::Eq,
1903 Box::new(IntegerExpression::Const(0)),
1904 ),
1905 ])
1906 .unwrap()
1907 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1908 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1909 ComparisonOp::Gt,
1910 Box::new(IntegerExpression::BinaryExpr(
1911 Box::new(IntegerExpression::Const(3)),
1912 IntegerOp::Mul,
1913 Box::new(IntegerExpression::Param(Parameter::new("f"))),
1914 )),
1915 ))
1916 .unwrap()
1917 .build();
1918
1919 let parser = ByMCParser;
1920 let got_ta = parser.parse_ta(test_spec).unwrap();
1921
1922 assert_eq!(got_ta, expected_ta)
1923 }
1924
1925 #[test]
1926 fn test_parse_ta_1_with_define() {
1927 let test_spec = "
1928 skel test_ta1 {
1929 shared var1, var2, var3;
1930 parameters n, t, f;
1931
1932 define THRESH_1 == n-f;
1933 define THRESH_2 == n;
1934
1935 assumptions (1) {
1936 n > 3 * f;
1937 }
1938
1939 locations (2) {
1940 loc1 : [0];
1941 loc2 : [1];
1942 loc3 : [2];
1943 }
1944
1945 inits (1) {
1946 loc1 == THRESH_1 || loc2 == 0;
1947 var1 == 0;
1948 var2 == 0;
1949 var3 == 0;
1950 }
1951
1952 rules (4) {
1953 0: loc1 -> loc2
1954 when(true)
1955 do {};
1956 1: loc2 -> loc3
1957 when( var1 == 1 && var2 == THRESH_2)
1958 do { reset(var3); var1' == var1 + 1 };
1959 }
1960
1961 specifications (1) {
1962 }
1963 }
1964 ";
1965
1966 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1967 .with_parameters(vec![
1968 Parameter::new("n"),
1969 Parameter::new("t"),
1970 Parameter::new("f"),
1971 ])
1972 .unwrap()
1973 .with_variables(vec![
1974 Variable::new("var1"),
1975 Variable::new("var2"),
1976 Variable::new("var3"),
1977 ])
1978 .unwrap()
1979 .with_locations(vec![
1980 Location::new("loc1"),
1981 Location::new("loc2"),
1982 Location::new("loc3"),
1983 ])
1984 .unwrap()
1985 .initialize()
1986 .with_rules(vec![
1987 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1988 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1989 .with_guard(BooleanExpression::BinaryExpression(
1990 Box::new(BooleanVarConstraint::ComparisonExpression(
1991 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1992 ComparisonOp::Eq,
1993 Box::new(IntegerExpression::Const(1)),
1994 )),
1995 BooleanConnective::And,
1996 Box::new(BooleanVarConstraint::ComparisonExpression(
1997 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1998 ComparisonOp::Eq,
1999 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2000 )),
2001 ))
2002 .with_action(Action::new_reset(Variable::new("var3")))
2003 .with_action(
2004 Action::new(
2005 Variable::new("var1"),
2006 IntegerExpression::Atom(Variable::new("var1"))
2007 + IntegerExpression::Const(1),
2008 )
2009 .unwrap(),
2010 )
2011 .build(),
2012 ])
2013 .unwrap()
2014 .with_initial_location_constraint(LocationConstraint::BinaryExpression(
2015 Box::new(LocationConstraint::ComparisonExpression(
2016 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
2017 ComparisonOp::Eq,
2018 Box::new(IntegerExpression::BinaryExpr(
2019 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2020 IntegerOp::Sub,
2021 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2022 )),
2023 )),
2024 BooleanConnective::Or,
2025 Box::new(LocationConstraint::ComparisonExpression(
2026 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
2027 ComparisonOp::Eq,
2028 Box::new(IntegerExpression::Const(0)),
2029 )),
2030 ))
2031 .unwrap()
2032 .with_initial_variable_constraints(vec![
2033 BooleanVarConstraint::ComparisonExpression(
2034 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2035 ComparisonOp::Eq,
2036 Box::new(IntegerExpression::Const(0)),
2037 ),
2038 BooleanVarConstraint::ComparisonExpression(
2039 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2040 ComparisonOp::Eq,
2041 Box::new(IntegerExpression::Const(0)),
2042 ),
2043 BooleanVarConstraint::ComparisonExpression(
2044 Box::new(IntegerExpression::Atom(Variable::new("var3"))),
2045 ComparisonOp::Eq,
2046 Box::new(IntegerExpression::Const(0)),
2047 ),
2048 ])
2049 .unwrap()
2050 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2051 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2052 ComparisonOp::Gt,
2053 Box::new(IntegerExpression::BinaryExpr(
2054 Box::new(IntegerExpression::Const(3)),
2055 IntegerOp::Mul,
2056 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2057 )),
2058 ))
2059 .unwrap()
2060 .build();
2061
2062 let parser = ByMCParser;
2063 let got_ta = parser.parse_ta(test_spec).unwrap();
2064
2065 assert_eq!(got_ta, expected_ta)
2066 }
2067
2068 #[test]
2069 fn test_parse_ta_2() {
2070 let test_spec = "
2071 skel test_ta {
2072 local loc;
2073 shared sha;
2074 parameters n;
2075
2076 assumptions (1) {
2077 n >= 1;
2078 }
2079
2080 locations (3) {
2081 q0: [0];
2082 q1: [1];
2083 q2: [2];
2084 }/*loc*/
2085 inits (3) {
2086 q0 == n;
2087 q1 == 0;
2088 q2 == 0;
2089 sha == 0;
2090 }/*in */
2091 rules (1) {
2092 0: q0 -> q1
2093 when (true)
2094 do { sha' == sha + 1 };
2095 }
2096
2097 specifications (0) {
2098
2099 }
2100 }
2101 ";
2102
2103 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2104 .with_variables(vec![Variable::new("sha")])
2105 .unwrap()
2106 .with_parameters(vec![Parameter::new("n")])
2107 .unwrap()
2108 .with_locations(vec![
2109 Location::new("q0"),
2110 Location::new("q1"),
2111 Location::new("q2"),
2112 ])
2113 .unwrap()
2114 .initialize()
2115 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2116 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2117 ComparisonOp::Geq,
2118 Box::new(IntegerExpression::Const(1)),
2119 ))
2120 .unwrap()
2121 .with_initial_location_constraints(vec![
2122 LocationConstraint::ComparisonExpression(
2123 Box::new(IntegerExpression::Atom(Location::new("q0"))),
2124 ComparisonOp::Eq,
2125 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2126 ),
2127 LocationConstraint::ComparisonExpression(
2128 Box::new(IntegerExpression::Atom(Location::new("q1"))),
2129 ComparisonOp::Eq,
2130 Box::new(IntegerExpression::Const(0)),
2131 ),
2132 LocationConstraint::ComparisonExpression(
2133 Box::new(IntegerExpression::Atom(Location::new("q2"))),
2134 ComparisonOp::Eq,
2135 Box::new(IntegerExpression::Const(0)),
2136 ),
2137 ])
2138 .unwrap()
2139 .with_initial_variable_constraint(BooleanVarConstraint::ComparisonExpression(
2140 Box::new(IntegerExpression::Atom(Variable::new("sha"))),
2141 ComparisonOp::Eq,
2142 Box::new(IntegerExpression::Const(0)),
2143 ))
2144 .unwrap()
2145 .with_rules(vec![
2146 RuleBuilder::new(0, Location::new("q0"), Location::new("q1"))
2147 .with_guard(BooleanExpression::True)
2148 .with_action(
2149 Action::new(
2150 Variable::new("sha"),
2151 IntegerExpression::Atom(Variable::new("sha"))
2152 + IntegerExpression::Const(1),
2153 )
2154 .unwrap(),
2155 )
2156 .build(),
2157 ])
2158 .unwrap()
2159 .build();
2160
2161 let parser = ByMCParser;
2162 let got_ta = parser.parse_ta(test_spec).unwrap();
2163
2164 assert_eq!(got_ta, expected_ta)
2165 }
2166
2167 #[test]
2168 fn test_parse_ta_2_invalid_component_numbers() {
2169 let test_spec = "
2170 skel test_ta {
2171 local loc;
2172 shared sha;
2173 parameters n;
2174
2175 assumptions (0) {
2176 n >= 1;
2177 }
2178
2179 locations (0) {
2180 q0: [0];
2181 q1: [1];
2182 q2: [2];
2183 }/*loc*/
2184
2185 inits (0) {
2186 q0 == n;
2187 q1 == 0;
2188 q2 == 0;
2189 sha == 0;
2190 }/*in */
2191
2192 rules (0) {
2193 0: q0 -> q1
2194 when (true)
2195 do { sha' == sha + 1 };
2196 }
2197
2198 specifications (5) {
2199
2200 }
2201 }
2202 ";
2203
2204 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2205 .with_variables(vec![Variable::new("sha")])
2206 .unwrap()
2207 .with_parameters(vec![Parameter::new("n")])
2208 .unwrap()
2209 .with_locations(vec![
2210 Location::new("q0"),
2211 Location::new("q1"),
2212 Location::new("q2"),
2213 ])
2214 .unwrap()
2215 .initialize()
2216 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2217 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2218 ComparisonOp::Geq,
2219 Box::new(IntegerExpression::Const(1)),
2220 ))
2221 .unwrap()
2222 .with_initial_location_constraints(vec![
2223 LocationConstraint::ComparisonExpression(
2224 Box::new(IntegerExpression::Atom(Location::new("q0"))),
2225 ComparisonOp::Eq,
2226 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2227 ),
2228 LocationConstraint::ComparisonExpression(
2229 Box::new(IntegerExpression::Atom(Location::new("q1"))),
2230 ComparisonOp::Eq,
2231 Box::new(IntegerExpression::Const(0)),
2232 ),
2233 LocationConstraint::ComparisonExpression(
2234 Box::new(IntegerExpression::Atom(Location::new("q2"))),
2235 ComparisonOp::Eq,
2236 Box::new(IntegerExpression::Const(0)),
2237 ),
2238 ])
2239 .unwrap()
2240 .with_initial_variable_constraint(BooleanVarConstraint::ComparisonExpression(
2241 Box::new(IntegerExpression::Atom(Variable::new("sha"))),
2242 ComparisonOp::Eq,
2243 Box::new(IntegerExpression::Const(0)),
2244 ))
2245 .unwrap()
2246 .with_rules(vec![
2247 RuleBuilder::new(0, Location::new("q0"), Location::new("q1"))
2248 .with_guard(BooleanExpression::True)
2249 .with_action(
2250 Action::new(
2251 Variable::new("sha"),
2252 IntegerExpression::Atom(Variable::new("sha"))
2253 + IntegerExpression::Const(1),
2254 )
2255 .unwrap(),
2256 )
2257 .build(),
2258 ])
2259 .unwrap()
2260 .build();
2261
2262 let parser = ByMCParser::new();
2263 let got_ta = parser.parse_ta(test_spec).unwrap();
2264
2265 assert_eq!(got_ta, expected_ta)
2266 }
2267
2268 #[test]
2269 fn test_parse_ta_3() {
2270 let test_spec = "
2271 skel test_ta {
2272 shared sha, var1, var2, var3;
2273 parameters n, t, f, p;
2274
2275 assumptions (1) {
2276 n >= 1;
2277 n > 3 * f;
2278 n < 3 * t;
2279 p + n <= t / f;
2280 }
2281
2282 locations (3) {
2283 q0: [0];
2284 q1: [1];
2285 q2: [2];
2286 }
2287
2288 // my comment
2289 inits (3) {
2290 q0 == n + f;
2291 q1 == 0;
2292 q2 == 0;
2293 sha == 0;
2294 var1 < 1;
2295 var2 <= 0;
2296 }
2297
2298 rules (1) {
2299 0: q0 -> q1
2300 when (true)
2301 do { sha' == sha + 1 };
2302 1: q0 -> q1
2303 when (false)
2304 do { unchanged(var1, var2, var3) };
2305 2: q1 -> q2
2306 when (var1 == 1 && var2 <= 0)
2307 do { reset(var3); var1' == var1 + 1 };
2308 3: q2 -> q1
2309 when ( !(var1 < 1) || var3 <= n)
2310 do { var3' == 0 };
2311 }
2312
2313 specifications (0) {
2314
2315 }
2316 }
2317 ";
2318
2319 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2320 .with_variables(vec![
2321 Variable::new("sha"),
2322 Variable::new("var1"),
2323 Variable::new("var2"),
2324 Variable::new("var3"),
2325 ])
2326 .unwrap()
2327 .with_parameters(vec![
2328 Parameter::new("n"),
2329 Parameter::new("t"),
2330 Parameter::new("f"),
2331 Parameter::new("p"),
2332 ])
2333 .unwrap()
2334 .with_locations(vec![
2335 Location::new("q0"),
2336 Location::new("q1"),
2337 Location::new("q2"),
2338 ])
2339 .unwrap()
2340 .initialize()
2341 .with_resilience_conditions(vec![
2342 ParameterConstraint::ComparisonExpression(
2343 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2344 ComparisonOp::Geq,
2345 Box::new(IntegerExpression::Const(1)),
2346 ),
2347 ParameterConstraint::ComparisonExpression(
2348 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2349 ComparisonOp::Gt,
2350 Box::new(IntegerExpression::BinaryExpr(
2351 Box::new(IntegerExpression::Const(3)),
2352 IntegerOp::Mul,
2353 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2354 )),
2355 ),
2356 ParameterConstraint::ComparisonExpression(
2357 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2358 ComparisonOp::Lt,
2359 Box::new(IntegerExpression::BinaryExpr(
2360 Box::new(IntegerExpression::Const(3)),
2361 IntegerOp::Mul,
2362 Box::new(IntegerExpression::Param(Parameter::new("t"))),
2363 )),
2364 ),
2365 ParameterConstraint::ComparisonExpression(
2366 Box::new(IntegerExpression::BinaryExpr(
2367 Box::new(IntegerExpression::Param(Parameter::new("p"))),
2368 IntegerOp::Add,
2369 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2370 )),
2371 ComparisonOp::Leq,
2372 Box::new(IntegerExpression::BinaryExpr(
2373 Box::new(IntegerExpression::Param(Parameter::new("t"))),
2374 IntegerOp::Div,
2375 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2376 )),
2377 ),
2378 ])
2379 .unwrap()
2380 .with_initial_location_constraints(vec![
2381 LocationConstraint::ComparisonExpression(
2382 Box::new(IntegerExpression::Atom(Location::new("q0"))),
2383 ComparisonOp::Eq,
2384 Box::new(IntegerExpression::BinaryExpr(
2385 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2386 IntegerOp::Add,
2387 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2388 )),
2389 ),
2390 LocationConstraint::ComparisonExpression(
2391 Box::new(IntegerExpression::Atom(Location::new("q1"))),
2392 ComparisonOp::Eq,
2393 Box::new(IntegerExpression::Const(0)),
2394 ),
2395 LocationConstraint::ComparisonExpression(
2396 Box::new(IntegerExpression::Atom(Location::new("q2"))),
2397 ComparisonOp::Eq,
2398 Box::new(IntegerExpression::Const(0)),
2399 ),
2400 ])
2401 .unwrap()
2402 .with_initial_variable_constraints(vec![
2403 BooleanVarConstraint::ComparisonExpression(
2404 Box::new(IntegerExpression::Atom(Variable::new("sha"))),
2405 ComparisonOp::Eq,
2406 Box::new(IntegerExpression::Const(0)),
2407 ),
2408 BooleanVarConstraint::ComparisonExpression(
2409 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2410 ComparisonOp::Lt,
2411 Box::new(IntegerExpression::Const(1)),
2412 ),
2413 BooleanVarConstraint::ComparisonExpression(
2414 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2415 ComparisonOp::Leq,
2416 Box::new(IntegerExpression::Const(0)),
2417 ),
2418 ])
2419 .unwrap()
2420 .with_rules(vec![
2421 RuleBuilder::new(0, Location::new("q0"), Location::new("q1"))
2422 .with_guard(BooleanExpression::True)
2423 .with_action(
2424 Action::new(
2425 Variable::new("sha"),
2426 IntegerExpression::Atom(Variable::new("sha"))
2427 + IntegerExpression::Const(1),
2428 )
2429 .unwrap(),
2430 )
2431 .build(),
2432 RuleBuilder::new(1, Location::new("q0"), Location::new("q1"))
2433 .with_guard(BooleanExpression::False)
2434 .build(),
2435 RuleBuilder::new(2, Location::new("q1"), Location::new("q2"))
2436 .with_guard(BooleanExpression::BinaryExpression(
2437 Box::new(BooleanVarConstraint::ComparisonExpression(
2438 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2439 ComparisonOp::Eq,
2440 Box::new(IntegerExpression::Const(1)),
2441 )),
2442 BooleanConnective::And,
2443 Box::new(BooleanVarConstraint::ComparisonExpression(
2444 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2445 ComparisonOp::Leq,
2446 Box::new(IntegerExpression::Const(0)),
2447 )),
2448 ))
2449 .with_actions(vec![
2450 Action::new_reset(Variable::new("var3")),
2451 Action::new(
2452 Variable::new("var1"),
2453 IntegerExpression::Atom(Variable::new("var1"))
2454 + IntegerExpression::Const(1),
2455 )
2456 .unwrap(),
2457 ])
2458 .build(),
2459 RuleBuilder::new(3, Location::new("q2"), Location::new("q1"))
2460 .with_guard(BooleanExpression::BinaryExpression(
2461 Box::new(BooleanExpression::Not(Box::new(
2462 BooleanVarConstraint::ComparisonExpression(
2463 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2464 ComparisonOp::Lt,
2465 Box::new(IntegerExpression::Const(1)),
2466 ),
2467 ))),
2468 BooleanConnective::Or,
2469 Box::new(BooleanVarConstraint::ComparisonExpression(
2470 Box::new(IntegerExpression::Atom(Variable::new("var3"))),
2471 ComparisonOp::Leq,
2472 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2473 )),
2474 ))
2475 .with_actions(vec![Action::new_reset(Variable::new("var3"))])
2476 .build(),
2477 ])
2478 .unwrap()
2479 .build();
2480
2481 let parser = ByMCParser;
2482 let got_ta = parser.parse_ta(test_spec).unwrap();
2483
2484 assert_eq!(got_ta, expected_ta)
2485 }
2486
2487 #[test]
2488 fn test_parse_ta_3_with_define() {
2489 let test_spec = "
2490 skel test_ta {
2491 shared sha, var1, var2, var3;
2492 parameters n, t, f, p;
2493
2494 define THRESH_0 == 3 * f;
2495 define THRESH_1 == n + f;
2496 define THRESH_2 == 0;
2497 define THRESH_4 == n;
2498
2499 assumptions (1) {
2500 n >= 1;
2501 n > THRESH_0;
2502 n < 3 * t;
2503 p + n <= t / f;
2504 }
2505
2506 locations (3) {
2507 q0: [0];
2508 q1: [1];
2509 q2: [2];
2510 }
2511
2512 // my comment
2513 inits (3) {
2514 q0 == THRESH_1;
2515 q1 == THRESH_2;
2516 q2 == THRESH_2;
2517 sha == THRESH_2;
2518 var1 < 1;
2519 var2 <= THRESH_2;
2520 }
2521
2522 rules (1) {
2523 0: q0 -> q1
2524 when (true)
2525 do { sha' == sha + 1 };
2526 1: q0 -> q1
2527 when (false)
2528 do { unchanged(var1, var2, var3) };
2529 2: q1 -> q2
2530 when (var1 == 1 && var2 <= THRESH_2)
2531 do { reset(var3); var1' == var1 + 1 };
2532 3: q2 -> q1
2533 when ( !(var1 < 1) || var3 <= THRESH_4)
2534 do { var3' == 0 };
2535 }
2536
2537 specifications (0) {
2538
2539 }
2540 }
2541 ";
2542
2543 let expected_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2544 .with_variables(vec![
2545 Variable::new("sha"),
2546 Variable::new("var1"),
2547 Variable::new("var2"),
2548 Variable::new("var3"),
2549 ])
2550 .unwrap()
2551 .with_parameters(vec![
2552 Parameter::new("n"),
2553 Parameter::new("t"),
2554 Parameter::new("f"),
2555 Parameter::new("p"),
2556 ])
2557 .unwrap()
2558 .with_locations(vec![
2559 Location::new("q0"),
2560 Location::new("q1"),
2561 Location::new("q2"),
2562 ])
2563 .unwrap()
2564 .initialize()
2565 .with_resilience_conditions(vec![
2566 ParameterConstraint::ComparisonExpression(
2567 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2568 ComparisonOp::Geq,
2569 Box::new(IntegerExpression::Const(1)),
2570 ),
2571 ParameterConstraint::ComparisonExpression(
2572 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2573 ComparisonOp::Gt,
2574 Box::new(IntegerExpression::BinaryExpr(
2575 Box::new(IntegerExpression::Const(3)),
2576 IntegerOp::Mul,
2577 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2578 )),
2579 ),
2580 ParameterConstraint::ComparisonExpression(
2581 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2582 ComparisonOp::Lt,
2583 Box::new(IntegerExpression::BinaryExpr(
2584 Box::new(IntegerExpression::Const(3)),
2585 IntegerOp::Mul,
2586 Box::new(IntegerExpression::Param(Parameter::new("t"))),
2587 )),
2588 ),
2589 ParameterConstraint::ComparisonExpression(
2590 Box::new(IntegerExpression::BinaryExpr(
2591 Box::new(IntegerExpression::Param(Parameter::new("p"))),
2592 IntegerOp::Add,
2593 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2594 )),
2595 ComparisonOp::Leq,
2596 Box::new(IntegerExpression::BinaryExpr(
2597 Box::new(IntegerExpression::Param(Parameter::new("t"))),
2598 IntegerOp::Div,
2599 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2600 )),
2601 ),
2602 ])
2603 .unwrap()
2604 .with_initial_location_constraints(vec![
2605 LocationConstraint::ComparisonExpression(
2606 Box::new(IntegerExpression::Atom(Location::new("q0"))),
2607 ComparisonOp::Eq,
2608 Box::new(IntegerExpression::BinaryExpr(
2609 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2610 IntegerOp::Add,
2611 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2612 )),
2613 ),
2614 LocationConstraint::ComparisonExpression(
2615 Box::new(IntegerExpression::Atom(Location::new("q1"))),
2616 ComparisonOp::Eq,
2617 Box::new(IntegerExpression::Const(0)),
2618 ),
2619 LocationConstraint::ComparisonExpression(
2620 Box::new(IntegerExpression::Atom(Location::new("q2"))),
2621 ComparisonOp::Eq,
2622 Box::new(IntegerExpression::Const(0)),
2623 ),
2624 ])
2625 .unwrap()
2626 .with_initial_variable_constraints(vec![
2627 BooleanVarConstraint::ComparisonExpression(
2628 Box::new(IntegerExpression::Atom(Variable::new("sha"))),
2629 ComparisonOp::Eq,
2630 Box::new(IntegerExpression::Const(0)),
2631 ),
2632 BooleanVarConstraint::ComparisonExpression(
2633 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2634 ComparisonOp::Lt,
2635 Box::new(IntegerExpression::Const(1)),
2636 ),
2637 BooleanVarConstraint::ComparisonExpression(
2638 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2639 ComparisonOp::Leq,
2640 Box::new(IntegerExpression::Const(0)),
2641 ),
2642 ])
2643 .unwrap()
2644 .with_rules(vec![
2645 RuleBuilder::new(0, Location::new("q0"), Location::new("q1"))
2646 .with_guard(BooleanExpression::True)
2647 .with_action(
2648 Action::new(
2649 Variable::new("sha"),
2650 IntegerExpression::Atom(Variable::new("sha"))
2651 + IntegerExpression::Const(1),
2652 )
2653 .unwrap(),
2654 )
2655 .build(),
2656 RuleBuilder::new(1, Location::new("q0"), Location::new("q1"))
2657 .with_guard(BooleanExpression::False)
2658 .build(),
2659 RuleBuilder::new(2, Location::new("q1"), Location::new("q2"))
2660 .with_guard(BooleanExpression::BinaryExpression(
2661 Box::new(BooleanVarConstraint::ComparisonExpression(
2662 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2663 ComparisonOp::Eq,
2664 Box::new(IntegerExpression::Const(1)),
2665 )),
2666 BooleanConnective::And,
2667 Box::new(BooleanVarConstraint::ComparisonExpression(
2668 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2669 ComparisonOp::Leq,
2670 Box::new(IntegerExpression::Const(0)),
2671 )),
2672 ))
2673 .with_actions(vec![
2674 Action::new_reset(Variable::new("var3")),
2675 Action::new(
2676 Variable::new("var1"),
2677 IntegerExpression::Atom(Variable::new("var1"))
2678 + IntegerExpression::Const(1),
2679 )
2680 .unwrap(),
2681 ])
2682 .build(),
2683 RuleBuilder::new(3, Location::new("q2"), Location::new("q1"))
2684 .with_guard(BooleanExpression::BinaryExpression(
2685 Box::new(BooleanExpression::Not(Box::new(
2686 BooleanVarConstraint::ComparisonExpression(
2687 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2688 ComparisonOp::Lt,
2689 Box::new(IntegerExpression::Const(1)),
2690 ),
2691 ))),
2692 BooleanConnective::Or,
2693 Box::new(BooleanVarConstraint::ComparisonExpression(
2694 Box::new(IntegerExpression::Atom(Variable::new("var3"))),
2695 ComparisonOp::Leq,
2696 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2697 )),
2698 ))
2699 .with_actions(vec![Action::new_reset(Variable::new("var3"))])
2700 .build(),
2701 ])
2702 .unwrap()
2703 .build();
2704
2705 let parser = ByMCParser;
2706 let got_ta = parser.parse_ta(test_spec).unwrap();
2707
2708 assert_eq!(got_ta, expected_ta)
2709 }
2710 }
2711}