use crate::prelude::*;
impl Runtime {
pub fn parse_by_contra_stmt(&mut self, tb: &mut TokenBlock) -> Result<Stmt, RuntimeError> {
tb.skip_token(CONTRA)?;
if tb.current()? == RIGHT_ARROW {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra: use `by contra <goal>:` instead of `by contra => <goal>:`"
.to_string(),
tb.line_file.clone(),
),
)));
}
let (to_prove, inline_goal): (Fact, bool) = if tb.current()? != COLON {
let header = &tb.header;
if header.len() < tb.parse_index + 2 || header.last().map(|t| t.as_str()) != Some(COLON)
{
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra ... : expected one goal and a trailing `:` on the same line"
.to_string(),
tb.line_file.clone(),
),
)));
}
let colon_pos = header.len() - 1;
let fact_tokens = header[tb.parse_index..colon_pos].to_vec();
if fact_tokens.is_empty() {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra ... : expected a non-empty goal after `by contra`".to_string(),
tb.line_file.clone(),
),
)));
}
let mut fact_tb = TokenBlock::new(fact_tokens, vec![], tb.line_file.clone());
let fact = self.parse_fact(&mut fact_tb)?;
if !fact_tb.exceed_end_of_head() {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra ... : unfinished tokens in goal".to_string(),
tb.line_file.clone(),
),
)));
}
tb.parse_index = colon_pos + 1;
if !tb.exceed_end_of_head() {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra ... : unexpected tokens after `:`".to_string(),
tb.line_file.clone(),
),
)));
}
(fact, true)
} else {
tb.skip_token(COLON)?;
if !tb.exceed_end_of_head() {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra: expected end of head after by contra:".to_string(),
tb.line_file.clone(),
),
)));
}
if tb.body.len() < 2 {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra: expects prove: block and impossible ... tail".to_string(),
tb.line_file.clone(),
),
)));
}
let to_prove = {
let prove_block = tb.body.get_mut(0).ok_or_else(|| {
RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"Expected body".to_string(),
tb.line_file.clone(),
),
))
})?;
prove_block.skip_token_and_colon_and_exceed_end_of_head(PROVE)?;
if prove_block.body.len() != 1 {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra: prove: expects exactly one fact block".to_string(),
prove_block.line_file.clone(),
),
)));
}
let fact_block = prove_block.body.get_mut(0).ok_or_else(|| {
RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"Expected body".to_string(),
prove_block.line_file.clone(),
),
))
})?;
self.parse_fact(fact_block)?
};
(to_prove, false)
};
let n = tb.body.len();
if inline_goal {
if n < 1 {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra ... : expects a final `impossible ...` block in the body"
.to_string(),
tb.line_file.clone(),
),
)));
}
} else if n < 2 {
return Err(RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"by contra: expects prove: block and impossible ... tail".to_string(),
tb.line_file.clone(),
),
)));
}
let proof_hi = n.saturating_sub(1);
let proof_lo = if inline_goal { 0 } else { 1 };
let mut proof = Vec::new();
if proof_lo < proof_hi {
for block in tb.body[proof_lo..proof_hi].iter_mut() {
proof.push(self.parse_stmt(block)?);
}
}
let mut last_block = tb.body.last_mut().ok_or_else(|| {
RuntimeError::from(ParseRuntimeError(
RuntimeErrorStruct::new_with_msg_and_line_file(
"Expected body".to_string(),
tb.line_file.clone(),
),
))
})?;
last_block.skip_token(IMPOSSIBLE)?;
let impossible_fact = self.parse_atomic_fact(&mut last_block, true)?;
Ok(ByContraStmt::new(to_prove, proof, impossible_fact, tb.line_file.clone()).into())
}
}