use crate::prelude::*;
pub fn check_forall_fact_has_no_duplicate_forall_free_parameter(
forall_fact: &ForallFact,
) -> Result<(), RuntimeError> {
let mut params_already_used: Vec<Vec<String>> = Vec::new();
check_forall_fact_has_no_duplicate_free_parameter(
forall_fact,
ParamObjType::Forall,
&mut params_already_used,
)
}
pub fn check_exist_fact_has_no_duplicate_exist_free_parameter(
exist_fact: &ExistFactEnum,
) -> Result<(), RuntimeError> {
let mut params_already_used: Vec<Vec<String>> = Vec::new();
check_exist_fact_has_no_duplicate_free_parameter(
exist_fact,
ParamObjType::Exist,
&mut params_already_used,
)
}
pub fn check_forall_fact_with_iff_has_no_duplicate_forall_free_parameter(
forall_fact_with_iff: &ForallFactWithIff,
) -> Result<(), RuntimeError> {
let mut params_already_used: Vec<Vec<String>> = Vec::new();
check_forall_fact_with_iff_has_no_duplicate_free_parameter(
forall_fact_with_iff,
ParamObjType::Forall,
&mut params_already_used,
)
}
fn check_fact_has_no_duplicate_free_parameter(
fact: &Fact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
match fact {
Fact::AtomicFact(_) => Ok(()),
Fact::ExistFact(exist_fact) => check_exist_fact_has_no_duplicate_free_parameter(
exist_fact,
free_param_type,
params_already_used,
),
Fact::OrFact(or_fact) => check_or_fact_has_no_duplicate_free_parameter(
or_fact,
free_param_type,
params_already_used,
),
Fact::AndFact(_) => Ok(()),
Fact::ChainFact(_) => Ok(()),
Fact::ForallFact(forall_fact) => check_forall_fact_has_no_duplicate_free_parameter(
forall_fact,
free_param_type,
params_already_used,
),
Fact::ForallFactWithIff(forall_fact_with_iff) => {
check_forall_fact_with_iff_has_no_duplicate_free_parameter(
forall_fact_with_iff,
free_param_type,
params_already_used,
)
}
Fact::NotForall(not_forall) => check_forall_fact_has_no_duplicate_free_parameter(
¬_forall.forall_fact,
free_param_type,
params_already_used,
),
}
}
fn check_forall_fact_with_iff_has_no_duplicate_free_parameter(
forall_fact_with_iff: &ForallFactWithIff,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
let pushed_scope = push_forall_scope_if_needed(
&forall_fact_with_iff.forall_fact,
free_param_type,
params_already_used,
)?;
for fact in forall_fact_with_iff.forall_fact.dom_facts.iter() {
check_fact_has_no_duplicate_free_parameter(fact, free_param_type, params_already_used)?;
}
for fact in forall_fact_with_iff.forall_fact.then_facts.iter() {
check_exist_or_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact,
free_param_type,
params_already_used,
)?;
}
for fact in forall_fact_with_iff.iff_facts.iter() {
check_exist_or_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact,
free_param_type,
params_already_used,
)?;
}
if pushed_scope {
params_already_used.pop();
}
Ok(())
}
fn check_forall_fact_has_no_duplicate_free_parameter(
forall_fact: &ForallFact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
let pushed_scope =
push_forall_scope_if_needed(forall_fact, free_param_type, params_already_used)?;
for fact in forall_fact.dom_facts.iter() {
check_fact_has_no_duplicate_free_parameter(fact, free_param_type, params_already_used)?;
}
for fact in forall_fact.then_facts.iter() {
check_exist_or_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact,
free_param_type,
params_already_used,
)?;
}
if pushed_scope {
params_already_used.pop();
}
Ok(())
}
fn push_forall_scope_if_needed(
forall_fact: &ForallFact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<bool, RuntimeError> {
if free_param_type != ParamObjType::Forall {
return Ok(false);
}
push_param_def_scope_or_error(
forall_fact.params_def_with_type.collect_param_names(),
free_param_type,
&forall_fact.line_file,
params_already_used,
)?;
Ok(true)
}
fn check_exist_fact_has_no_duplicate_free_parameter(
exist_fact: &ExistFactEnum,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
let pushed_scope =
push_exist_scope_if_needed(exist_fact, free_param_type, params_already_used)?;
for fact in exist_fact.facts().iter() {
check_exist_body_fact_has_no_duplicate_free_parameter(
fact,
free_param_type,
params_already_used,
)?;
}
if pushed_scope {
params_already_used.pop();
}
Ok(())
}
fn check_exist_body_fact_has_no_duplicate_free_parameter(
fact: &ExistBodyFact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
match fact {
ExistBodyFact::AtomicFact(_) => Ok(()),
ExistBodyFact::AndFact(_) => Ok(()),
ExistBodyFact::ChainFact(_) => Ok(()),
ExistBodyFact::OrFact(or_fact) => check_or_fact_has_no_duplicate_free_parameter(
or_fact,
free_param_type,
params_already_used,
),
ExistBodyFact::InlineForall(forall_fact) => {
check_forall_fact_has_no_duplicate_free_parameter(
forall_fact,
free_param_type,
params_already_used,
)
}
}
}
fn push_exist_scope_if_needed(
exist_fact: &ExistFactEnum,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<bool, RuntimeError> {
if free_param_type != ParamObjType::Exist {
return Ok(false);
}
let body = exist_fact.body();
push_param_def_scope_or_error(
body.params_def_with_type.collect_param_names(),
free_param_type,
&body.line_file,
params_already_used,
)?;
Ok(true)
}
fn check_exist_or_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact: &ExistOrAndChainAtomicFact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
match fact {
ExistOrAndChainAtomicFact::AtomicFact(_) => Ok(()),
ExistOrAndChainAtomicFact::AndFact(_) => Ok(()),
ExistOrAndChainAtomicFact::ChainFact(_) => Ok(()),
ExistOrAndChainAtomicFact::OrFact(or_fact) => {
check_or_fact_has_no_duplicate_free_parameter(
or_fact,
free_param_type,
params_already_used,
)
}
ExistOrAndChainAtomicFact::ExistFact(exist_fact) => {
check_exist_fact_has_no_duplicate_free_parameter(
exist_fact,
free_param_type,
params_already_used,
)
}
}
}
fn check_or_fact_has_no_duplicate_free_parameter(
or_fact: &OrFact,
free_param_type: ParamObjType,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
for fact in or_fact.facts.iter() {
check_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact,
free_param_type,
params_already_used,
)?;
}
Ok(())
}
fn check_and_chain_atomic_fact_has_no_duplicate_free_parameter(
fact: &AndChainAtomicFact,
_free_param_type: ParamObjType,
_params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
match fact {
AndChainAtomicFact::AtomicFact(_) => Ok(()),
AndChainAtomicFact::AndFact(_) => Ok(()),
AndChainAtomicFact::ChainFact(_) => Ok(()),
}
}
fn push_param_def_scope_or_error(
param_names: Vec<String>,
free_param_type: ParamObjType,
line_file: &LineFile,
params_already_used: &mut Vec<Vec<String>>,
) -> Result<(), RuntimeError> {
let mut params_in_current_scope: Vec<String> = Vec::new();
for param_name in param_names.iter() {
if params_in_current_scope.contains(param_name) {
return Err(duplicate_param_error(
param_name,
free_param_type,
line_file.clone(),
));
}
if param_name_already_used(param_name, params_already_used) {
return Err(duplicate_param_error(
param_name,
free_param_type,
line_file.clone(),
));
}
params_in_current_scope.push(param_name.clone());
}
params_already_used.push(params_in_current_scope);
Ok(())
}
fn param_name_already_used(param_name: &String, params_already_used: &Vec<Vec<String>>) -> bool {
for params_in_scope in params_already_used.iter() {
if params_in_scope.contains(param_name) {
return true;
}
}
false
}
fn duplicate_param_error(
param_name: &String,
free_param_type: ParamObjType,
line_file: LineFile,
) -> RuntimeError {
DefineParamsRuntimeError(RuntimeErrorStruct::new_with_msg_and_line_file(
format!(
"duplicate {:?} free parameter `{}` in nested scope",
free_param_type, param_name
),
line_file,
))
.into()
}