use crate::{
kw, utils::ParseNested, Lit, LitDenominated, SolIdent, Spanned, SubDenomination, Type,
};
use proc_macro2::{Ident, Span};
use std::fmt;
use syn::{
ext::IdentExt,
parse::{Parse, ParseStream},
token::{Brace, Bracket, Paren},
Result, Token,
};
mod array;
pub use array::{ExprArray, ExprIndex};
mod args;
pub use args::{
ArgList, ArgListImpl, ExprCall, ExprCallOptions, ExprPayable, NamedArg, NamedArgList,
};
mod binary;
pub use binary::{BinOp, ExprBinary};
mod member;
pub use member::ExprMember;
mod ternary;
pub use ternary::ExprTernary;
mod tuple;
pub use tuple::ExprTuple;
mod r#type;
pub use r#type::{ExprNew, ExprTypeCall};
mod unary;
pub use unary::{ExprDelete, ExprPostfix, ExprUnary, PostUnOp, UnOp};
#[derive(Clone)]
pub enum Expr {
Array(ExprArray),
Binary(ExprBinary),
Call(ExprCall),
CallOptions(ExprCallOptions),
Delete(ExprDelete),
Ident(SolIdent),
Index(ExprIndex),
Lit(Lit),
LitDenominated(LitDenominated),
Member(ExprMember),
New(ExprNew),
Payable(ExprPayable),
Postfix(ExprPostfix),
Ternary(ExprTernary),
Tuple(ExprTuple),
Type(Type),
TypeCall(ExprTypeCall),
Unary(ExprUnary),
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Expr::")?;
match self {
Self::Array(expr) => expr.fmt(f),
Self::Binary(expr) => expr.fmt(f),
Self::Call(expr) => expr.fmt(f),
Self::CallOptions(expr) => expr.fmt(f),
Self::Delete(expr) => expr.fmt(f),
Self::Ident(ident) => ident.fmt(f),
Self::Index(expr) => expr.fmt(f),
Self::Lit(lit) => lit.fmt(f),
Self::LitDenominated(lit) => lit.fmt(f),
Self::Member(expr) => expr.fmt(f),
Self::New(expr) => expr.fmt(f),
Self::Payable(expr) => expr.fmt(f),
Self::Postfix(expr) => expr.fmt(f),
Self::Ternary(expr) => expr.fmt(f),
Self::Tuple(expr) => expr.fmt(f),
Self::Type(ty) => ty.fmt(f),
Self::TypeCall(expr) => expr.fmt(f),
Self::Unary(expr) => expr.fmt(f),
}
}
}
impl Parse for Expr {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let _ = input.call(syn::Attribute::parse_outer)?;
debug!(" > Expr: {:?}", input.to_string());
let mut expr = Self::parse_simple(input)?;
debug!(" < Expr: {expr:?}");
loop {
let (new, cont) = Self::parse_nested(expr, input)?;
if cont {
debug!(" << Expr: {new:?}");
expr = new;
} else {
return Ok(new);
}
}
}
}
impl Spanned for Expr {
fn span(&self) -> Span {
match self {
Self::Array(expr) => expr.span(),
Self::Binary(expr) => expr.span(),
Self::Call(expr) => expr.span(),
Self::CallOptions(expr) => expr.span(),
Self::Delete(expr) => expr.span(),
Self::Ident(ident) => ident.span(),
Self::Index(expr) => expr.span(),
Self::Lit(lit) => lit.span(),
Self::LitDenominated(lit) => lit.span(),
Self::Member(expr) => expr.span(),
Self::New(expr) => expr.span(),
Self::Payable(expr) => expr.span(),
Self::Postfix(expr) => expr.span(),
Self::Ternary(expr) => expr.span(),
Self::Tuple(expr) => expr.span(),
Self::Type(ty) => ty.span(),
Self::TypeCall(expr) => expr.span(),
Self::Unary(expr) => expr.span(),
}
}
fn set_span(&mut self, span: Span) {
match self {
Self::Array(expr) => expr.set_span(span),
Self::Binary(expr) => expr.set_span(span),
Self::Call(expr) => expr.set_span(span),
Self::CallOptions(expr) => expr.set_span(span),
Self::Delete(expr) => expr.set_span(span),
Self::Ident(ident) => ident.set_span(span),
Self::Index(expr) => expr.set_span(span),
Self::Lit(lit) => lit.set_span(span),
Self::LitDenominated(lit) => lit.set_span(span),
Self::Member(expr) => expr.set_span(span),
Self::New(expr) => expr.set_span(span),
Self::Payable(expr) => expr.set_span(span),
Self::Postfix(expr) => expr.set_span(span),
Self::Ternary(expr) => expr.set_span(span),
Self::Tuple(expr) => expr.set_span(span),
Self::Type(ty) => ty.set_span(span),
Self::TypeCall(expr) => expr.set_span(span),
Self::Unary(expr) => expr.set_span(span),
}
}
}
impl Expr {
fn parse_simple(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Paren) {
input.parse().map(Self::Tuple)
} else if lookahead.peek(Bracket) {
input.parse().map(Self::Array)
} else if UnOp::peek(input, &lookahead) {
input.parse().map(Self::Unary)
} else if Lit::peek(&lookahead) {
match (input.parse()?, input.call(SubDenomination::parse_opt)?) {
(Lit::Number(number), Some(denom)) => {
Ok(Self::LitDenominated(LitDenominated { number, denom }))
}
(lit, None) => Ok(Self::Lit(lit)),
(_, Some(denom)) => {
Err(syn::Error::new(denom.span(), "unexpected subdenomination for literal"))
}
}
} else if lookahead.peek(kw::payable) {
input.parse().map(Self::Payable)
} else if lookahead.peek(Token![type]) {
input.parse().map(Self::TypeCall)
} else if lookahead.peek(kw::new) {
input.parse().map(Self::New)
} else if lookahead.peek(kw::delete) {
input.parse().map(Self::Delete)
} else if lookahead.peek(Ident::peek_any) {
let ident = input.call(Ident::parse_any)?;
match Type::parse_ident(ident.clone()) {
Ok(ty) if !ty.is_custom() => ty.parse_payable(input).map(Self::Type),
_ => Ok(Self::Ident(ident.into())),
}
} else {
Err(lookahead.error())
}
}
fn parse_nested(expr: Self, input: ParseStream<'_>) -> Result<(Self, bool)> {
macro_rules! parse {
(break) => {
Ok((expr, false))
};
($map:expr) => {
ParseNested::parse_nested(expr.into(), input).map(|e| ($map(e), true))
};
}
let lookahead = input.lookahead1();
if lookahead.peek(Bracket) {
parse!(Self::Index)
} else if lookahead.peek(Brace) {
if input.peek2(kw::catch) {
parse!(break)
} else {
parse!(Self::CallOptions)
}
} else if lookahead.peek(Paren) {
parse!(Self::Call)
} else if lookahead.peek(Token![.]) {
parse!(Self::Member)
} else if lookahead.peek(Token![?]) {
parse!(Self::Ternary)
} else if PostUnOp::peek(input, &lookahead) {
parse!(Self::Postfix)
} else if BinOp::peek(input, &lookahead) {
parse!(Self::Binary)
} else {
parse!(break)
}
}
}