1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use cas_error::Error;
use crate::parser::{
ast::expr::Expr,
fmt::Latex,
token::Semicolon,
Parse,
Parser,
};
use std::{fmt, ops::Range};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Represents a statement in CalcScript.
///
/// A statement is the building block of all CalcScript programs. A complete program is a sequence
/// of one or more statements that are executed in order, and returns the value of the last
/// statement. A statement's return value can be discarded by adding a semicolon at the end of the
/// statement.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Stmt {
/// The expression of a statement.
pub expr: Expr,
/// The span of the semicolon that terminates the statement, if any.
///
/// When this is [`None`], the statement is an expression, and will return the value of the
/// expression. Otherwise, the expression is evaluated for side effects, and the statement
/// returns the unit type `()`.
pub semicolon: Option<Range<usize>>,
/// The region of the source code that this statement was parsed from.
pub span: Range<usize>,
}
impl Stmt {
/// Returns the span of the statement.
pub fn span(&self) -> Range<usize> {
self.span.clone()
}
}
impl<'source> Parse<'source> for Stmt {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let expr = input.try_parse::<Expr>().forward_errors(recoverable_errors)?;
let semicolon = if let Ok(semi) = input.try_parse::<Semicolon>().forward_errors(recoverable_errors) {
Some(semi.span.clone())
} else {
None
};
let stmt_span = if let Some(semicolon) = &semicolon {
expr.span().start..semicolon.end
} else {
expr.span()
};
Ok(Stmt {
expr,
semicolon,
span: stmt_span,
})
}
}
impl std::fmt::Display for Stmt {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.expr)?;
if self.semicolon.is_some() {
write!(f, ";")?;
}
Ok(())
}
}
impl Latex for Stmt {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.expr.fmt_latex(f)?;
if self.semicolon.is_some() {
write!(f, ";")?;
}
Ok(())
}
}