use cas_error::Error;
use crate::parser::{
ast::expr::Expr,
error::{BreakOutsideLoop, ContinueOutsideLoop},
fmt::Latex,
keyword::{Break as BreakToken, Continue as ContinueToken, Loop as LoopToken},
Parse,
Parser,
};
use std::{fmt, ops::Range};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Loop {
pub body: Box<Expr>,
pub span: Range<usize>,
pub loop_span: Range<usize>,
}
impl Loop {
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
}
impl<'source> Parse<'source> for Loop {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let loop_token = input.try_parse::<LoopToken>().forward_errors(recoverable_errors)?;
let body = input.try_parse_with_state::<_, Expr>(|state| {
state.allow_loop_control = true;
}).forward_errors(recoverable_errors)?;
let span = loop_token.span.start..body.span().end;
Ok(Self {
body: Box::new(body),
span,
loop_span: loop_token.span,
})
}
}
impl std::fmt::Display for Loop {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "loop {}", self.body)
}
}
impl Latex for Loop {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\\text{{loop }}")?;
self.body.fmt_latex(f)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Break {
pub value: Option<Box<Expr>>,
pub span: Range<usize>,
pub break_span: Range<usize>,
}
impl Break {
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
}
impl<'source> Parse<'source> for Break {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let break_token = input.try_parse::<BreakToken>().forward_errors(recoverable_errors)?;
let value = input.try_parse_with_state::<_, Expr>(|state| {
state.expr_end_at_eol = true;
}).forward_errors(recoverable_errors).ok();
let span = if let Some(value) = &value {
break_token.span.start..value.span().end
} else {
break_token.span.clone()
};
if !input.state.allow_loop_control {
recoverable_errors.push(Error::new(
vec![break_token.span.clone()],
BreakOutsideLoop,
));
}
Ok(Self {
value: value.map(Box::new),
span,
break_span: break_token.span,
})
}
}
impl std::fmt::Display for Break {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "break")?;
if let Some(value) = &self.value {
write!(f, " {}", value)?;
}
Ok(())
}
}
impl Latex for Break {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\\text{{break }}")?;
if let Some(value) = &self.value {
value.fmt_latex(f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Continue {
pub span: Range<usize>,
}
impl Continue {
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
}
impl<'source> Parse<'source> for Continue {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let continue_token = input.try_parse::<ContinueToken>()
.forward_errors(recoverable_errors)?;
if !input.state.allow_loop_control {
recoverable_errors.push(Error::new(
vec![continue_token.span.clone()],
ContinueOutsideLoop,
));
}
Ok(Self {
span: continue_token.span,
})
}
}
impl std::fmt::Display for Continue {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "continue")
}
}
impl Latex for Continue {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\\text{{continue}}")
}
}