1use std::collections::HashMap;
7
8use taco_display_utils::join_iterator;
9
10use log::{debug, warn};
11use pest::{
12 Parser, Span,
13 iterators::{Pair, Pairs},
14};
15use pest_derive::Parser;
16use taco_model_checker::eltl::ELTLSpecificationBuilder;
17
18use anyhow::{Context, Error, anyhow};
19use taco_threshold_automaton::{
20 ModifiableThresholdAutomaton, RuleDefinition, ThresholdAutomaton,
21 expressions::{
22 Atomic, BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, IntegerOp,
23 IsDeclared, Location, Parameter, Variable,
24 },
25 general_threshold_automaton::{
26 self, GeneralThresholdAutomaton,
27 builder::{
28 GeneralThresholdAutomatonBuilder, InitializedGeneralThresholdAutomatonBuilder,
29 RuleBuilder,
30 },
31 },
32};
33
34use crate::{
35 ParseTA, ParseTAWithLTL,
36 tla::integer_exprs::{
37 parse_int_boolean_expr, parse_integer_const, parse_integer_update_expr,
38 parse_ltl_specification,
39 },
40};
41
42mod integer_exprs;
43
44const PROC_SET: &str = "Processes";
46const N_CORR_PROC: &str = "NbOfCorrProc";
48const LOCATIONS_SET: &str = "ProcessesLocations";
50
51#[allow(missing_docs)]
53#[derive(Parser)]
54#[grammar = "./tla_format.pest"]
55struct PestTLAParser;
56
57impl ParseTA for TLAParser {
58 fn parse_ta(&self, input: &str) -> Result<GeneralThresholdAutomaton, Error> {
59 let (ta, _, _) = self.parse_ta_and_return_ltl_pairs(input)?;
60 Ok(ta)
61 }
62}
63
64impl ParseTAWithLTL for TLAParser {
65 fn parse_ta_and_spec(
66 &self,
67 input: &str,
68 ) -> Result<
69 (
70 GeneralThresholdAutomaton,
71 taco_model_checker::eltl::ELTLSpecification,
72 ),
73 Error,
74 > {
75 let (ta, pairs, ctx) = self.parse_ta_and_return_ltl_pairs(input)?;
76
77 let ltl_spec = self.parse_specification_and_add_expressions(
78 pairs,
79 ELTLSpecificationBuilder::new(&ta),
80 ctx,
81 )?;
82 let ltl_spec = ltl_spec.build();
83
84 Ok((ta, ltl_spec))
85 }
86}
87
88struct TLAParsingContext<'a> {
91 param_to_corr_procs: Option<IntegerExpression<Location>>,
94 int_macro: HashMap<String, Pair<'a, Rule>>,
96 spec_declaration_parsed: bool,
98 rule_ids_in_next: Vec<u32>,
100}
101impl TLAParsingContext<'_> {
102 pub fn new() -> Self {
103 TLAParsingContext {
104 param_to_corr_procs: None,
105 int_macro: HashMap::new(),
106 spec_declaration_parsed: false,
107 rule_ids_in_next: Vec::new(),
108 }
109 }
110}
111
112pub struct TLAParser {}
117
118impl Default for TLAParser {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl TLAParser {
125 pub fn new() -> Self {
127 TLAParser {}
128 }
129
130 fn parse_specification_and_add_expressions<'a>(
132 &self,
133 pair: Vec<Pair<'a, Rule>>,
134 mut builder: ELTLSpecificationBuilder<'a, GeneralThresholdAutomaton>,
135 ctx: TLAParsingContext,
136 ) -> Result<ELTLSpecificationBuilder<'a, GeneralThresholdAutomaton>, anyhow::Error> {
137 let pairs = pair.into_iter();
138
139 let expressions = pairs
140 .map(|spec| parse_ltl_specification(spec, &builder, &ctx))
141 .collect::<Result<Vec<_>, Error>>()
142 .with_context(|| "Failed to parse specifications: ")?;
143
144 builder
145 .add_expressions(expressions)
146 .with_context(|| "Specification failed validation: ")?;
147 Ok(builder)
148 }
149
150 fn parse_ta_and_return_ltl_pairs<'a>(
151 &self,
152 input: &'a str,
153 ) -> Result<
154 (
155 GeneralThresholdAutomaton,
156 Vec<Pair<'a, Rule>>,
157 TLAParsingContext<'a>,
158 ),
159 Error,
160 > {
161 let mut ctx = TLAParsingContext::new();
162
163 let mut pairs = PestTLAParser::parse(Rule::tla_definition, input)?;
164
165 let mut pairs = pairs
166 .next()
167 .expect("Missing: variable declaration")
168 .into_inner();
169 let mut pair = pairs.next().expect("Missing: constant declaration");
170
171 pair = Self::validate_module_imports(&mut pairs, pair);
172
173 let builder = GeneralThresholdAutomatonBuilder::new("tla_ta");
174
175 let (mut pair, builder) = Self::parse_constant_declaration(&mut pairs, pair, builder)?;
176
177 let mut assume_to_parse_later = Vec::new();
179 while pair.as_rule() == Rule::assume_declaration {
180 assume_to_parse_later.push(pair);
181 pair = pairs.next().expect("Missing: constant declaration");
182 }
183
184 let (pair, builder) = Self::parse_variable_declaration(&mut pairs, pair, builder)?;
185
186 let (pair, builder) = Self::parse_type_ok(&mut pairs, pair, builder)?;
187
188 let mut builder = builder;
189 for rc in assume_to_parse_later {
190 builder = Self::parse_assume_declaration(rc, builder, &mut ctx)?;
191 }
192
193 if ctx.param_to_corr_procs.is_none() {
194 return Err(anyhow!(
195 "Error after parsing all assumptions ('ASSSUME' statements): \
196Missing constraint on the number of correct processes. You need to specify the \
197relation of the number of correct processes '{N_CORR_PROC}' to the other \
198parameters (example: '{N_CORR_PROC} = N - F')."
199 ));
200 }
201
202 let (pair, builder) =
203 Self::parse_initial_constraint_declaration(&mut pairs, pair, builder, &mut ctx)?;
204 if pair.is_none() {
205 warn!("TLA file does not declare rules");
206 return Ok((builder.build(), Vec::new(), ctx));
207 }
208
209 let pair = Self::parse_int_macro_declarations(&mut pairs, Some(pair.unwrap()), &mut ctx);
210 if pair.is_none() {
211 warn!("TLA file does not declare rules");
212 return Ok((builder.build(), Vec::new(), ctx));
213 }
214
215 let (pair, builder) =
216 Self::parse_rule_declarations(&mut pairs, pair.unwrap(), builder, &mut ctx)?;
217
218 let pair = Self::parse_next_declaration(&mut pairs, pair, &mut ctx)?;
219
220 let pair = Self::validate_spec_declaration(&mut pairs, pair, &mut ctx, &builder)?;
221
222 let mut ltl_pairs = Vec::new();
223
224 let mut pair = pair;
225 while pair.is_some() {
226 let inner_pair = pair.unwrap();
227
228 if inner_pair.as_rule() != Rule::int_macro_declaration
229 && inner_pair.as_rule() != Rule::ltl_spec_declaration
230 {
231 pair = Some(inner_pair);
232 break;
233 }
234
235 if inner_pair.as_rule() == Rule::int_macro_declaration {
236 pair = Self::parse_int_macro_declarations(&mut pairs, Some(inner_pair), &mut ctx);
237 continue;
238 }
239
240 ltl_pairs.push(inner_pair);
241 pair = pairs.next();
242 }
243
244 let pair = Self::parse_next_declaration(&mut pairs, pair, &mut ctx)?;
245
246 let pair = Self::validate_spec_declaration(&mut pairs, pair, &mut ctx, &builder)?;
247
248 debug_assert!(
249 pair.unwrap().as_rule() == Rule::EOI,
250 "Expected no more tokens!"
251 );
252
253 if ctx.rule_ids_in_next.is_empty() {
254 warn!(
255 "TLA file does not declare 'Next' or the 'Next' declaration \
256does not specify any rules. This will break compatibility with TLC. Using all \
257declared rules!"
258 );
259 }
260
261 let mut ta = builder.build();
262
263 let rules_not_in_next = ta
264 .rules()
265 .filter(|r| !ctx.rule_ids_in_next.contains(&r.id()))
266 .cloned()
267 .collect::<Vec<_>>();
268 if !rules_not_in_next.is_empty() {
269 warn!(
270 "The next constraint excludes some rules from the threshold \
271automaton. The following rules will be not appear in the automaton: {}",
272 join_iterator(
273 rules_not_in_next
274 .iter()
275 .map(|r| format!("'Rule{}'", r.id())),
276 ", "
277 )
278 );
279 ta.retain_rules(|r| !rules_not_in_next.contains(r));
280 }
281
282 Ok((ta, ltl_pairs, ctx))
283 }
284
285 fn validate_module_imports<'a>(
290 pairs: &mut Pairs<'a, Rule>,
291 pair: Pair<'a, Rule>,
292 ) -> Pair<'a, Rule> {
293 if pair.as_rule() != Rule::module_declaration {
294 warn!(
295 "No module imports found. To maintain compatibility with TLC, \
296at least 'Integers' and 'FiniteSets' should be imported (use \
297'EXTENDS Integers, FiniteSets' at the beginning of the file)."
298 );
299 return pair;
300 }
301
302 let mut inner_pairs = pair.into_inner();
303 let ident_list = inner_pairs
304 .next()
305 .expect("Expected identifier list in EXTENDS");
306
307 let module_identifiers: Vec<String> = parse_identifier_list_to_t(ident_list);
308 debug!(
309 "TLA file imports modules: {}",
310 join_iterator(module_identifiers.iter(), ", ")
311 );
312
313 if !module_identifiers.contains(&"Integers".to_string()) {
314 warn!(
315 "Module import 'Integers' not found. This will break \
316compatibility with TLC."
317 );
318 }
319
320 if !module_identifiers.contains(&"FiniteSets".to_string()) {
321 warn!(
322 "Module import 'FiniteSet' not found. This will break \
323compatibility with TLC."
324 );
325 }
326
327 pairs.next().expect("Missing: constant declaration")
328 }
329
330 fn parse_constant_declaration<'a>(
338 pairs: &mut Pairs<'a, Rule>,
339 pair: Pair<'a, Rule>,
340 builder: GeneralThresholdAutomatonBuilder,
341 ) -> Result<(Pair<'a, Rule>, GeneralThresholdAutomatonBuilder), anyhow::Error> {
342 debug_assert!(
343 pair.as_rule() == Rule::constant_declaration,
344 "Got rule {:?} for {}",
345 pair.as_rule(),
346 pair.as_str()
347 );
348
349 let mut found_params = Vec::new();
350 let mut pair = pair;
351 while pair.as_rule() == Rule::constant_declaration {
352 let mut inner_pairs = pair.into_inner();
353 let inner_pair = inner_pairs.next().unwrap();
354
355 debug_assert!(inner_pairs.next().is_none());
356
357 let params: Vec<Parameter> = parse_identifier_list_to_t(inner_pair);
358 found_params.extend(params);
359
360 pair = pairs.next().expect("Missing: variable declaration");
361 }
362
363 if !found_params.contains(&Parameter::new(PROC_SET)) {
364 return Err(anyhow!(
365 "Missing special constant '{PROC_SET}': No declaration for\
366constant '{PROC_SET}' which represents the set of processes was found. Please \
367add a declaration for constant '{PROC_SET}'."
368 ));
369 }
370
371 if !found_params.contains(&Parameter::new(N_CORR_PROC)) {
372 return Err(anyhow!(
373 "Missing special constant '{N_CORR_PROC}': No declaration for\
374constant '{N_CORR_PROC}' which represents the number of processes executing \
375correctly was found. Please add a declaration for constant '{N_CORR_PROC}'."
376 ));
377 }
378 debug!(
379 "TLA file declares constants: {}",
380 join_iterator(found_params.iter(), ", ")
381 );
382
383 found_params
385 .retain(|p| p != &Parameter::new(PROC_SET) && p != &Parameter::new(N_CORR_PROC));
386
387 let builder = builder.with_parameters(found_params).with_context(|| {
388 "Error while parsing 'CONSTANT' / 'CONSTANTS' declaration, i.e., \
389parameters for the threshold automaton: "
390 })?;
391
392 Ok((pair, builder))
393 }
394
395 fn parse_assume_declaration<'a>(
400 pair: Pair<'a, Rule>,
401 builder: InitializedGeneralThresholdAutomatonBuilder,
402 ctx: &mut TLAParsingContext,
403 ) -> Result<InitializedGeneralThresholdAutomatonBuilder, anyhow::Error> {
404 debug_assert!(
405 pair.as_rule() == Rule::assume_declaration,
406 "Got rule {:?} for {}",
407 pair.as_rule(),
408 pair.as_str()
409 );
410 let err_context_msg = "Failed to parse 'ASSUME' / resilience condition: ";
411
412 let mut inner_pairs = pair.into_inner();
413 let inner_pair = inner_pairs.next().expect("Missing: condition for assume");
414 let inner_span = inner_pair.as_span();
415
416 debug_assert!(inner_pairs.next().is_none());
417
418 let rc_cond =
419 parse_int_boolean_expr(inner_pair, &builder, ctx).with_context(|| err_context_msg);
420
421 let mut rc_split = split_conjunct_bool_expr(rc_cond.unwrap());
422 validate_and_add_n_corr_proc_constr(ctx, &mut rc_split, inner_span)
423 .with_context(|| err_context_msg)?;
424
425 let builder = builder
426 .with_resilience_conditions(rc_split)
427 .with_context(|| err_context_msg)?;
428
429 Ok(builder)
430 }
431
432 fn parse_variable_declaration<'a>(
439 pairs: &mut Pairs<'a, Rule>,
440 pair: Pair<'a, Rule>,
441 builder: GeneralThresholdAutomatonBuilder,
442 ) -> Result<(Pair<'a, Rule>, GeneralThresholdAutomatonBuilder), anyhow::Error> {
443 if pair.as_rule() != Rule::variable_declaration {
444 warn!(
445 "Did not find any declared shared variables. Variables must be \
446declared using 'VARIABLE' / 'VARIABLES' declaration."
447 );
448 return Ok((pair, builder));
449 }
450
451 let mut inner_pairs = pair.into_inner();
452 let inner_pair = inner_pairs.next().unwrap();
453
454 let mut shared_vars: Vec<Variable> = parse_identifier_list_to_t(inner_pair);
455
456 if !shared_vars.contains(&Variable::new(LOCATIONS_SET)) {
457 return Err(anyhow!(
458 "Missing special variable '{LOCATIONS_SET}': No declaration for \
459variable '{LOCATIONS_SET}' which is used to declares the set of locations. \
460Please add a declaration for variable '{LOCATIONS_SET}'."
461 ));
462 }
463
464 debug!(
465 "TLA file declares constants: {}",
466 join_iterator(shared_vars.iter(), ", ")
467 );
468
469 shared_vars.retain(|v| v != &Variable::new(LOCATIONS_SET));
470
471 let builder = builder.with_variables(shared_vars)?;
472
473 let pair = pairs.next().expect("Missing: TypeOk");
474
475 Ok((pair, builder))
476 }
477
478 fn parse_type_ok<'a>(
482 pairs: &mut Pairs<'a, Rule>,
483 pair: Pair<'a, Rule>,
484 builder: GeneralThresholdAutomatonBuilder,
485 ) -> Result<(Pair<'a, Rule>, InitializedGeneralThresholdAutomatonBuilder), anyhow::Error> {
486 debug_assert!(
487 pair.as_rule() == Rule::typeok_declaration,
488 "Got rule {:?} for {}",
489 pair.as_rule(),
490 pair.as_str()
491 );
492 let ctx_err_msg = "Error while parsing correctness \
493constraint 'TypeOk': ";
494
495 let mut variables_declared_nat = Vec::new();
496 let mut set_of_locs = Vec::new();
497
498 for set_expr in pair.into_inner() {
499 debug_assert!(set_expr.as_rule() == Rule::set_boolean_expr);
500
501 let mut inner_pairs = set_expr.into_inner();
502
503 let ident: Variable = parse_identifier_to_t(
504 &inner_pairs
505 .next()
506 .expect("Invalid set_boolean_expr: Missing identifier"),
507 );
508 let set_expr = inner_pairs
509 .next()
510 .expect("Invalid set_boolean_expr: Missing set");
511
512 if ident == Variable::new(LOCATIONS_SET) {
515 if !set_of_locs.is_empty() {
516 return new_parsing_error_with_ctx(
517 "Second declaration of set of locations found: ",
518 set_expr.as_span(),
519 ctx_err_msg,
520 );
521 }
522
523 set_of_locs = parse_set_of_locations(set_expr).with_context(|| ctx_err_msg)?;
524 continue;
525 }
526
527 if set_expr.as_rule() != Rule::nat {
528 return new_parsing_error_with_ctx(
529 format!(
530 "Shared variables are expected to be of type 'Nat' but \
531'{}' is declared as type '{}'.",
532 ident.name(),
533 set_expr.as_str()
534 ),
535 set_expr.as_span(),
536 ctx_err_msg,
537 );
538 }
539
540 variables_declared_nat.push((ident, set_expr));
541 }
542
543 let builder = builder.with_locations(set_of_locs)?;
544
545 let pair = pairs.next().expect("Missing: init constraint");
546 let builder = builder.initialize();
547
548 for (var, expr) in variables_declared_nat {
550 if !builder.has_variable(&var) {
551 return new_parsing_error_with_ctx(
552 format!(
553 "Type declaration for unknown variable '{}' found",
554 var.name()
555 ),
556 expr.as_span(),
557 ctx_err_msg,
558 );
559 }
560 }
561
562 Ok((pair, builder))
563 }
564
565 fn parse_initial_constraint_declaration<'a>(
567 pairs: &mut Pairs<'a, Rule>,
568 pair: Pair<'a, Rule>,
569 builder: InitializedGeneralThresholdAutomatonBuilder,
570 ctx: &mut TLAParsingContext,
571 ) -> Result<
572 (
573 Option<Pair<'a, Rule>>,
574 InitializedGeneralThresholdAutomatonBuilder,
575 ),
576 anyhow::Error,
577 > {
578 if pair.as_rule() != Rule::init_constraint_declaration {
579 debug!("No initial constraints have been found in TLA file");
580 return Ok((Some(pair), builder));
581 }
582 let err_context_msg = "Error while parsing initial constraints /
583'Init': ";
584
585 let inner_pairs = pair.into_inner();
586
587 let mut builder = builder;
588
589 for init_constr in inner_pairs {
590 debug_assert!(init_constr.as_rule() == Rule::init_constraint_expr);
591
592 let mut inner_pairs = init_constr.into_inner();
593 let inner_pair = inner_pairs.next().expect("Expected initial constraint");
594
595 if inner_pair.as_rule() == Rule::int_bool_expr {
596 let var_expr = parse_int_boolean_expr(inner_pair, &builder, ctx)
597 .with_context(|| err_context_msg)?;
598 let split_var_expr = split_conjunct_bool_expr(var_expr);
600
601 builder = builder
602 .with_initial_variable_constraints(split_var_expr)
603 .with_context(|| err_context_msg)?;
604 continue;
605 }
606
607 if inner_pair.as_rule() == Rule::set_boolean_expr {
608 let loc_constr = parse_initial_loc_constr(inner_pair, &builder, ctx)?;
609 builder = builder
610 .with_initial_location_constraints(loc_constr)
611 .with_context(|| err_context_msg)?;
612 continue;
613 }
614
615 debug_assert!(false, "Expected location or variable expression")
616 }
617
618 let pair = pairs.next();
619 Ok((pair, builder))
620 }
621
622 fn parse_int_macro_declarations<'a>(
624 pairs: &mut Pairs<'a, Rule>,
625 pair: Option<Pair<'a, Rule>>,
626 ctx: &mut TLAParsingContext<'a>,
627 ) -> Option<Pair<'a, Rule>> {
628 let mut opt_pair = pair;
629 while opt_pair.is_some() {
630 let pair = opt_pair.unwrap();
631 if pair.as_rule() != Rule::int_macro_declaration {
632 opt_pair = Some(pair);
633 break;
634 }
635
636 let mut inner_pairs = pair.into_inner();
637
638 let ident_pair = inner_pairs.next().expect("Missing: macro ident");
639 let ident: String = parse_identifier_to_t(&ident_pair);
640
641 let expr_pair = inner_pairs.next().expect("Missing: integer macro int expr");
642 debug_assert!(expr_pair.as_rule() == Rule::integer_expr);
643
644 debug!(
645 "TLA file declared integer macro: '{ident}' := '{}'",
646 expr_pair.as_str()
647 );
648
649 ctx.int_macro.insert(ident, expr_pair);
650 opt_pair = pairs.next()
651 }
652
653 opt_pair
654 }
655
656 fn parse_rule_declarations<'a>(
658 pairs: &mut Pairs<'a, Rule>,
659 pair: Pair<'a, Rule>,
660 builder: InitializedGeneralThresholdAutomatonBuilder,
661 ctx: &mut TLAParsingContext<'a>,
662 ) -> Result<
663 (
664 Option<Pair<'a, Rule>>,
665 InitializedGeneralThresholdAutomatonBuilder,
666 ),
667 anyhow::Error,
668 > {
669 let mut opt_pair = Some(pair);
670 let mut builder = builder;
671
672 while opt_pair.is_some() {
673 let pair = opt_pair.unwrap();
674 if pair.as_rule() != Rule::rule_declaration {
675 opt_pair = Some(pair);
676 break;
677 }
678
679 let rule_span = pair.as_span();
680 let mut inner_pairs = pair.into_inner();
681
682 let id_pair = inner_pairs.next().expect("Missing: rule_ident");
683 let (id, proc_ident) = parse_rule_ident_to_id_and_proc_ident(id_pair);
684
685 let rule =
686 parse_rule_body_exprs(inner_pairs, rule_span, &builder, id, &proc_ident, ctx)?;
687 debug!("TLA file declared rule: {rule}");
688 builder = builder
689 .with_rule(rule)
690 .with_context(|| "Error while parsing rule: ")?;
691
692 opt_pair = pairs.next();
693 }
694
695 Ok((opt_pair, builder))
696 }
697
698 fn parse_next_declaration<'a>(
700 pairs: &mut Pairs<'a, Rule>,
701 pair: Option<Pair<'a, Rule>>,
702 ctx: &mut TLAParsingContext,
703 ) -> Result<Option<Pair<'a, Rule>>, anyhow::Error> {
704 if pair.is_none() {
705 return Ok(pair);
706 }
707
708 let pair = pair.unwrap();
709 if pair.as_rule() != Rule::next_declaration {
710 return Ok(Some(pair));
711 }
712
713 if !ctx.rule_ids_in_next.is_empty() {
714 return new_parsing_error_with_ctx(
715 "Duplicate next declaration found.",
716 pair.as_span(),
717 "Error while parsing 'Next' specification",
718 );
719 }
720
721 let rules = parse_next_expr(pair)?;
722 ctx.rule_ids_in_next.extend(rules);
723
724 Ok(pairs.next())
725 }
726
727 fn validate_spec_declaration<'a>(
729 pairs: &mut Pairs<'a, Rule>,
730 pair: Option<Pair<'a, Rule>>,
731 ctx: &mut TLAParsingContext,
732 builder: &InitializedGeneralThresholdAutomatonBuilder,
733 ) -> Result<Option<Pair<'a, Rule>>, anyhow::Error> {
734 if pair.is_none() {
735 return Ok(None);
736 }
737
738 let pair = pair.unwrap();
739 let pair_span = pair.as_span();
740 if pair.as_rule() != Rule::spec_declaration {
741 return Ok(Some(pair));
742 }
743 let err_ctx_msg = "Error while parsing Spec declaration: ";
744
745 if ctx.spec_declaration_parsed {
746 return new_parsing_error_with_ctx(
747 "Duplicate definition of 'Spec'.",
748 pair_span,
749 err_ctx_msg,
750 );
751 }
752 ctx.spec_declaration_parsed = true;
753
754 let mut inner_pairs = pair.into_inner();
755 let vars_referenced: Vec<Variable> =
756 parse_identifier_list_to_t(inner_pairs.next().expect("Missing: Updated params"));
757
758 let proc_var = Variable::new(LOCATIONS_SET);
759
760 if let Some(var) = vars_referenced
761 .iter()
762 .find(|v| *v != &proc_var && !builder.is_declared(*v))
763 {
764 return new_parsing_error_with_ctx(
765 format!("Undeclared variable '{var}'"),
766 pair_span,
767 err_ctx_msg,
768 );
769 }
770
771 if let Some(var) = builder.variables().find(|v| !vars_referenced.contains(v)) {
772 return new_parsing_error_with_ctx(
773 format!("Next instantiation does not include variable '{var}'"),
774 pair_span,
775 err_ctx_msg,
776 );
777 }
778
779 if !vars_referenced.contains(&proc_var) {
780 return new_parsing_error_with_ctx(
781 format!(
782 "Next instantiation does not include the location of processes represented by variable '{LOCATIONS_SET}'"
783 ),
784 pair_span,
785 err_ctx_msg,
786 );
787 }
788
789 Ok(pairs.next())
790 }
791}
792
793fn validate_and_add_n_corr_proc_constr(
798 ctx: &mut TLAParsingContext,
799 rcs: &mut Vec<BooleanExpression<Parameter>>,
800 span: Span<'_>,
801) -> Result<(), anyhow::Error> {
802 let err_ctx_msg = "Error while validating assumptions on the number correct processes";
803
804 for rc in rcs {
805 match rc {
806 BooleanExpression::ComparisonExpression(lhs, op, rhs) => {
807 let mut other_expr = None;
808 if lhs.as_ref() == &IntegerExpression::Atom(Parameter::new(N_CORR_PROC))
809 || lhs.as_ref() == &IntegerExpression::Param(Parameter::new(N_CORR_PROC))
810 {
811 other_expr = Some(*rhs.clone());
812 }
813
814 if rhs.as_ref() == &IntegerExpression::Atom(Parameter::new(N_CORR_PROC))
815 || rhs.as_ref() == &IntegerExpression::Param(Parameter::new(N_CORR_PROC))
816 {
817 other_expr = Some(*lhs.clone())
818 }
819 if other_expr.is_none() {
820 continue;
821 }
822
823 let other_expr = other_expr.unwrap();
824
825 if other_expr.contains_atom_a(&Parameter::new(N_CORR_PROC))
826 || op != &ComparisonOp::Eq
827 {
828 return new_parsing_error_with_ctx(
829 format!(
830 "Invalid declaration of the number of correct \
831processes '{}{}{}' does not specify a concrete number of correct processes. \
832Expected constraint of the form '{N_CORR_PROC} == <EXPR>' where <EXPR> is an \
833integer expression over the parameters of the threshold automaton",
834 lhs, op, rhs
835 ),
836 span,
837 err_ctx_msg,
838 );
839 }
840
841 if ctx.param_to_corr_procs.is_some() {
842 return new_parsing_error_with_ctx(
843 format!(
844 "Found second constraint on the number of correct \
845processes: '{}{}{}'. There can only be one constraint on '{N_CORR_PROC}'!",
846 lhs, op, rhs
847 ),
848 span,
849 err_ctx_msg,
850 );
851 }
852
853 debug!(
854 "TLA file declares the number of correct processes as: {}",
855 other_expr
856 );
857
858 ctx.param_to_corr_procs = Some(other_expr.into());
859 *rc = BooleanExpression::True;
860 }
861 BooleanExpression::BinaryExpression(_, BooleanConnective::And, _) => {
862 debug_assert!(
863 false,
864 "Boolean expression should have been split before validation!"
865 );
866 }
867 _ => {}
868 }
869 }
870
871 Ok(())
872}
873
874fn parse_initial_loc_constr(
882 pair: Pair<'_, Rule>,
883 builder: &InitializedGeneralThresholdAutomatonBuilder,
884 ctx: &mut TLAParsingContext,
885) -> Result<Vec<BooleanExpression<Location>>, anyhow::Error> {
886 let err_ctx_msg = "Error while parsing set of locations: ";
887 let mut inner_pairs = pair.into_inner();
888
889 let ident_pair = &inner_pairs
890 .next()
891 .expect("Invalid set_bool_expr: Missing identifier");
892 let ident: Variable = parse_identifier_to_t(ident_pair);
893 if ident != Variable::new(LOCATIONS_SET) {
894 return new_parsing_error_with_ctx(
895 "Set constraint found that is not over the locations of processes",
896 ident_pair.as_span(),
897 err_ctx_msg,
898 );
899 }
900
901 let set_expr = inner_pairs
902 .next()
903 .expect("Invalid set_bool_expr: Missing set");
904
905 let init_locs = parse_set_of_locations(set_expr).with_context(|| err_ctx_msg)?;
906
907 debug!(
908 "TLA file declares the following locations as initial: {}",
909 join_iterator(init_locs.iter(), ", ")
910 );
911
912 let not_init_locs = builder
914 .locations()
915 .filter(|l| !init_locs.contains(l))
916 .map(|l| {
917 BooleanExpression::ComparisonExpression(
918 Box::new(IntegerExpression::Atom(l.clone())),
919 ComparisonOp::Eq,
920 Box::new(IntegerExpression::Const(0)),
921 )
922 });
923
924 if init_locs.is_empty() {
925 warn!("No initial location found!");
926 return Ok(not_init_locs.collect());
927 }
928
929 let first_loc = IntegerExpression::Atom(init_locs[0].clone());
931 let init_loc_sum = init_locs.iter().skip(1).fold(first_loc, |acc, l| {
932 IntegerExpression::BinaryExpr(
933 Box::new(acc),
934 IntegerOp::Add,
935 Box::new(IntegerExpression::Atom(l.clone())),
936 )
937 });
938
939 let init_proc_constr = BooleanExpression::ComparisonExpression(
941 Box::new(init_loc_sum),
942 ComparisonOp::Eq,
943 Box::new(ctx.param_to_corr_procs.clone().unwrap()),
944 );
945
946 let mut loc_constr = vec![init_proc_constr];
947 loc_constr.extend(not_init_locs);
948
949 Ok(loc_constr)
950}
951
952fn parse_set_of_locations<'a>(pair: Pair<'a, Rule>) -> Result<Vec<Location>, anyhow::Error> {
955 let ctx_err_msg = "Error while parsing the list of locations: ";
956
957 if pair.as_rule() != Rule::map_definition {
958 return new_parsing_error_with_ctx(
959 format!(
960 "The type of '{LOCATIONS_SET}' is expected to be a map from the \
961set of processes '{PROC_SET}' to the set of locations, specified as a set of \
962strings"
963 ),
964 pair.as_span(),
965 ctx_err_msg,
966 );
967 }
968
969 let mut pairs: Pairs<'_, Rule> = pair.into_inner();
970
971 let ident_pair = pairs.next().expect("Missing: identifier");
972 let ident: String = parse_identifier_to_t(&ident_pair);
973 if ident != PROC_SET {
974 return new_parsing_error_with_ctx(
975 format!(
976 "The type of '{LOCATIONS_SET}' is expected to be a map from the \
977set of processes '{PROC_SET}' to the set of locations. Instead it is a map \
978from unknown set {ident}"
979 ),
980 ident_pair.as_span(),
981 ctx_err_msg,
982 );
983 }
984
985 let set_pair = pairs.next().expect("Missing: set definition");
986 if set_pair.as_rule() != Rule::set_definition_identifier_list {
987 return new_parsing_error_with_ctx(
988 format!(
989 "The type of '{LOCATIONS_SET}' is expected to be a map from \
990the set of processes '{PROC_SET}' to the set of locations, specified as pair \
991of strings. Location set has an unexpected form."
992 ),
993 set_pair.as_span(),
994 ctx_err_msg,
995 );
996 }
997
998 let mut inner_pairs = set_pair.into_inner();
999 let inner_pair = inner_pairs.next().expect("Location set cannot be empty");
1000
1001 let locations = parse_identifier_list_to_t(inner_pair);
1002
1003 debug!(
1004 "TLA file declared the following locations: {}",
1005 join_iterator(locations.iter(), ", ")
1006 );
1007
1008 Ok(locations)
1009}
1010
1011fn parse_rule_body_exprs<'a>(
1015 pairs: Pairs<'a, Rule>,
1016 rule_span: Span<'a>,
1017 builder: &InitializedGeneralThresholdAutomatonBuilder,
1018 rule_id: u32,
1019 proc_ident: &String,
1020 ctx: &mut TLAParsingContext<'a>,
1021) -> Result<general_threshold_automaton::Rule, anyhow::Error> {
1022 let ctx_err_msg = format!("Error while parsing rule {rule_id}");
1023
1024 let pair_vec = pairs.collect::<Vec<_>>();
1025
1026 let src_pair = pair_vec
1028 .iter()
1029 .filter(|p| p.as_rule() == Rule::map_boolean_expr)
1030 .collect::<Vec<_>>();
1031 if src_pair.is_empty() {
1032 return new_parsing_error_with_ctx(
1033 "Missing: Definition of source location",
1034 rule_span,
1035 ctx_err_msg,
1036 );
1037 }
1038 if src_pair.len() > 1 {
1039 return new_parsing_error_with_ctx(
1040 format!(
1041 "Two declarations of target locations found in rule {rule_id}. Target constraints: {}",
1042 join_iterator(src_pair.iter().map(|p| p.as_str()), ",")
1043 ),
1044 rule_span,
1045 ctx_err_msg,
1046 );
1047 }
1048 let source = try_parse_map_entry_constr_loc(src_pair[0].clone(), proc_ident)
1049 .with_context(|| ctx_err_msg.clone())?;
1050
1051 let target_pair = pair_vec
1053 .iter()
1054 .filter(|p| p.as_rule() == Rule::map_update_expr)
1055 .collect::<Vec<_>>();
1056 if target_pair.is_empty() {
1057 return new_parsing_error_with_ctx(
1058 "Missing: Definition of target location",
1059 rule_span,
1060 ctx_err_msg,
1061 );
1062 }
1063 if target_pair.len() > 1 {
1064 return new_parsing_error_with_ctx(
1065 format!(
1066 "Two declarations of target locations found in rule {rule_id}. Target constraints: {}",
1067 join_iterator(target_pair.iter().map(|p| p.as_str()), ", ")
1068 ),
1069 rule_span,
1070 ctx_err_msg,
1071 );
1072 }
1073 let target = parse_map_update_to_target_location(target_pair[0].clone(), proc_ident)
1074 .with_context(|| ctx_err_msg.clone())?;
1075
1076 let mut rule_builder = RuleBuilder::new(rule_id, source, target);
1077
1078 let guard_conditions = pair_vec
1080 .iter()
1081 .filter(|p| p.as_rule() == Rule::int_bool_expr)
1082 .map(|p| parse_int_boolean_expr(p.clone(), builder, ctx))
1083 .collect::<Result<Vec<BooleanExpression<Variable>>, _>>()
1084 .with_context(|| ctx_err_msg.clone())?;
1085
1086 let mut first = BooleanExpression::True;
1088 if !guard_conditions.is_empty() {
1089 first = guard_conditions[0].clone();
1090 }
1091 let guard = guard_conditions.into_iter().skip(1).fold(first, |acc, x| {
1092 BooleanExpression::BinaryExpression(Box::new(acc), BooleanConnective::And, Box::new(x))
1093 });
1094 rule_builder = rule_builder.with_guard(guard);
1095
1096 let rule_updates = pair_vec
1098 .iter()
1099 .filter(|p| p.as_rule() == Rule::integer_update)
1100 .map(|p| parse_integer_update_expr(p.clone(), builder, ctx))
1101 .collect::<Result<Vec<_>, _>>()
1102 .with_context(|| ctx_err_msg.clone())?
1103 .into_iter()
1104 .flatten();
1105
1106 rule_builder = rule_builder.with_actions(rule_updates);
1108
1109 Ok(rule_builder.build())
1110}
1111
1112fn parse_map_update_to_target_location(
1117 pair: Pair<'_, Rule>,
1118 proc_ident: &String,
1119) -> Result<Location, anyhow::Error> {
1120 debug_assert!(
1121 pair.as_rule() == Rule::map_update_expr,
1122 "Got rule {:?} for {}",
1123 pair.as_rule(),
1124 pair.as_str()
1125 );
1126
1127 let pair_span = pair.as_span();
1128 let mut inner_pairs = pair.into_inner();
1129
1130 let map_ident: String = parse_identifier_to_t(&inner_pairs.next().unwrap());
1131 if map_ident != LOCATIONS_SET {
1132 return new_parsing_error_with_ctx(
1133 format!(
1134 "Unknown map update. Only location map '{LOCATIONS_SET}' can be \
1135updated (Example: \
1136'{LOCATIONS_SET}' = [{LOCATIONS_SET} EXCEPT ![{proc_ident}] = \"loc0\"')."
1137 ),
1138 pair_span,
1139 "Error while parsing rule body: ",
1140 );
1141 }
1142
1143 let map_pair = inner_pairs.next().expect("Missing: map redefinition");
1144
1145 try_parse_map_redef_to_target_loc(map_pair, proc_ident)
1146}
1147
1148fn try_parse_map_redef_to_target_loc(
1150 pair: Pair<'_, Rule>,
1151 proc_ident: &String,
1152) -> Result<Location, anyhow::Error> {
1153 debug_assert!(
1154 pair.as_rule() == Rule::map_redefinition,
1155 "Got rule {:?} for {}",
1156 pair.as_rule(),
1157 pair.as_str()
1158 );
1159 let ctx_err_msg = "Error while parsing rule body: ";
1160
1161 let pair_span = pair.as_span();
1162 let mut inner_pairs = pair.into_inner();
1163
1164 let map_ident: String = parse_identifier_to_t(&inner_pairs.next().unwrap());
1165 if map_ident != LOCATIONS_SET {
1166 return new_parsing_error_with_ctx(
1167 format!(
1168 "Unknown map update. Only location map '{LOCATIONS_SET}' can be \
1169updated (Example: \
1170'{LOCATIONS_SET}' = [{LOCATIONS_SET} EXCEPT ![{proc_ident}] = \"loc0\"')."
1171 ),
1172 pair_span,
1173 ctx_err_msg,
1174 );
1175 }
1176
1177 let parsed_elem_name: String = parse_identifier_to_t(&inner_pairs.next().unwrap());
1178 if &parsed_elem_name != proc_ident {
1179 return new_parsing_error_with_ctx(
1180 format!(
1181 "Unknown element used in index of the map: got \
1182'{parsed_elem_name}', expected '{proc_ident}'"
1183 ),
1184 pair_span,
1185 ctx_err_msg,
1186 );
1187 }
1188
1189 let loc: Location = parse_identifier_to_t(&inner_pairs.next().unwrap());
1190 Ok(loc)
1191}
1192
1193fn try_parse_map_entry_constr_loc<T: Atomic + 'static>(
1197 pair: Pair<'_, Rule>,
1198 proc_ident: &String,
1199) -> Result<T, anyhow::Error> {
1200 debug_assert!(
1201 pair.as_rule() == Rule::map_boolean_expr,
1202 "Got rule {:?} for {}",
1203 pair.as_rule(),
1204 pair.as_str()
1205 );
1206 let ctx_err_msg = "Error while parsing rule body: ";
1207
1208 let mut inner_pairs = pair.into_inner();
1209
1210 let map_entry_pair = inner_pairs.next().expect("Missing: map entry");
1211 let map_entry_span = map_entry_pair.as_span();
1212 let (map_name, parsed_proc_name) =
1213 parse_map_entry_into_map_ident_and_proc_ident(map_entry_pair);
1214
1215 if map_name != LOCATIONS_SET {
1216 return new_parsing_error_with_ctx(
1217 format!(
1218 "Unknown map condition. Source location should be specified by \
1219constraining valuation of in '{LOCATIONS_SET}' (Example: \
1220'{LOCATIONS_SET}[{proc_ident}] = \"{proc_ident}\"')"
1221 ),
1222 map_entry_span,
1223 ctx_err_msg,
1224 );
1225 }
1226
1227 if proc_ident != &parsed_proc_name {
1228 return new_parsing_error_with_ctx(
1229 format!(
1230 "Unknown element used in index of the map: got \
1231'{parsed_proc_name}', expected '{proc_ident}'"
1232 ),
1233 map_entry_span,
1234 ctx_err_msg,
1235 );
1236 }
1237
1238 let loc = parse_identifier_to_t(&inner_pairs.next().expect("Missing: loc"));
1239
1240 Ok(loc)
1241}
1242
1243fn parse_next_expr(pair: Pair<'_, Rule>) -> Result<Vec<u32>, anyhow::Error> {
1245 debug_assert!(
1246 pair.as_rule() == Rule::next_declaration,
1247 "Got rule {:?} for {}",
1248 pair.as_rule(),
1249 pair.as_str()
1250 );
1251
1252 let mut inner_pairs = pair.into_inner();
1253
1254 let elem_ident_pair = inner_pairs.next().expect("Missing: process identifier");
1255 let proc_ident: String = parse_identifier_to_t(&elem_ident_pair);
1256
1257 let mut found_ids = Vec::new();
1258 for pair in inner_pairs {
1260 let pair_span = pair.as_span();
1261 let (id, parsed_proc_ident) = parse_rule_ident_to_id_and_proc_ident(pair);
1262
1263 if parsed_proc_ident != proc_ident {
1264 return new_parsing_error_with_ctx(
1265 format!(
1266 "Unknown process identifier '{parsed_proc_ident}' (expected previously declared process dentifier '{proc_ident}'"
1267 ),
1268 pair_span,
1269 "Error while parsing next constraint: ",
1270 );
1271 }
1272
1273 found_ids.push(id);
1274 }
1275
1276 Ok(found_ids)
1277}
1278
1279fn parse_map_entry_into_map_ident_and_proc_ident(pair: Pair<'_, Rule>) -> (String, String) {
1282 debug_assert!(
1283 pair.as_rule() == Rule::map_entry,
1284 "Got rule {:?} for {}",
1285 pair.as_rule(),
1286 pair.as_str()
1287 );
1288 let mut inner_pairs = pair.into_inner();
1289
1290 let map_ident: String = parse_identifier_to_t(&inner_pairs.next().unwrap());
1291 let proc_ident: String = parse_identifier_to_t(&inner_pairs.next().unwrap());
1292
1293 (map_ident, proc_ident)
1294}
1295
1296fn parse_rule_ident_to_id_and_proc_ident(pair: Pair<'_, Rule>) -> (u32, String) {
1303 debug_assert!(
1304 pair.as_rule() == Rule::rule_identifier,
1305 "Got rule {:?} for {}",
1306 pair.as_rule(),
1307 pair.as_str()
1308 );
1309 let mut inner_pairs = pair.into_inner();
1310
1311 let id_pair = inner_pairs.next().expect("Missing: rule id");
1312 let id = parse_integer_const(id_pair);
1313
1314 let proc_ident_pair = inner_pairs
1315 .next()
1316 .expect("Missing: rule process identifier");
1317 let proc_ident: String = parse_identifier_to_t(&proc_ident_pair);
1318
1319 (id, proc_ident)
1320}
1321
1322fn split_conjunct_bool_expr<T: Atomic>(bexpr: BooleanExpression<T>) -> Vec<BooleanExpression<T>> {
1327 match bexpr {
1328 BooleanExpression::BinaryExpression(lhs, BooleanConnective::And, rhs) => {
1329 let mut lhs = split_conjunct_bool_expr(*lhs);
1330 let rhs = split_conjunct_bool_expr(*rhs);
1331 lhs.extend(rhs);
1332 lhs
1333 }
1334 s => vec![s],
1335 }
1336}
1337
1338#[inline(always)]
1340fn parse_identifier_list_to_t<T: for<'a> From<&'a str>>(pair: Pair<'_, Rule>) -> Vec<T> {
1341 debug_assert!(
1342 pair.as_rule() == Rule::identifier_list || pair.as_rule() == Rule::string_identifier_list,
1343 "Got rule {:?} for {}",
1344 pair.as_rule(),
1345 pair.as_str()
1346 );
1347
1348 pair.into_inner()
1349 .map(|id| parse_identifier_to_t(&id))
1350 .collect()
1351}
1352
1353#[inline(always)]
1355fn parse_identifier_to_t<T: for<'a> From<&'a str>>(pair: &Pair<'_, Rule>) -> T {
1356 debug_assert!(
1357 pair.as_rule() == Rule::identifier,
1358 "Got rule {:?} for {}",
1359 pair.as_rule(),
1360 pair.as_str()
1361 );
1362
1363 T::from(pair.as_str())
1364}
1365
1366#[inline(always)]
1368fn new_parsing_error_with_ctx<T, S: Into<String>, SI: Into<String>>(
1369 err_msg: S,
1370 span: Span<'_>,
1371 ctx_msg: SI,
1372) -> Result<T, anyhow::Error> {
1373 let err_msg = err_msg.into();
1374 let parse_err: Box<pest::error::Error<Rule>> = Box::new(pest::error::Error::new_from_span(
1375 pest::error::ErrorVariant::CustomError { message: err_msg },
1376 span,
1377 ));
1378
1379 let ctx_msg = ctx_msg.into();
1380 Err(parse_err).with_context(|| ctx_msg)
1381}
1382
1383#[inline(always)]
1385fn new_parsing_error<S: Into<String>>(message: S, span: Span<'_>) -> Box<pest::error::Error<()>> {
1386 let message = message.into();
1387 Box::new(pest::error::Error::new_from_span(
1388 pest::error::ErrorVariant::CustomError { message },
1389 span,
1390 ))
1391}
1392
1393#[cfg(test)]
1394mod tests {
1395
1396 use pest::Parser;
1397
1398 use crate::{
1399 ParseTA, ParseTAWithLTL,
1400 bymc::ByMCParser,
1401 tla::{PestTLAParser, Rule, TLAParser},
1402 };
1403
1404 #[test]
1405 fn test_var_dec_parses() {
1406 let input =
1407 "VARIABLES ProcessesLocations, nsnt00, nsnt01, nsnt10, nsnt11, nsnt00plus01, nfaulty";
1408
1409 let _pairs = PestTLAParser::parse(Rule::variable_declaration, input);
1410 }
1411
1412 #[test]
1413 fn test_parse_min_working() {
1414 let input = "
1415-------------------------------- MODULE aba --------------------------------
1416CONSTANT Processes, NbOfCorrProc, n
1417
1418ASSUME NbOfCorrProc = 0
1419
1420VARIABLES ProcessesLocations
1421
1422TypeOk ==
1423/\\ ProcessesLocations \\in [Processes -> {\"loc\"}]
1424";
1425
1426 let parser = TLAParser::default();
1427 let res = parser.parse_ta_and_spec(input);
1428 let (got_ta, got_ltl) = res.unwrap();
1429 let got_ta_2 = TLAParser::default().parse_ta(input).unwrap();
1430
1431 let expected_ta = "\
1432ta tla_ta {
1433 parameters n;
1434
1435 assumptions (1) {
1436 true;
1437 }
1438
1439 locations (5) {
1440 loc: [0];
1441 }
1442
1443 inits (0) {
1444 }
1445
1446 rules (0) {
1447 }
1448
1449 spec(0) {
1450 }
1451
1452}";
1453 let (expected_ta, expected_ltl) = ByMCParser::new().parse_ta_and_spec(expected_ta).unwrap();
1454
1455 assert_eq!(
1456 got_ta, expected_ta,
1457 "expected:\n{expected_ta}\n\ngot:\n{got_ta}"
1458 );
1459 assert_eq!(
1460 got_ta_2, expected_ta,
1461 "expected:\n{expected_ta}\n\ngot:\n{got_ta}"
1462 );
1463 assert_eq!(
1464 got_ltl, expected_ltl,
1465 "expected:\n{expected_ltl}\n\ngot:\n{got_ltl}"
1466 )
1467 }
1468
1469 #[test]
1470 fn test_parse_full_spec() {
1471 let input = "
1472-------------------------------- MODULE aba --------------------------------
1473
1474EXTENDS Integers, FiniteSets
1475
1476CONSTANT
1477Processes,
1478NbOfCorrProc,
1479N,
1480T,
1481F
1482
1483ASSUME
1484/\\ NbOfCorrProc = N - F
1485/\\ N > 3 * T
1486/\\ T >= F
1487/\\ T >= 1
1488
1489VARIABLES
1490ProcessesLocations,
1491nsntEC,
1492nsntRD
1493
1494
1495TypeOk ==
1496/\\ ProcessesLocations \\in [Processes -> {\"loc0\", \"loc1\", \"locEC\", \"locRD\", \"locAC\"}]
1497/\\ nsntEC \\in Nat
1498/\\ nsntRD \\in Nat
1499
1500
1501
1502Init ==
1503/\\ ProcessesLocations \\in [Processes -> {\"loc0\", \"loc1\"}]
1504/\\ nsntEC = 0
1505/\\ nsntRD = 0
1506
1507------------------------------------------------------------------------------------------
1508
1509Rule0(p) ==
1510 /\\ ProcessesLocations[p] = \"loc1\"
1511 /\\ ProcessesLocations\' = [ProcessesLocations EXCEPT ![p] = \"locEC\"]
1512 /\\ nsntEC' = nsntEC + 1
1513 /\\ UNCHANGED <<nsntRD>>
1514----------------------------------------------------------------------------------------
1515Rule1(p) ==
1516 /\\ ProcessesLocations[p] = \"loc0\"
1517 /\\ 2 * nsntEC >= N + T + 1 - 2 * F
1518 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locEC\"]
1519 /\\ nsntEC' = nsntEC + 1
1520 /\\ UNCHANGED <<nsntRD>>
1521
1522----------------------------------------------------------------------------------------
1523Rule2(p) ==
1524 /\\ ProcessesLocations[p] = \"loc0\"
1525 /\\ nsntRD >= T + 1 - F
1526 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locEC\"]
1527 /\\ nsntEC' = nsntEC + 1
1528 /\\ UNCHANGED <<nsntRD>>
1529
1530----------------------------------------------------------------------------------------
1531Rule3(p) ==
1532 /\\ ProcessesLocations[p] = \"locEC\"
1533 /\\ 2 * nsntEC >= N + T + 1 - 2 * F
1534 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locRD\"]
1535 /\\ nsntRD' = nsntRD + 1
1536 /\\ UNCHANGED << nsntEC>>
1537
1538----------------------------------------------------------------------------------------
1539Rule4(p) ==
1540 /\\ ProcessesLocations[p] = \"locEC\"
1541 /\\ nsntRD >= T + 1 - F
1542 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locRD\"]
1543 /\\ nsntRD' = nsntRD + 1
1544 /\\ UNCHANGED << nsntEC>>
1545------------------------------------------------------------------------
1546
1547Rule5(p) ==
1548 /\\ ProcessesLocations[p] = \"locRD\"
1549 /\\ 2 * nsntRD >= 2 * T + 1
1550 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locAC\"]
1551 /\\ UNCHANGED <<nsntEC, nsntRD>>
1552----------------------------------------------------------------------------------------
1553Rule6(p) ==
1554 /\\ ProcessesLocations[p] = \"loc0\"
1555 /\\ 2 * nsntEC < N + T + 1
1556 /\\ nsntRD < T + 1
1557 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"loc0\"]
1558 /\\ UNCHANGED <<nsntEC, nsntRD>>
1559----------------------------------------------------------------------------------------
1560
1561 Rule7(p) ==
1562 /\\ ProcessesLocations[p] = \"locEC\"
1563 /\\ 2 * nsntEC < N + T + 1
1564 /\\ nsntRD < T + 1
1565 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locEC\"]
1566 /\\ UNCHANGED << nsntEC, nsntRD>>
1567
1568----------------------------------------------------------------------------------------
1569Rule8(p) ==
1570 /\\ ProcessesLocations[p] = \"locRD\"
1571 /\\ nsntRD < 2 * T + 1
1572 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locRD\"]
1573 /\\ UNCHANGED <<nsntEC, nsntRD>>
1574
1575
1576----------------------------------------------------------------------------------------
1577Rule9(p) ==
1578 /\\ ProcessesLocations[p] = \"locAC\"
1579 /\\ ProcessesLocations' = [ProcessesLocations EXCEPT ![p] = \"locAC\"]
1580 /\\ UNCHANGED <<nsntEC, nsntRD>>
1581
1582----------------------------------------------------------------------------------------
1583
1584NumInloc0 == Cardinality({p \\in Processes : ProcessesLocations[p] = \"loc0\"})
1585NumInloc1 == Cardinality({p \\in Processes : ProcessesLocations[p] = \"loc1\"})
1586NumInlocEC == Cardinality({p \\in Processes : ProcessesLocations[p] = \"locEC\"})
1587NumInlocAC == Cardinality({p \\in Processes : ProcessesLocations[p] = \"locAC\"})
1588NumInlocRD == Cardinality({p \\in Processes : ProcessesLocations[p] = \"locRD\"})
1589
1590unforg ==
1591 NumInloc1 = 0 => [] (NumInlocAC = 0)
1592
1593corr ==
1594 <>[](
1595 (((2 * nsntEC < N + T + 1) \\/ (NumInloc0 = 0))
1596 /\\ ((nsntRD < T + 1) \\/ (NumInloc0 = 0))
1597 /\\ ((2 * nsntEC < N + T + 1) \\/ (NumInlocEC = 0))
1598 /\\ ((nsntRD < T + 1) \\/ (NumInlocEC = 0))
1599 /\\ ((nsntRD < 2 * T + 1) \\/ (NumInlocRD = 0))
1600 /\\ (NumInloc1 = 0))
1601 => ((NumInloc0 = 0) => <> (NumInlocAC /= 0))
1602 )
1603
1604 agreement ==
1605 <>[](
1606 (((2 * nsntEC < N + T + 1) \\/ (NumInloc0 = 0))
1607 /\\ ((nsntRD < T + 1) \\/ (NumInloc0 = 0))
1608 /\\ ((2 * nsntEC < N + T + 1) \\/ (NumInlocEC = 0))
1609 /\\ ((nsntRD < T + 1) \\/ (NumInlocEC = 0))
1610 /\\ ((nsntRD < 2 * T + 1) \\/ (NumInlocRD = 0))
1611 /\\ (NumInloc1 = 0))
1612 =>
1613 [](
1614 (NumInlocAC /= 0)
1615 => <>((NumInloc0 = 0) /\\ (NumInloc1 = 0) /\\ (NumInlocEC = 0) /\\ (NumInlocRD = 0))
1616 )
1617 )
1618
1619
1620----------------------------------------------------------------------------------------
1621Next == \\E p \\in Processes:
1622 \\/ Rule0(p)
1623 \\/ Rule1(p)
1624 \\/ Rule2(p)
1625 \\/ Rule3(p)
1626 \\/ Rule4(p)
1627 \\/ Rule5(p)
1628 \\/ Rule6(p)
1629 \\/ Rule7(p)
1630 \\/ Rule8(p)
1631 \\/ Rule9(p)
1632
1633
1634Spec == Init /\\ [][Next]_<< ProcessesLocations, nsntEC, nsntRD >>
1635=============================================================================
1636";
1637
1638 let parser = TLAParser::default();
1639 let res = parser.parse_ta_and_spec(input);
1640 let (got_ta, got_ltl) = res.unwrap();
1641 let got_ta_2 = TLAParser::default().parse_ta(input).unwrap();
1642
1643 let expected_ta = "\
1644ta tla_ta {
1645 shared nsntEC, nsntRD;
1646
1647 parameters N, T, F;
1648
1649 assumptions (4) {
1650 true;
1651 N > (3 * T);
1652 T >= F;
1653 T >= 1;
1654 }
1655
1656 locations (5) {
1657 loc0:[0];
1658 loc1:[1];
1659 locAC:[2];
1660 locEC:[3];
1661 locRD:[4];
1662 }
1663
1664 inits (6) {
1665 (loc0 + loc1) == (N - F);
1666 locAC == 0;
1667 locRD == 0;
1668 locEC == 0;
1669 nsntEC == 0;
1670 nsntRD == 0;
1671 }
1672
1673 rules (10) {
1674 0: loc1 -> locEC
1675 when ( true )
1676 do { nsntEC' == nsntEC + 1 };
1677 1: loc0 -> locEC
1678 when ( (2 * nsntEC) >= (((N + T) + 1) - (2 * F)) )
1679 do { nsntEC' == nsntEC + 1 };
1680 2: loc0 -> locEC
1681 when ( nsntRD >= ((T + 1) - F) )
1682 do { nsntEC' == nsntEC + 1 };
1683 3: locEC -> locRD
1684 when ( (2 * nsntEC) >= (((N + T) + 1) - (2 * F)) )
1685 do { nsntRD' == nsntRD + 1 };
1686 4: locEC -> locRD
1687 when ( nsntRD >= ((T + 1) - F) )
1688 do { nsntRD' == nsntRD + 1 };
1689 5: locRD -> locAC
1690 when ( (2 * nsntRD) >= ((2 * T) + 1) )
1691 do { };
1692 6: loc0 -> loc0
1693 when ( ((2 * nsntEC) < ((N + T) + 1) && nsntRD < (T + 1)) )
1694 do { };
1695 7: locEC -> locEC
1696 when ( ((2 * nsntEC) < ((N + T) + 1) && nsntRD < (T + 1)) )
1697 do { };
1698 8: locRD -> locRD
1699 when ( nsntRD < ((2 * T) + 1) )
1700 do { };
1701 9: locAC -> locAC
1702 when ( true )
1703 do { };
1704 }
1705
1706 spec(3) {
1707 unforg: (loc1 == 0) -> ([](locAC == 0));
1708 corr: <>([](((((((((2 * nsntEC) < ((N + T) + 1)) || (loc0 == 0)) && ((nsntRD < (T + 1)) || (loc0 == 0))) && (((2 * nsntEC) < ((N + T) + 1)) || (locEC == 0))) && ((nsntRD < (T + 1)) || (locEC == 0))) && ((nsntRD < ((2 * T) + 1)) || (locRD == 0))) && (loc1 == 0)) -> ((loc0 == 0) -> (<>(locAC != 0)))));
1709 agreement: <>([](((((((((2 * nsntEC) < ((N + T) + 1)) || (loc0 == 0)) && ((nsntRD < (T + 1)) || (loc0 == 0))) && (((2 * nsntEC) < ((N + T) + 1)) || (locEC == 0))) && ((nsntRD < (T + 1)) || (locEC == 0))) && ((nsntRD < ((2 * T) + 1)) || (locRD == 0))) && (loc1 == 0)) -> ([]((locAC != 0) -> (<>((((loc0 == 0) && (loc1 == 0)) && (locEC == 0)) && (locRD == 0)))))));
1710 }
1711
1712}";
1713 let (expected_ta, expected_ltl) = ByMCParser::new().parse_ta_and_spec(expected_ta).unwrap();
1714
1715 assert_eq!(
1716 got_ta, expected_ta,
1717 "expected:\n{expected_ta}\n\ngot:\n{got_ta}"
1718 );
1719 assert_eq!(
1720 got_ta_2, expected_ta,
1721 "expected:\n{expected_ta}\n\ngot:\n{got_ta}"
1722 );
1723 assert_eq!(
1724 got_ltl, expected_ltl,
1725 "expected:\n{expected_ltl}\n\ngot:\n{got_ltl}"
1726 )
1727 }
1728}