use tsv_lang::{Comment, Span};
use super::{
ArrayPattern, AssignmentPattern, BlockStatement, ClassExpression, Identifier, Literal,
ObjectPattern, PrivateIdentifier, RestElement, TSParameterProperty, TSType, TSTypeAnnotation,
TSTypeParameterDeclaration, TSTypeParameterInstantiation,
};
#[derive(Debug, Clone)]
pub struct Expression<'arena> {
pub span: Span,
pub kind: ExpressionKind<'arena>,
}
#[derive(Debug, Clone)]
pub enum ExpressionKind<'arena> {
Literal(Literal<'arena>),
Identifier(Identifier<'arena>),
PrivateIdentifier(PrivateIdentifier<'arena>),
ObjectExpression(ObjectExpression<'arena>),
ArrayExpression(ArrayExpression<'arena>),
UnaryExpression(UnaryExpression<'arena>),
UpdateExpression(UpdateExpression<'arena>),
BinaryExpression(BinaryExpression<'arena>),
CallExpression(CallExpression<'arena>),
NewExpression(NewExpression<'arena>),
MemberExpression(MemberExpression<'arena>),
ConditionalExpression(ConditionalExpression<'arena>),
ArrowFunctionExpression(&'arena ArrowFunctionExpression<'arena>),
FunctionExpression(&'arena FunctionExpression<'arena>),
ClassExpression(&'arena ClassExpression<'arena>),
SpreadElement(SpreadElement<'arena>),
TemplateLiteral(TemplateLiteral<'arena>),
TaggedTemplateExpression(&'arena TaggedTemplateExpression<'arena>),
AwaitExpression(AwaitExpression<'arena>),
YieldExpression(YieldExpression<'arena>),
SequenceExpression(SequenceExpression<'arena>),
RegexLiteral(RegexLiteral),
ThisExpression(ThisExpression),
Super(Super),
AssignmentExpression(AssignmentExpression<'arena>),
ObjectPattern(ObjectPattern<'arena>),
ArrayPattern(ArrayPattern<'arena>),
AssignmentPattern(AssignmentPattern<'arena>),
RestElement(RestElement<'arena>),
TSTypeAssertion(TSTypeAssertion<'arena>),
TSAsExpression(TSAsExpression<'arena>),
TSSatisfiesExpression(TSSatisfiesExpression<'arena>),
TSInstantiationExpression(TSInstantiationExpression<'arena>),
TSNonNullExpression(TSNonNullExpression<'arena>),
TSParameterProperty(TSParameterProperty<'arena>),
ImportExpression(ImportExpression<'arena>),
MetaProperty(&'arena MetaProperty<'arena>),
CommentBoundPair(CommentBoundPair<'arena>),
ParenthesizedExpression(ParenthesizedExpression<'arena>),
}
impl<'arena> Expression<'arena> {
#[inline]
pub fn span(&self) -> Span {
self.span
}
#[inline]
pub fn from_identifier(id: Identifier<'arena>) -> Self {
Self {
span: id.span,
kind: ExpressionKind::Identifier(id),
}
}
#[inline]
pub fn from_literal(lit: Literal<'arena>) -> Self {
Self {
span: lit.span,
kind: ExpressionKind::Literal(lit),
}
}
#[inline]
pub fn from_template_literal(template: TemplateLiteral<'arena>) -> Self {
Self {
span: template.span,
kind: ExpressionKind::TemplateLiteral(template),
}
}
#[inline]
pub fn from_rest_element(rest: RestElement<'arena>) -> Self {
Self {
span: rest.span,
kind: ExpressionKind::RestElement(rest),
}
}
#[inline]
pub fn from_spread_element(spread: SpreadElement<'arena>) -> Self {
Self {
span: spread.span,
kind: ExpressionKind::SpreadElement(spread),
}
}
#[inline]
pub fn from_unary_expression(unary: UnaryExpression<'arena>) -> Self {
Self {
span: unary.span,
kind: ExpressionKind::UnaryExpression(unary),
}
}
#[inline]
pub fn from_function_expression(func: &'arena FunctionExpression<'arena>) -> Self {
Self {
span: func.span,
kind: ExpressionKind::FunctionExpression(func),
}
}
pub fn printed_end(&self) -> u32 {
match &self.kind {
ExpressionKind::AssignmentPattern(assign) => assign.right.printed_end(),
ExpressionKind::TSParameterProperty(param_prop) => param_prop.parameter.printed_end(),
_ => self.span.end,
}
}
pub fn skip_type_assertions(&self) -> &Expression<'arena> {
match &self.kind {
ExpressionKind::TSAsExpression(e) => e.expression.skip_type_assertions(),
ExpressionKind::TSSatisfiesExpression(e) => e.expression.skip_type_assertions(),
ExpressionKind::TSNonNullExpression(e) => e.expression.skip_type_assertions(),
ExpressionKind::TSTypeAssertion(e) => e.expression.skip_type_assertions(),
ExpressionKind::CommentBoundPair(e) => e.inner.skip_type_assertions(),
_ => self,
}
}
pub fn unwrap_comment_bound_pairs(&self) -> &Expression<'arena> {
let mut e = self;
while let ExpressionKind::CommentBoundPair(cast) = &e.kind {
e = cast.inner;
}
e
}
pub fn paren_interior(&self) -> Option<Span> {
match &self.kind {
ExpressionKind::SpreadElement(s) => Some(s.paren_interior()),
ExpressionKind::RestElement(r) => Some(r.paren_interior()),
_ => None,
}
}
pub fn has_optional_in_chain(&self) -> bool {
let mut expr = self;
loop {
let (optional, child) = match &expr.kind {
ExpressionKind::MemberExpression(m) => (m.optional, m.object),
ExpressionKind::CallExpression(c) => (c.optional, c.callee),
ExpressionKind::TSNonNullExpression(n) => (false, n.expression),
_ => return false,
};
if optional {
return true;
}
if expr.span.start < child.span.start {
return false;
}
expr = child;
}
}
pub fn is_sealing_non_null(&self) -> bool {
matches!(
&self.kind,
ExpressionKind::TSNonNullExpression(n) if n.seals_optional_chain(self.span)
)
}
}
#[derive(Debug, Clone)]
pub struct CommentBoundPair<'arena> {
pub inner: &'arena Expression<'arena>,
pub comment: Comment,
pub kind: CommentBoundPairKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommentBoundPairKind {
Cast,
GluedComment,
}
#[derive(Debug, Clone)]
pub struct ParenthesizedExpression<'arena> {
pub expression: &'arena Expression<'arena>,
}
impl<'arena> ParenthesizedExpression<'arena> {
#[track_caller]
pub fn unreachable_contents(&self) -> &'arena Expression<'arena> {
#[expect(clippy::unreachable)] if cfg!(debug_assertions) {
unreachable!("a ParenthesizedExpression reached a reader of the paren-free tree");
}
self.expression
}
}
#[derive(Debug, Clone)]
pub struct ObjectExpression<'arena> {
pub properties: &'arena [ObjectProperty<'arena>],
pub spread_trailing_comma: bool,
}
#[derive(Debug, Clone)]
pub enum ObjectProperty<'arena> {
Property(Property<'arena>),
SpreadElement(SpreadElement<'arena>),
}
impl<'arena> ObjectProperty<'arena> {
pub fn span(&self) -> Span {
match self {
ObjectProperty::Property(p) => p.span,
ObjectProperty::SpreadElement(s) => s.span,
}
}
pub fn value_end(&self) -> u32 {
match self {
ObjectProperty::Property(p) => p.value.span().end,
ObjectProperty::SpreadElement(s) => s.span.end,
}
}
pub fn paren_interior(&self) -> Option<Span> {
match self {
ObjectProperty::SpreadElement(s) => Some(s.paren_interior()),
ObjectProperty::Property(_) => None,
}
}
}
#[derive(Debug, Clone)]
pub struct ArrayExpression<'arena> {
pub elements: &'arena [Option<&'arena Expression<'arena>>],
pub spread_trailing_comma: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum UpdateOperator {
Increment = 0, Decrement = 1, }
impl UpdateOperator {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
UpdateOperator::Increment => "++",
UpdateOperator::Decrement => "--",
}
}
}
#[derive(Debug, Clone)]
pub struct UpdateExpression<'arena> {
pub operator: UpdateOperator,
pub argument: &'arena Expression<'arena>,
pub prefix: bool, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum UnaryOperator {
Minus = 0, Plus = 1, Bang = 2, Typeof = 3, Void = 4, Delete = 5, Tilde = 6, }
impl UnaryOperator {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
UnaryOperator::Minus => "-",
UnaryOperator::Plus => "+",
UnaryOperator::Bang => "!",
UnaryOperator::Typeof => "typeof",
UnaryOperator::Void => "void",
UnaryOperator::Delete => "delete",
UnaryOperator::Tilde => "~",
}
}
#[inline]
pub const fn is_keyword_operator(self) -> bool {
matches!(
self,
UnaryOperator::Typeof | UnaryOperator::Void | UnaryOperator::Delete
)
}
}
#[derive(Debug, Clone)]
pub struct UnaryExpression<'arena> {
pub operator: UnaryOperator,
pub argument: &'arena Expression<'arena>,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BinaryOperator {
Plus = 0, Minus = 1, Star = 2, Slash = 3, Percent = 4, LessThan = 5, GreaterThan = 6, LessThanEquals = 7, GreaterThanEquals = 8, EqualsEquals = 9, EqualsEqualsEquals = 10, BangEquals = 11, BangEqualsEquals = 12, AmpersandAmpersand = 13, PipePipe = 14, QuestionQuestion = 15, Ampersand = 16, Pipe = 17, Caret = 18, LeftShift = 19, RightShift = 20, UnsignedRightShift = 21, StarStar = 22, Instanceof = 23, In = 24, }
impl BinaryOperator {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
BinaryOperator::Plus => "+",
BinaryOperator::Minus => "-",
BinaryOperator::Star => "*",
BinaryOperator::Slash => "/",
BinaryOperator::Percent => "%",
BinaryOperator::LessThan => "<",
BinaryOperator::GreaterThan => ">",
BinaryOperator::LessThanEquals => "<=",
BinaryOperator::GreaterThanEquals => ">=",
BinaryOperator::EqualsEquals => "==",
BinaryOperator::EqualsEqualsEquals => "===",
BinaryOperator::BangEquals => "!=",
BinaryOperator::BangEqualsEquals => "!==",
BinaryOperator::AmpersandAmpersand => "&&",
BinaryOperator::PipePipe => "||",
BinaryOperator::QuestionQuestion => "??",
BinaryOperator::Ampersand => "&",
BinaryOperator::Pipe => "|",
BinaryOperator::Caret => "^",
BinaryOperator::LeftShift => "<<",
BinaryOperator::RightShift => ">>",
BinaryOperator::UnsignedRightShift => ">>>",
BinaryOperator::StarStar => "**",
BinaryOperator::Instanceof => "instanceof",
BinaryOperator::In => "in",
}
}
pub fn precedence(&self) -> crate::ast::precedence::PrecedenceLevel {
crate::ast::precedence::get_precedence(*self)
}
pub fn can_flatten_with(&self, other: BinaryOperator) -> bool {
crate::ast::precedence::should_flatten(*self, other)
}
#[inline]
pub const fn rebalances_with(self, other: BinaryOperator) -> bool {
self.is_logical() && self as u8 == other as u8
}
#[inline]
pub const fn is_logical(self) -> bool {
matches!(
self,
BinaryOperator::AmpersandAmpersand
| BinaryOperator::PipePipe
| BinaryOperator::QuestionQuestion
)
}
#[inline]
pub const fn is_bitwise(self) -> bool {
matches!(
self,
BinaryOperator::Pipe
| BinaryOperator::Caret
| BinaryOperator::Ampersand
| BinaryOperator::LeftShift
| BinaryOperator::RightShift
| BinaryOperator::UnsignedRightShift
)
}
}
#[derive(Debug, Clone)]
pub struct BinaryExpression<'arena> {
pub left: &'arena Expression<'arena>,
pub operator: BinaryOperator,
pub right: &'arena Expression<'arena>,
pub relexes_as_type_arguments: bool,
pub may_close_type_arguments: bool,
}
impl<'arena> BinaryExpression<'arena> {
#[must_use]
pub fn rebalanced_right(&self) -> &'arena Expression<'arena> {
let mut right = self.right;
while let ExpressionKind::BinaryExpression(inner) = &right.kind {
if !self.operator.rebalances_with(inner.operator) {
break;
}
right = inner.right;
}
right
}
}
#[derive(Debug, Clone)]
pub struct CallExpression<'arena> {
pub callee: &'arena Expression<'arena>,
pub type_arguments: Option<TSTypeParameterInstantiation<'arena>>,
pub arguments: &'arena [&'arena Expression<'arena>],
pub optional: bool, }
#[derive(Debug, Clone)]
pub struct NewExpression<'arena> {
pub callee: &'arena Expression<'arena>,
pub type_arguments: Option<TSTypeParameterInstantiation<'arena>>,
pub arguments: &'arena [&'arena Expression<'arena>],
}
#[derive(Debug, Clone)]
pub struct ImportExpression<'arena> {
pub source: &'arena Expression<'arena>,
pub options: Option<&'arena Expression<'arena>>,
pub phase: super::modules::ImportPhase,
}
#[derive(Debug, Clone)]
pub struct MetaProperty<'arena> {
pub meta: Identifier<'arena>,
pub property: Identifier<'arena>,
}
#[derive(Debug, Clone)]
pub struct MemberExpression<'arena> {
pub object: &'arena Expression<'arena>,
pub property: &'arena Expression<'arena>,
pub computed: bool, pub optional: bool, }
#[derive(Debug, Clone)]
pub struct ConditionalExpression<'arena> {
pub test: &'arena Expression<'arena>,
pub consequent: &'arena Expression<'arena>,
pub alternate: &'arena Expression<'arena>,
}
#[derive(Debug, Clone)]
pub struct ArrowFunctionExpression<'arena> {
pub type_parameters: Option<TSTypeParameterDeclaration<'arena>>,
pub params: &'arena [Expression<'arena>],
pub body: ArrowFunctionBody<'arena>,
pub return_type: Option<TSTypeAnnotation<'arena>>,
pub r#async: bool,
pub params_start: Option<u32>,
pub arrow_token: u32,
}
#[derive(Debug, Clone)]
pub enum ArrowFunctionBody<'arena> {
Expression(&'arena Expression<'arena>),
BlockStatement(BlockStatement<'arena>),
}
impl<'arena> ArrowFunctionBody<'arena> {
pub fn span(&self) -> Span {
match self {
ArrowFunctionBody::Expression(expr) => expr.span(),
ArrowFunctionBody::BlockStatement(block) => block.span,
}
}
pub fn is_expression(&self) -> bool {
matches!(self, ArrowFunctionBody::Expression(_))
}
}
#[derive(Debug, Clone)]
pub struct FunctionExpression<'arena> {
pub id: Option<Identifier<'arena>>,
pub type_parameters: Option<TSTypeParameterDeclaration<'arena>>,
pub params: &'arena [Expression<'arena>],
pub return_type: Option<TSTypeAnnotation<'arena>>,
pub body: BlockStatement<'arena>,
pub generator: bool,
pub r#async: bool,
pub params_start: u32,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct SpreadElement<'arena> {
pub argument: &'arena Expression<'arena>,
pub span: Span,
}
impl SpreadElement<'_> {
pub fn paren_interior(&self) -> Span {
Span::new(self.argument.span().end, self.span.end)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PropertyKind {
#[default]
Init = 0,
Get = 1,
Set = 2,
}
impl PropertyKind {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
PropertyKind::Init => "init",
PropertyKind::Get => "get",
PropertyKind::Set => "set",
}
}
}
#[derive(Debug, Clone)]
pub struct Property<'arena> {
pub key: &'arena Expression<'arena>,
pub value: &'arena Expression<'arena>,
pub kind: PropertyKind, pub shorthand: bool, pub computed: bool, pub method: bool, pub span: Span,
}
#[derive(Debug, Clone)]
pub struct TemplateLiteral<'arena> {
pub quasis: &'arena [TemplateElement<'arena>],
pub expressions: &'arena [&'arena Expression<'arena>],
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum TemplateCooked<'arena> {
Verbatim,
Decoded(&'arena str),
Invalid,
}
#[derive(Debug, Clone)]
pub struct TemplateElement<'arena> {
pub raw_span: Span,
pub raw_trv: Option<&'arena str>,
pub cooked: TemplateCooked<'arena>,
pub has_newline: bool,
pub tail: bool,
pub span: Span,
}
impl<'arena> TemplateElement<'arena> {
#[inline]
pub fn raw<'s>(&self, source: &'s str) -> &'s str {
self.raw_span.extract(source)
}
#[inline]
pub fn trv<'s>(&'s self, source: &'s str) -> &'s str {
self.raw_trv.unwrap_or_else(|| self.raw(source))
}
#[inline]
pub fn cooked<'s>(&'s self, source: &'s str) -> Option<&'s str> {
match self.cooked {
TemplateCooked::Verbatim => Some(self.trv(source)),
TemplateCooked::Decoded(decoded) => Some(decoded),
TemplateCooked::Invalid => None,
}
}
}
#[derive(Debug, Clone)]
pub struct TaggedTemplateExpression<'arena> {
pub tag: &'arena Expression<'arena>,
pub type_arguments: Option<TSTypeParameterInstantiation<'arena>>,
pub quasi: TemplateLiteral<'arena>,
}
#[derive(Debug, Clone)]
pub struct AwaitExpression<'arena> {
pub argument: &'arena Expression<'arena>,
}
#[derive(Debug, Clone)]
pub struct YieldExpression<'arena> {
pub argument: Option<&'arena Expression<'arena>>,
pub delegate: bool,
}
#[derive(Debug, Clone)]
pub struct SequenceExpression<'arena> {
pub expressions: &'arena [&'arena Expression<'arena>],
}
#[derive(Debug, Clone)]
pub struct RegexLiteral {
pub pattern_span: Span,
pub flags_span: Span,
pub pattern_width: u16,
}
impl RegexLiteral {
#[inline]
pub fn pattern<'s>(&self, source: &'s str) -> &'s str {
self.pattern_span.extract(source)
}
#[inline]
pub fn flags<'s>(&self, source: &'s str) -> &'s str {
self.flags_span.extract(source)
}
}
#[derive(Debug, Clone)]
pub struct ThisExpression;
#[derive(Debug, Clone)]
pub struct Super;
#[derive(Debug, Clone)]
pub struct TSTypeAssertion<'arena> {
pub type_annotation: &'arena TSType<'arena>,
pub expression: &'arena Expression<'arena>,
}
#[derive(Debug, Clone)]
pub struct TSAsExpression<'arena> {
pub expression: &'arena Expression<'arena>,
pub type_annotation: &'arena TSType<'arena>,
}
#[derive(Debug, Clone)]
pub struct TSSatisfiesExpression<'arena> {
pub expression: &'arena Expression<'arena>,
pub type_annotation: &'arena TSType<'arena>,
}
#[derive(Debug, Clone)]
pub struct TSInstantiationExpression<'arena> {
pub expression: &'arena Expression<'arena>,
pub type_arguments: TSTypeParameterInstantiation<'arena>,
}
#[derive(Debug, Clone)]
pub struct TSNonNullExpression<'arena> {
pub expression: &'arena Expression<'arena>,
}
impl<'arena> TSNonNullExpression<'arena> {
pub fn seals_optional_chain(&self, span: Span) -> bool {
span.start < self.expression.span.start && self.expression.has_optional_in_chain()
}
}
#[derive(Debug, Clone)]
pub struct AssignmentExpression<'arena> {
pub left: &'arena Expression<'arena>,
pub operator: AssignmentOperator,
pub right: &'arena Expression<'arena>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum AssignmentOperator {
Assign = 0, AddAssign = 1, SubtractAssign = 2, MultiplyAssign = 3, DivideAssign = 4, RemainderAssign = 5, ExponentiateAssign = 6, LeftShiftAssign = 7, RightShiftAssign = 8, UnsignedRightShiftAssign = 9, BitwiseOrAssign = 10, BitwiseXorAssign = 11, BitwiseAndAssign = 12, LogicalOrAssign = 13, LogicalAndAssign = 14, NullishAssign = 15, }
impl AssignmentOperator {
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
AssignmentOperator::Assign => "=",
AssignmentOperator::AddAssign => "+=",
AssignmentOperator::SubtractAssign => "-=",
AssignmentOperator::MultiplyAssign => "*=",
AssignmentOperator::DivideAssign => "/=",
AssignmentOperator::RemainderAssign => "%=",
AssignmentOperator::ExponentiateAssign => "**=",
AssignmentOperator::LeftShiftAssign => "<<=",
AssignmentOperator::RightShiftAssign => ">>=",
AssignmentOperator::UnsignedRightShiftAssign => ">>>=",
AssignmentOperator::BitwiseOrAssign => "|=",
AssignmentOperator::BitwiseXorAssign => "^=",
AssignmentOperator::BitwiseAndAssign => "&=",
AssignmentOperator::LogicalOrAssign => "||=",
AssignmentOperator::LogicalAndAssign => "&&=",
AssignmentOperator::NullishAssign => "??=",
}
}
#[inline]
pub const fn as_str_with_leading_space(self) -> &'static str {
match self {
AssignmentOperator::Assign => " =",
AssignmentOperator::AddAssign => " +=",
AssignmentOperator::SubtractAssign => " -=",
AssignmentOperator::MultiplyAssign => " *=",
AssignmentOperator::DivideAssign => " /=",
AssignmentOperator::RemainderAssign => " %=",
AssignmentOperator::ExponentiateAssign => " **=",
AssignmentOperator::LeftShiftAssign => " <<=",
AssignmentOperator::RightShiftAssign => " >>=",
AssignmentOperator::UnsignedRightShiftAssign => " >>>=",
AssignmentOperator::BitwiseOrAssign => " |=",
AssignmentOperator::BitwiseXorAssign => " ^=",
AssignmentOperator::BitwiseAndAssign => " &=",
AssignmentOperator::LogicalOrAssign => " ||=",
AssignmentOperator::LogicalAndAssign => " &&=",
AssignmentOperator::NullishAssign => " ??=",
}
}
}