434 lines
9.7 KiB
Rust
434 lines
9.7 KiB
Rust
|
use super::{Generics, Type};
|
||
|
use proc_macro2::{Ident, Span};
|
||
|
use std::fmt::{self, Debug, Display, Formatter};
|
||
|
use syn::{
|
||
|
punctuated::Punctuated, spanned::Spanned, token::Paren, Index, LitBool, LitInt, Token
|
||
|
};
|
||
|
|
||
|
mod parse;
|
||
|
|
||
|
#[derive(Clone, Copy, Debug)]
|
||
|
pub enum ArithmeticOp {
|
||
|
Add(Token![+]),
|
||
|
Sub(Token![-]),
|
||
|
Mul(Token![*]),
|
||
|
Div(Token![/])
|
||
|
}
|
||
|
|
||
|
impl ArithmeticOp {
|
||
|
pub fn is_add_or_sub_op(self) -> bool {
|
||
|
matches!(self, Self::Add(_) | Self::Sub(_))
|
||
|
}
|
||
|
|
||
|
pub fn is_mul_or_div_op(self) -> bool {
|
||
|
matches!(self, Self::Mul(_) | Self::Div(_))
|
||
|
}
|
||
|
|
||
|
pub fn char(self) -> char {
|
||
|
match self {
|
||
|
ArithmeticOp::Add(_) => '+',
|
||
|
ArithmeticOp::Sub(_) => '-',
|
||
|
ArithmeticOp::Mul(_) => '*',
|
||
|
ArithmeticOp::Div(_) => '/'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Spanned for ArithmeticOp {
|
||
|
fn span(&self) -> Span {
|
||
|
match self {
|
||
|
Self::Add(token) => token.span,
|
||
|
Self::Sub(token) => token.span,
|
||
|
Self::Mul(token) => token.span,
|
||
|
Self::Div(token) => token.span
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Display for ArithmeticOp {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
write!(f, "{}", self.char())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Copy, Debug)]
|
||
|
pub enum ComparisonOp {
|
||
|
Eq(Token![==]),
|
||
|
Neq(Token![!=]),
|
||
|
Lt(Token![<]),
|
||
|
Le(Token![<=]),
|
||
|
Gt(Token![>]),
|
||
|
Ge(Token![>=])
|
||
|
}
|
||
|
|
||
|
impl ComparisonOp {
|
||
|
fn str(self) -> &'static str {
|
||
|
match self {
|
||
|
ComparisonOp::Eq(_) => "==",
|
||
|
ComparisonOp::Neq(_) => "!=",
|
||
|
ComparisonOp::Lt(_) => "<",
|
||
|
ComparisonOp::Le(_) => "<=",
|
||
|
ComparisonOp::Gt(_) => ">",
|
||
|
ComparisonOp::Ge(_) => ">="
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Spanned for ComparisonOp {
|
||
|
fn span(&self) -> Span {
|
||
|
match self {
|
||
|
Self::Eq(token) => token.span(),
|
||
|
Self::Neq(token) => token.span(),
|
||
|
Self::Lt(token) => token.span,
|
||
|
Self::Le(token) => token.span(),
|
||
|
Self::Gt(token) => token.span,
|
||
|
Self::Ge(token) => token.span()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Display for ComparisonOp {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
f.write_str(self.str())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Copy, Debug)]
|
||
|
pub enum ConjunctionOp {
|
||
|
And(Token![&&]),
|
||
|
Or(Token![||])
|
||
|
}
|
||
|
|
||
|
impl ConjunctionOp {
|
||
|
fn str(self) -> &'static str {
|
||
|
match self {
|
||
|
ConjunctionOp::And(_) => "&&",
|
||
|
ConjunctionOp::Or(_) => "||"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Spanned for ConjunctionOp {
|
||
|
fn span(&self) -> Span {
|
||
|
match self {
|
||
|
Self::And(token) => token.span(),
|
||
|
Self::Or(token) => token.span()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Display for ConjunctionOp {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
f.write_str(self.str())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub enum Expression {
|
||
|
/// An expression enclosed in parentheses.
|
||
|
Term {
|
||
|
paren_token: Paren,
|
||
|
expr: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// Variable access.
|
||
|
Ident(Ident),
|
||
|
|
||
|
/// A boolean literal.
|
||
|
Bool(LitBool),
|
||
|
|
||
|
/// A number literal.
|
||
|
Literal(LitInt),
|
||
|
|
||
|
/// A boolean negation (`!expr`).
|
||
|
BoolNegation {
|
||
|
not_token: Token![!],
|
||
|
expr: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// An integer negation (`-expr`).
|
||
|
IntNegation {
|
||
|
minus_token: Token![-],
|
||
|
expr: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// An arithmetic expression.
|
||
|
Arithmetic {
|
||
|
lhs: Box<Expression>,
|
||
|
op: ArithmeticOp,
|
||
|
rhs: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// A comparison.
|
||
|
Comparison {
|
||
|
lhs: Box<Expression>,
|
||
|
op: ComparisonOp,
|
||
|
rhs: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// A conjunction or disjunction.
|
||
|
Conjunction {
|
||
|
lhs: Box<Expression>,
|
||
|
op: ConjunctionOp,
|
||
|
rhs: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// A tuple constructor.
|
||
|
TupleCtor {
|
||
|
paren_token: Paren,
|
||
|
elems: Punctuated<Expression, Token![,]>
|
||
|
},
|
||
|
|
||
|
/// A tuple index expression.
|
||
|
TupleIndex {
|
||
|
expr: Box<Expression>,
|
||
|
dot_token: Token![.],
|
||
|
index: Index
|
||
|
},
|
||
|
|
||
|
/// An `Rc` constructor.
|
||
|
RcCtor {
|
||
|
span: Span,
|
||
|
paren_token: Paren,
|
||
|
expr: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// An `Option` constructor.
|
||
|
OptionCtor {
|
||
|
span: Span,
|
||
|
expr: Option<(Paren, Box<Expression>)>
|
||
|
},
|
||
|
|
||
|
/// A deref expression.
|
||
|
Deref {
|
||
|
star_token: Token![*],
|
||
|
expr: Box<Expression>
|
||
|
},
|
||
|
|
||
|
/// A function call.
|
||
|
FnCall {
|
||
|
ident: Ident,
|
||
|
generics: Option<Generics<Type>>,
|
||
|
paren_token: Paren,
|
||
|
inputs: Punctuated<Expression, Token![,]>
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Expression {
|
||
|
/// Replace the rightmost part of this expression with the return value
|
||
|
/// of the callback. If self is not a binary expression, or if the filter
|
||
|
/// callback returns false, calls the callback with self.
|
||
|
fn replace_rightmost<F, C>(self, filter: F, callback: C) -> Self
|
||
|
where
|
||
|
F: Fn(&Self) -> bool,
|
||
|
C: FnOnce(Self) -> Self
|
||
|
{
|
||
|
if !filter(&self) {
|
||
|
return callback(self);
|
||
|
}
|
||
|
|
||
|
match self {
|
||
|
Self::Arithmetic { lhs, op, rhs } => Self::Arithmetic {
|
||
|
lhs,
|
||
|
op,
|
||
|
rhs: Box::new(rhs.replace_rightmost(filter, callback))
|
||
|
},
|
||
|
Self::Comparison { lhs, op, rhs } => Self::Comparison {
|
||
|
lhs,
|
||
|
op,
|
||
|
rhs: Box::new(rhs.replace_rightmost(filter, callback))
|
||
|
},
|
||
|
Self::Conjunction { lhs, op, rhs } => Self::Conjunction {
|
||
|
lhs,
|
||
|
op,
|
||
|
rhs: Box::new(rhs.replace_rightmost(filter, callback))
|
||
|
},
|
||
|
|
||
|
this => callback(this)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Spanned for Expression {
|
||
|
fn span(&self) -> Span {
|
||
|
match self {
|
||
|
Self::Term { paren_token, .. } => paren_token.span,
|
||
|
Self::Ident(ident) => ident.span(),
|
||
|
Self::Bool(bool) => bool.span(),
|
||
|
Self::Literal(lit) => lit.span(),
|
||
|
Self::BoolNegation { not_token, .. } => not_token.span,
|
||
|
Self::IntNegation { minus_token, .. } => minus_token.span,
|
||
|
Self::Arithmetic { lhs, op, rhs } => lhs
|
||
|
.span()
|
||
|
.join(op.span())
|
||
|
.unwrap()
|
||
|
.join(rhs.span())
|
||
|
.unwrap(),
|
||
|
Self::Comparison { lhs, op, rhs } => lhs
|
||
|
.span()
|
||
|
.join(op.span())
|
||
|
.unwrap()
|
||
|
.join(rhs.span())
|
||
|
.unwrap(),
|
||
|
Self::Conjunction { lhs, op, rhs } => lhs
|
||
|
.span()
|
||
|
.join(op.span())
|
||
|
.unwrap()
|
||
|
.join(rhs.span())
|
||
|
.unwrap(),
|
||
|
Self::TupleCtor { paren_token, .. } => paren_token.span,
|
||
|
Self::TupleIndex {
|
||
|
expr,
|
||
|
dot_token,
|
||
|
index
|
||
|
} => expr
|
||
|
.span()
|
||
|
.join(dot_token.span())
|
||
|
.unwrap()
|
||
|
.join(index.span())
|
||
|
.unwrap(),
|
||
|
Self::RcCtor { span, .. } => *span,
|
||
|
Self::OptionCtor { span, .. } => *span,
|
||
|
Self::Deref { star_token, expr } => {
|
||
|
star_token.span.join(expr.span()).unwrap()
|
||
|
},
|
||
|
Self::FnCall { ident, .. } => ident.span()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Debug for Expression {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
match self {
|
||
|
Self::Term { expr, .. } => f
|
||
|
.debug_struct("Expression::Term")
|
||
|
.field("expr", &expr)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::Ident(ident) => {
|
||
|
f.debug_tuple("Expression::Ident").field(ident).finish()
|
||
|
},
|
||
|
Self::Bool(lit) => {
|
||
|
f.debug_tuple("Expression::Bool").field(&lit.value).finish()
|
||
|
},
|
||
|
Self::Literal(lit) => f
|
||
|
.debug_tuple("Expression::Literal")
|
||
|
.field(&lit.base10_parse::<isize>())
|
||
|
.finish(),
|
||
|
Self::BoolNegation { expr, .. } => f
|
||
|
.debug_struct("Expression::BoolNegation")
|
||
|
.field("expr", &expr)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::IntNegation { expr, .. } => f
|
||
|
.debug_struct("Expression::IntNegation")
|
||
|
.field("expr", &expr)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::Arithmetic { lhs, op, rhs } => f
|
||
|
.debug_struct("Expression::Arithmetic")
|
||
|
.field("lhs", &lhs)
|
||
|
.field("op", &op.char())
|
||
|
.field("rhs", &rhs)
|
||
|
.finish(),
|
||
|
Self::Comparison { lhs, op, rhs } => f
|
||
|
.debug_struct("Expression::Comparison")
|
||
|
.field("lhs", &lhs)
|
||
|
.field("op", &op.str())
|
||
|
.field("rhs", &rhs)
|
||
|
.finish(),
|
||
|
Self::Conjunction { lhs, op, rhs } => f
|
||
|
.debug_struct("Expression::Conjunction")
|
||
|
.field("lhs", &lhs)
|
||
|
.field("op", &op.str())
|
||
|
.field("rhs", &rhs)
|
||
|
.finish(),
|
||
|
Self::TupleCtor { elems, .. } => f
|
||
|
.debug_struct("Expression::TupleCtor")
|
||
|
.field("elems", &elems)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::TupleIndex { expr, index, .. } => f
|
||
|
.debug_struct("Expression::TupleCtor")
|
||
|
.field("expr", &expr)
|
||
|
.field("index", &index.index)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::RcCtor { expr, .. } => f
|
||
|
.debug_struct("Expression::RcCtor")
|
||
|
.field("expr", &expr)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::OptionCtor { expr, .. } => f
|
||
|
.debug_struct("Expression::OptionCtor")
|
||
|
.field("expr", &expr.as_ref().map(|(_, expr)| expr))
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::Deref { expr, .. } => f
|
||
|
.debug_struct("Expression::Deref")
|
||
|
.field("expr", &expr)
|
||
|
.finish_non_exhaustive(),
|
||
|
Self::FnCall { ident, .. } => f
|
||
|
.debug_struct("Expression::FnCall")
|
||
|
.field("ident", &ident)
|
||
|
.finish_non_exhaustive()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Display for Expression {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
match self {
|
||
|
Self::Term { expr, .. } => write!(f, "({{ {expr} }})"),
|
||
|
Self::Ident(ident) => {
|
||
|
write!(f, "{ident}")
|
||
|
},
|
||
|
Self::Bool(lit) => match lit.value() {
|
||
|
true => f.write_str("true"),
|
||
|
false => f.write_str("false")
|
||
|
},
|
||
|
Self::Literal(lit) => f.write_str(lit.base10_digits()),
|
||
|
Self::BoolNegation { expr, .. } => write!(f, "!{{ {expr} }}"),
|
||
|
Self::IntNegation { expr, .. } => write!(f, "-{{ {expr} }}"),
|
||
|
Self::Arithmetic { lhs, op, rhs } => {
|
||
|
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
|
||
|
},
|
||
|
Self::Comparison { lhs, op, rhs } => {
|
||
|
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
|
||
|
},
|
||
|
Self::Conjunction { lhs, op, rhs } => {
|
||
|
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
|
||
|
},
|
||
|
Self::TupleCtor { elems, .. } => {
|
||
|
f.write_str("(")?;
|
||
|
for elem in elems.iter() {
|
||
|
write!(f, "{{ {elem} }}, ")?;
|
||
|
}
|
||
|
f.write_str(")")
|
||
|
},
|
||
|
Self::TupleIndex { expr, index, .. } => {
|
||
|
write!(f, "{{ {expr} }}.{}", index.index)
|
||
|
},
|
||
|
Self::RcCtor { expr, .. } => write!(f, "Rc({{ {expr} }})"),
|
||
|
Self::OptionCtor { expr: None, .. } => write!(f, "None"),
|
||
|
Self::OptionCtor {
|
||
|
expr: Some((_, expr)),
|
||
|
..
|
||
|
} => write!(f, "Some({{ {expr} }})"),
|
||
|
Self::Deref { expr, .. } => write!(f, "*{{ {expr} }}"),
|
||
|
Self::FnCall {
|
||
|
ident,
|
||
|
generics,
|
||
|
inputs,
|
||
|
..
|
||
|
} => {
|
||
|
write!(f, "{ident}")?;
|
||
|
if let Some(g) = generics {
|
||
|
write!(f, "::{g}")?;
|
||
|
}
|
||
|
f.write_str("(")?;
|
||
|
for arg in inputs.iter() {
|
||
|
write!(f, "{{ {arg} }}, ")?;
|
||
|
}
|
||
|
f.write_str(")")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests;
|