use super::{Box, Lit, SubDenomination, Type};
use crate::BoxSlice;
use either::Either;
use solar_data_structures::trustme;
use solar_interface::{Ident, Span, SpannedOption, diagnostics::ErrorGuaranteed};
use std::fmt;
pub type NamedArgList<'ast> = BoxSlice<'ast, NamedArg<'ast>>;
#[derive(Debug)]
pub struct Expr<'ast> {
pub span: Span,
pub kind: ExprKind<'ast>,
}
impl AsRef<Self> for Expr<'_> {
fn as_ref(&self) -> &Self {
self
}
}
impl<'ast> Expr<'ast> {
pub fn peel_parens(&self) -> &Self {
let mut expr = self;
while let ExprKind::Tuple(x) = &expr.kind
&& let [SpannedOption::Some(inner)] = x.as_slice()
{
expr = inner;
}
expr
}
pub fn peel_parens_mut(&mut self) -> &mut Self {
let mut expr = self;
while let ExprKind::Tuple(x) = &mut unsafe { trustme::decouple_lt_mut(expr) }.kind
&& let [SpannedOption::Some(inner)] = x.as_mut_slice()
{
expr = inner;
}
expr
}
pub fn from_ident(ident: Ident) -> Self {
Self { span: ident.span, kind: ExprKind::Ident(ident) }
}
pub fn from_ty(ty: Type<'ast>) -> Self {
Self { span: ty.span, kind: ExprKind::Type(ty) }
}
}
#[derive(Debug)]
pub enum ExprKind<'ast> {
Array(BoxSlice<'ast, Box<'ast, Expr<'ast>>>),
Assign(Box<'ast, Expr<'ast>>, Option<BinOp>, Box<'ast, Expr<'ast>>),
Binary(Box<'ast, Expr<'ast>>, BinOp, Box<'ast, Expr<'ast>>),
Call(Box<'ast, Expr<'ast>>, CallArgs<'ast>),
CallOptions(Box<'ast, Expr<'ast>>, NamedArgList<'ast>),
Delete(Box<'ast, Expr<'ast>>),
Ident(Ident),
Index(Box<'ast, Expr<'ast>>, IndexKind<'ast>),
Lit(Box<'ast, Lit<'ast>>, Option<SubDenomination>),
Member(Box<'ast, Expr<'ast>>, Ident),
New(Type<'ast>),
Payable(CallArgs<'ast>),
Ternary(Box<'ast, Expr<'ast>>, Box<'ast, Expr<'ast>>, Box<'ast, Expr<'ast>>),
Tuple(BoxSlice<'ast, SpannedOption<Box<'ast, Expr<'ast>>>>),
TypeCall(Type<'ast>),
Type(Type<'ast>),
Unary(UnOp, Box<'ast, Expr<'ast>>),
Err(ErrorGuaranteed),
}
#[derive(Clone, Copy, Debug)]
pub struct BinOp {
pub span: Span,
pub kind: BinOpKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BinOpKind {
Lt,
Le,
Gt,
Ge,
Eq,
Ne,
Or,
And,
Shr,
Shl,
Sar,
BitAnd,
BitOr,
BitXor,
Add,
Sub,
Pow,
Mul,
Div,
Rem,
}
impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.kind.to_str())
}
}
impl BinOpKind {
pub const fn to_str(self) -> &'static str {
match self {
Self::Lt => "<",
Self::Le => "<=",
Self::Gt => ">",
Self::Ge => ">=",
Self::Eq => "==",
Self::Ne => "!=",
Self::Or => "||",
Self::And => "&&",
Self::Sar => ">>>",
Self::Shr => ">>",
Self::Shl => "<<",
Self::BitAnd => "&",
Self::BitOr => "|",
Self::BitXor => "^",
Self::Add => "+",
Self::Sub => "-",
Self::Pow => "**",
Self::Mul => "*",
Self::Div => "/",
Self::Rem => "%",
}
}
pub const fn assignable(self) -> bool {
use BinOpKind::*;
matches!(self, BitOr | BitXor | BitAnd | Shl | Shr | Sar | Add | Sub | Mul | Div | Rem)
}
pub const fn is_cmp(self) -> bool {
use BinOpKind::*;
matches!(self, Lt | Le | Gt | Ge | Eq | Ne)
}
pub const fn is_shift(self) -> bool {
use BinOpKind::*;
matches!(self, Shl | Shr | Sar)
}
}
#[derive(Clone, Copy, Debug)]
pub struct UnOp {
pub span: Span,
pub kind: UnOpKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum UnOpKind {
PreInc,
PreDec,
Not,
Neg,
BitNot,
PostInc,
PostDec,
}
impl fmt::Display for UnOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.kind.to_str())
}
}
impl UnOpKind {
pub const fn to_str(self) -> &'static str {
match self {
Self::PreInc | Self::PostInc => "++",
Self::PreDec | Self::PostDec => "--",
Self::Not => "!",
Self::Neg => "-",
Self::BitNot => "~",
}
}
pub const fn is_prefix(self) -> bool {
match self {
Self::PreInc | Self::PreDec | Self::Not | Self::Neg | Self::BitNot => true,
Self::PostInc | Self::PostDec => false,
}
}
pub const fn is_postfix(self) -> bool {
!self.is_prefix()
}
pub const fn has_side_effects(self) -> bool {
match self {
Self::PreInc | Self::PreDec | Self::PostInc | Self::PostDec => true,
Self::Not | Self::Neg | Self::BitNot => false,
}
}
}
#[derive(Debug)]
pub struct CallArgs<'ast> {
pub span: Span,
pub kind: CallArgsKind<'ast>,
}
impl<'ast> CallArgs<'ast> {
pub fn empty(span: Span) -> Self {
Self { span, kind: CallArgsKind::empty() }
}
pub fn is_dummy(&self) -> bool {
self.span.lo() == self.span.hi()
}
pub fn len(&self) -> usize {
self.kind.len()
}
pub fn is_empty(&self) -> bool {
self.kind.is_empty()
}
pub fn exprs(
&self,
) -> impl ExactSizeIterator<Item = &Expr<'ast>> + DoubleEndedIterator + Clone {
self.kind.exprs()
}
pub fn exprs_mut(
&mut self,
) -> impl ExactSizeIterator<Item = &mut Box<'ast, Expr<'ast>>> + DoubleEndedIterator {
self.kind.exprs_mut()
}
}
#[derive(Debug)]
pub enum CallArgsKind<'ast> {
Unnamed(BoxSlice<'ast, Box<'ast, Expr<'ast>>>),
Named(NamedArgList<'ast>),
}
impl Default for CallArgsKind<'_> {
fn default() -> Self {
Self::empty()
}
}
impl<'ast> CallArgsKind<'ast> {
pub fn empty() -> Self {
Self::Unnamed(BoxSlice::default())
}
pub fn len(&self) -> usize {
match self {
Self::Unnamed(exprs) => exprs.len(),
Self::Named(args) => args.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn exprs(
&self,
) -> impl ExactSizeIterator<Item = &Expr<'ast>> + DoubleEndedIterator + Clone {
match self {
Self::Unnamed(exprs) => Either::Left(exprs.iter().map(|expr| &**expr)),
Self::Named(args) => Either::Right(args.iter().map(|arg| &*arg.value)),
}
}
pub fn exprs_mut(
&mut self,
) -> impl ExactSizeIterator<Item = &mut Box<'ast, Expr<'ast>>> + DoubleEndedIterator {
match self {
Self::Unnamed(exprs) => Either::Left(exprs.iter_mut()),
Self::Named(args) => Either::Right(args.iter_mut().map(|arg| &mut arg.value)),
}
}
pub fn span(&self) -> Option<Span> {
if self.is_empty() {
return None;
}
Some(Span::join_first_last(self.exprs().map(|e| e.span)))
}
}
#[derive(Debug)]
pub struct NamedArg<'ast> {
pub name: Ident,
pub value: Box<'ast, Expr<'ast>>,
}
#[derive(Debug)]
pub enum IndexKind<'ast> {
Index(Option<Box<'ast, Expr<'ast>>>),
Range(Option<Box<'ast, Expr<'ast>>>, Option<Box<'ast, Expr<'ast>>>),
}