diff --git a/src/expr.rs b/src/expr.rs index 5a9f69b..b6cd2ec 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -89,6 +89,7 @@ pub enum Expr { Update(UpdateExpr), /// yield a value from inside of a generator function Yield(YieldExpr), + OptionalChain(Box>), } impl IntoAllocated for Expr @@ -134,6 +135,7 @@ where Expr::Unary(inner) => Expr::Unary(inner.into_allocated()), Expr::Update(inner) => Expr::Update(inner.into_allocated()), Expr::Yield(inner) => Expr::Yield(inner.into_allocated()), + Expr::OptionalChain(inner) => Expr::OptionalChain(inner.into_allocated()), } } } @@ -602,6 +604,7 @@ where } } } + /// A Template literal preceded by a function identifier /// see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) for more details #[derive(PartialEq, Debug, Clone)] diff --git a/src/lib.rs b/src/lib.rs index 0908e4a..d187def 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -367,6 +367,9 @@ pub enum AssignOp { XOrEqual, AndEqual, PowerOfEqual, + DoubleAmpersandEqual, + DoublePipeEqual, + DoubleQuestionmarkEqual, } /// The available logical operators @@ -375,6 +378,7 @@ pub enum AssignOp { pub enum LogicalOp { Or, And, + NullishCoalescing, } /// The available operations for `Binary` Exprs diff --git a/src/spanned/convert.rs b/src/spanned/convert.rs index 2be8578..2d1f038 100644 --- a/src/spanned/convert.rs +++ b/src/spanned/convert.rs @@ -219,6 +219,7 @@ mod expr { Expr::Update(inner) => Self::Update(inner.into()), Expr::Yield(inner) => Self::Yield(inner.into()), Expr::Wrapped(inner) => inner.expr.into(), + Expr::OptionalChain(inner) => Self::OptionalChain(Box::new((*inner.expr).into())), } } } @@ -692,6 +693,9 @@ impl From for crate::AssignOp { AssignOp::XOrEqual(_) => Self::XOrEqual, AssignOp::AndEqual(_) => Self::AndEqual, AssignOp::PowerOfEqual(_) => Self::PowerOfEqual, + AssignOp::DoubleAmpersandEqual(_) => Self::DoubleAmpersandEqual, + AssignOp::DoublePipeEqual(_) => Self::DoublePipeEqual, + AssignOp::DoubleQuestionmarkEqual(_) => Self::DoubleQuestionmarkEqual, } } } @@ -701,6 +705,7 @@ impl From for crate::LogicalOp { match other { LogicalOp::Or(_) => Self::Or, LogicalOp::And(_) => Self::And, + LogicalOp::NullishCoalescing(_) => Self::NullishCoalescing, } } } diff --git a/src/spanned/expr.rs b/src/spanned/expr.rs index ba3cb33..1a6e8aa 100644 --- a/src/spanned/expr.rs +++ b/src/spanned/expr.rs @@ -3,10 +3,10 @@ use crate::spanned::{Class, Func, FuncArg, FuncBody, Ident}; use crate::IntoAllocated; use super::tokens::{ - AssignOp, Asterisk, Async, Await, BinaryOp, CloseBrace, CloseBracket, CloseParen, Colon, Comma, - Ellipsis, False, FatArrow, ForwardSlash, Get, LogicalOp, New, Null, OpenBrace, OpenBracket, - OpenParen, Period, QuasiQuote, QuestionMark, Quote, Set, Static, Super, This, Token, True, - UnaryOp, UpdateOp, Yield, + self, AssignOp, Asterisk, Async, Await, BinaryOp, CloseBrace, CloseBracket, CloseParen, Colon, + Comma, Ellipsis, False, FatArrow, ForwardSlash, Get, LogicalOp, New, Null, OpenBrace, + OpenBracket, OpenParen, Period, QuasiQuote, QuestionMark, Quote, Set, Static, Super, This, + Token, True, UnaryOp, UpdateOp, Yield, }; use super::{FuncArgEntry, ListEntry, Node, Slice, SourceLocation}; #[cfg(feature = "serde")] @@ -97,6 +97,7 @@ pub enum Expr { Wrapped(Box>), /// yield a value from inside of a generator function Yield(YieldExpr), + OptionalChain(OptionalChain), } impl IntoAllocated for Expr @@ -139,6 +140,7 @@ where Expr::Update(inner) => Expr::Update(inner.into_allocated()), Expr::Wrapped(inner) => Expr::Wrapped(inner.into_allocated()), Expr::Yield(inner) => Expr::Yield(inner.into_allocated()), + Expr::OptionalChain(inner) => Expr::OptionalChain(inner.into_allocated()), } } } @@ -172,6 +174,7 @@ impl Node for Expr { Expr::Update(inner) => inner.loc(), Expr::Yield(inner) => inner.loc(), Expr::Wrapped(inner) => inner.loc(), + Expr::OptionalChain(inner) => inner.loc(), } } } @@ -1008,6 +1011,34 @@ impl Node for MemberIndexer { } } +#[derive(PartialEq, Debug, Clone)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +pub struct OptionalChain { + pub expr: Box>, + pub op: tokens::QuestionMarkDot, +} + +impl IntoAllocated for OptionalChain +where + T: ToString, +{ + type Allocated = OptionalChain; + fn into_allocated(self) -> Self::Allocated { + OptionalChain { + expr: Box::new((*self.expr).into_allocated()), + op: self.op, + } + } +} + +impl Node for OptionalChain { + fn loc(&self) -> SourceLocation { + let start = self.expr.loc().start; + let end = self.op.end(); + SourceLocation { start, end } + } +} + /// A ternery expression /// ```js /// var a = true ? 'stuff' : 'things'; diff --git a/src/spanned/tokens.rs b/src/spanned/tokens.rs index 72e6da6..4a05ec5 100644 --- a/src/spanned/tokens.rs +++ b/src/spanned/tokens.rs @@ -162,6 +162,7 @@ define_token!(CloseBracket, "]"); define_token!(Colon, ":"); define_token!(Comma, ","); define_token!(DoubleAmpersand, "&&"); +define_token!(DoubleAmpersandEqual, "&&="); define_token!(DoubleAsterisk, "**"); define_token!(DoubleAsteriskEqual, "**="); define_token!(DoubleEqual, "=="); @@ -171,6 +172,9 @@ define_token!(DoubleGreaterThanEqual, ">>="); define_token!(DoubleLessThan, "<<"); define_token!(DoubleLessThanEqual, "<<="); define_token!(DoublePipe, "||"); +define_token!(DoublePipeEqual, "||="); +define_token!(DoubleQuestionmark, "??"); +define_token!(DoubleQuestionmarkEqual, "??="); define_token!(DoubleQuote, "\""); define_token!(Ellipsis, "..."); define_token!(Equal, "="); @@ -194,6 +198,7 @@ define_token!(PipeEqual, "|="); define_token!(Plus, "+"); define_token!(PlusEqual, "+="); define_token!(QuestionMark, "?"); +define_token!(QuestionMarkDot, "?."); define_token!(Semicolon, ";"); define_token!(SingleQuote, "'"); define_token!(Tilde, "~"); @@ -282,6 +287,9 @@ pub enum AssignOp { XOrEqual(CaretEqual), AndEqual(AmpersandEqual), PowerOfEqual(DoubleAsteriskEqual), + DoubleAmpersandEqual(DoubleAmpersandEqual), + DoublePipeEqual(DoublePipeEqual), + DoubleQuestionmarkEqual(DoubleQuestionmarkEqual), } impl Node for AssignOp { @@ -300,6 +308,9 @@ impl Node for AssignOp { AssignOp::XOrEqual(tok) => tok.loc(), AssignOp::AndEqual(tok) => tok.loc(), AssignOp::PowerOfEqual(tok) => tok.loc(), + AssignOp::DoubleAmpersandEqual(tok) => tok.loc(), + AssignOp::DoublePipeEqual(tok) => tok.loc(), + AssignOp::DoubleQuestionmarkEqual(tok) => tok.loc(), } } } @@ -310,6 +321,7 @@ impl Node for AssignOp { pub enum LogicalOp { Or(DoublePipe), And(DoubleAmpersand), + NullishCoalescing(DoubleQuestionmark), } impl Node for LogicalOp { @@ -317,6 +329,7 @@ impl Node for LogicalOp { match self { LogicalOp::Or(tok) => tok.loc(), LogicalOp::And(tok) => tok.loc(), + LogicalOp::NullishCoalescing(tok) => tok.loc(), } } }