refactor(parser): start porting arrow function parsing from tsc (#3340)

relates #3320
This commit is contained in:
Boshen 2024-05-18 22:35:29 +08:00 committed by GitHub
parent 17f4b199b9
commit 9ced605487
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 372 additions and 331 deletions

View file

@ -28,7 +28,7 @@ fn main() -> std::io::Result<()> {
println!("Original:");
println!("{source_text}");
let options = CodegenOptions::default();
let options = CodegenOptions { enable_source_map: false, enable_typescript: true };
let printed =
Codegen::<false>::new("", &source_text, options.clone()).build(&ret.program).source_text;
println!("Printed:");

View file

@ -15,7 +15,10 @@ fn main() -> Result<(), String> {
let source_text = std::fs::read_to_string(path).map_err(|_| format!("Missing '{name}'"))?;
let allocator = Allocator::default();
let source_type = SourceType::from_path(path).unwrap();
let now = std::time::Instant::now();
let ret = Parser::new(&allocator, &source_text, source_type).parse();
let elapsed_time = now.elapsed();
println!("{} seconds.", elapsed_time.as_millis());
println!("AST:");
println!("{}", serde_json::to_string_pretty(&ret.program).unwrap());

View file

@ -0,0 +1,309 @@
use oxc_allocator::Box;
use oxc_ast::ast::*;
use oxc_diagnostics::Result;
use oxc_span::{GetSpan, Span};
use crate::{diagnostics, lexer::Kind, AstBuilder, ParserImpl};
use super::Tristate;
type ArrowFunctionHead<'a> = (
Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
Box<'a, FormalParameters<'a>>,
Option<Box<'a, TSTypeAnnotation<'a>>>,
bool,
Span,
);
impl<'a> ParserImpl<'a> {
pub(crate) fn is_parenthesized_arrow_function_expression(&mut self, r#async: bool) -> Tristate {
let offset = u8::from(r#async);
match self.nth_kind(offset) {
Kind::LParen => match self.nth_kind(offset + 1) {
// '()' is an arrow expression if followed by an '=>', a type annotation or body.
// Otherwise, a parenthesized expression with a missing inner expression
Kind::RParen => {
let kind = self.nth_kind(offset + 2);
if self.ts_enabled() && kind == Kind::Colon {
Tristate::Maybe
} else if matches!(kind, Kind::Arrow | Kind::LCurly) {
Tristate::True
} else {
Tristate::False
}
}
// Rest parameter
// '(...ident' is not a parenthesized expression
// '(...null' is a parenthesized expression
Kind::Dot3 => match self.nth_kind(offset + 1) {
Kind::Ident => Tristate::True,
kind if kind.is_literal() => Tristate::False,
_ => Tristate::Maybe,
},
// '([ ...', '({ ... } can either be a parenthesized object or array expression or a destructing parameter
Kind::LBrack | Kind::LCurly => Tristate::Maybe,
_ if self.nth_kind(offset + 1).is_binding_identifier()
|| self.nth_at(offset + 1, Kind::This) =>
{
match self.nth_kind(offset + 2) {
// '(a: ' must be a type annotation
Kind::Colon => Tristate::True,
// * '(a = ': an initializer or a parenthesized assignment expression
// * '(a, ': separator to next parameter or a parenthesized sequence expression
// * '(a)': a single parameter OR a parenthesized expression
Kind::Eq | Kind::Comma | Kind::RParen => Tristate::Maybe,
// '(a?:' | '(a?,' | '(a?=' | '(a?)'
Kind::Question
if matches!(
self.nth_kind(offset + 3),
Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen
) =>
{
Tristate::True
}
_ => Tristate::False,
}
}
_ => Tristate::False,
},
Kind::LAngle => {
let kind = self.nth_kind(offset + 1);
// `<const` for const type parameter from TypeScript 5.0
if kind == Kind::Const {
return Tristate::Maybe;
}
if !kind.is_identifier() {
return Tristate::False;
}
if self.source_type.is_jsx() {
return match self.nth_kind(offset + 2) {
Kind::Extends => {
let third_kind = self.nth_kind(offset + 3);
if matches!(third_kind, Kind::Eq | Kind::RAngle) {
Tristate::False
} else if third_kind.is_identifier() {
Tristate::Maybe
} else {
Tristate::True
}
}
Kind::Eq | Kind::Comma => Tristate::True,
_ => Tristate::False,
};
}
Tristate::Maybe
}
_ => Tristate::False,
}
}
pub(crate) fn is_parenthesized_arrow_function(&mut self) -> Tristate {
match self.cur_kind() {
Kind::LAngle | Kind::LParen => self.is_parenthesized_arrow_function_expression(false),
Kind::Async => {
let peeked = self.peek_token();
if !peeked.is_on_new_line && matches!(peeked.kind, Kind::LAngle | Kind::LParen) {
self.is_parenthesized_arrow_function_expression(true)
} else {
Tristate::False
}
}
_ => Tristate::False,
}
}
pub(crate) fn parse_parenthesized_arrow_function_head(
&mut self,
) -> Result<ArrowFunctionHead<'a>> {
let span = self.start_span();
let r#async = self.eat(Kind::Async);
let has_await = self.ctx.has_await();
self.ctx = self.ctx.union_await_if(r#async);
let type_parameters = self.parse_ts_type_parameters()?;
let (this_param, params) =
self.parse_formal_parameters(FormalParameterKind::ArrowFormalParameters)?;
if let Some(this_param) = this_param {
// const x = (this: number) => {};
self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span));
}
let return_type = self.parse_ts_return_type_annotation()?;
self.ctx = self.ctx.and_await(has_await);
if self.cur_token().is_on_new_line {
self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span()));
}
self.expect(Kind::Arrow)?;
Ok((type_parameters, params, return_type, r#async, span))
}
/// [ConciseBody](https://tc39.es/ecma262/#prod-ConciseBody)
/// [lookahead ≠ {] `ExpressionBody`[?In, ~Await]
/// { `FunctionBody`[~Yield, ~Await] }
/// `ExpressionBody`[In, Await] :
/// `AssignmentExpression`[?In, ~Yield, ?Await]
pub(crate) fn parse_arrow_function_body(
&mut self,
span: Span,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
params: Box<'a, FormalParameters<'a>>,
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
r#async: bool,
) -> Result<Expression<'a>> {
let has_await = self.ctx.has_await();
let has_yield = self.ctx.has_yield();
self.ctx = self.ctx.and_await(r#async).and_yield(false);
let expression = !self.at(Kind::LCurly);
let body = if expression {
let expr = self.parse_assignment_expression_or_higher()?;
let span = expr.span();
let expr_stmt = self.ast.expression_statement(span, expr);
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
} else {
self.parse_function_body()?
};
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
Ok(self.ast.arrow_function_expression(
self.end_span(span),
expression,
r#async,
params,
body,
type_parameters,
return_type,
))
}
/// Section [Arrow Function](https://tc39.es/ecma262/#sec-arrow-function-definitions)
/// `ArrowFunction`[In, Yield, Await] :
/// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In]
pub(crate) fn parse_parenthesized_arrow_function(&mut self) -> Result<Option<Expression<'a>>> {
let (type_parameters, params, return_type, r#async, span) =
self.parse_parenthesized_arrow_function_head()?;
self.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
.map(Some)
}
pub(crate) fn parse_single_param_function_expression(
&mut self,
span: Span,
r#async: bool,
generator: bool,
) -> Result<Expression<'a>> {
let has_await = self.ctx.has_await();
let has_yield = self.ctx.has_yield();
self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator);
let params_span = self.start_span();
let param = self.parse_binding_identifier()?;
let ident = self.ast.binding_pattern_identifier(param);
let pattern = self.ast.binding_pattern(ident, None, false);
let params_span = self.end_span(params_span);
let formal_parameter = self.ast.formal_parameter(
params_span,
pattern,
None,
false,
false,
AstBuilder::new_vec(&self.ast),
);
let params = self.ast.formal_parameters(
params_span,
FormalParameterKind::ArrowFormalParameters,
self.ast.new_vec_single(formal_parameter),
None,
);
self.expect(Kind::Arrow)?;
self.ctx = self.ctx.and_await(r#async).and_yield(generator);
let expression = !self.at(Kind::LCurly);
let body = if expression {
let expr = self.parse_assignment_expression_or_higher()?;
let span = expr.span();
let expr_stmt = self.ast.expression_statement(span, expr);
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
} else {
self.parse_function_body()?
};
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
Ok(self.ast.arrow_function_expression(
self.end_span(span),
expression,
r#async,
params,
body,
None,
None,
))
}
pub(crate) fn parse_possible_parenthesized_arrow_function_expression(
&mut self,
) -> Result<Option<Expression<'a>>> {
let pos = self.cur_token().start;
if self.state.not_parenthesized_arrow.contains(&pos) {
return Ok(None);
}
if let Ok((type_parameters, params, return_type, r#async, span)) =
self.try_parse(ParserImpl::parse_parenthesized_arrow_function_head)
{
return self
.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
.map(Some);
}
self.state.not_parenthesized_arrow.insert(pos);
Ok(None)
}
pub(crate) fn try_parse_parenthesized_arrow_function_expression(
&mut self,
) -> Result<Option<Expression<'a>>> {
match self.is_parenthesized_arrow_function() {
Tristate::False => Ok(None),
Tristate::True => self.parse_parenthesized_arrow_function(),
Tristate::Maybe => self.parse_possible_parenthesized_arrow_function_expression(),
}
}
pub(crate) fn try_parse_async_simple_arrow_function_expression(
&mut self,
) -> Result<Option<Expression<'a>>> {
let span = self.start_span();
if self.cur_kind().is_binding_identifier()
&& self.peek_at(Kind::Arrow)
&& !self.peek_token().is_on_new_line
{
self.parse_single_param_function_expression(span, false, false).map(Some)
} else if self.at_async_no_new_line()
&& self.peek_kind().is_binding_identifier()
&& !self.peek_token().is_on_new_line
&& self.nth_at(2, Kind::Arrow)
{
self.bump_any(); // bump async
let arrow_token = self.peek_token();
if arrow_token.is_on_new_line {
self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span()));
}
self.parse_single_param_function_expression(span, true, false).map(Some)
} else {
Ok(None)
}
}
}

View file

@ -132,7 +132,7 @@ impl<'a> ParserImpl<'a> {
left: BindingPattern<'a>,
) -> Result<BindingPattern<'a>> {
if self.eat(Kind::Eq) {
let expr = self.parse_assignment_expression_base()?;
let expr = self.parse_assignment_expression_or_higher()?;
Ok(self.ast.assignment_pattern(self.end_span(span), left, expr))
} else {
Ok(left)

View file

@ -467,7 +467,7 @@ impl<'a> ParserImpl<'a> {
r#abstract: bool,
) -> Result<ClassElement<'a>> {
let value =
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?;
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher()).transpose()?;
let r#type = if r#abstract {
AccessorPropertyType::TSAbstractAccessorProperty
} else {

View file

@ -29,7 +29,7 @@ impl<'a> ParserImpl<'a> {
let peeked = self.peek_kind();
// let = foo, let instanceof x, let + 1
if peeked.is_assignment_operator() || peeked.is_binary_operator() {
let expr = self.parse_assignment_expression_base()?;
let expr = self.parse_assignment_expression_or_higher()?;
self.parse_expression_statement(span, expr)
// let.a = 1, let()[a] = 1
} else if matches!(peeked, Kind::Dot | Kind::LParen) {
@ -118,7 +118,7 @@ impl<'a> ParserImpl<'a> {
};
let init =
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_base()).transpose()?;
self.eat(Kind::Eq).then(|| self.parse_assignment_expression_or_higher()).transpose()?;
if init.is_none() && decl_ctx.parent == VariableDeclarationParent::Statement {
// LexicalBinding[In, Yield, Await] :

View file

@ -11,7 +11,6 @@ use oxc_syntax::{
};
use super::{
function::IsParenthesizedArrowFunction,
grammar::CoverGrammar,
list::{ArrayExpressionList, CallArguments, SequenceExpressionList},
operator::{
@ -43,7 +42,7 @@ impl<'a> ParserImpl<'a> {
self.ctx = self.ctx.and_decorator(false);
}
let lhs = self.parse_assignment_expression_base()?;
let lhs = self.parse_assignment_expression_or_higher()?;
if !self.at(Kind::Comma) {
return Ok(lhs);
}
@ -923,58 +922,24 @@ impl<'a> ParserImpl<'a> {
if !self.eat(Kind::Question) {
return Ok(lhs);
}
let consequent =
self.context(Context::In, Context::empty(), Self::parse_assignment_expression_base)?;
let consequent = self.context(
Context::In,
Context::empty(),
Self::parse_assignment_expression_or_higher,
)?;
self.expect(Kind::Colon)?;
let alternate = self.parse_assignment_expression_base()?;
let alternate = self.parse_assignment_expression_or_higher()?;
Ok(self.ast.conditional_expression(self.end_span(span), lhs, consequent, alternate))
}
pub(crate) fn parse_assignment_expression_base(&mut self) -> Result<Expression<'a>> {
match self.is_parenthesized_arrow_function() {
IsParenthesizedArrowFunction::True => {
return self.parse_parenthesized_arrow_function();
}
IsParenthesizedArrowFunction::Maybe => {
let pos = self.cur_token().start;
if !self.state.not_parenthesized_arrow.contains(&pos) {
if let Ok((type_parameters, params, return_type, r#async, span)) =
self.try_parse(ParserImpl::parse_parenthesized_arrow_function_head)
{
return self.parse_arrow_function_body(
span,
type_parameters,
params,
return_type,
r#async,
);
}
self.state.not_parenthesized_arrow.insert(pos);
}
}
IsParenthesizedArrowFunction::False => {}
pub(crate) fn parse_assignment_expression_or_higher(&mut self) -> Result<Expression<'a>> {
if let Some(arrow_expr) = self.try_parse_parenthesized_arrow_function_expression()? {
return Ok(arrow_expr);
}
let span = self.start_span();
if self.cur_kind().is_binding_identifier()
&& self.peek_at(Kind::Arrow)
&& !self.peek_token().is_on_new_line
{
self.parse_single_param_function_expression(span, false, false)
} else if self.at_async_no_new_line()
&& self.peek_kind().is_binding_identifier()
&& !self.peek_token().is_on_new_line
&& self.nth_at(2, Kind::Arrow)
{
self.bump_any(); // bump async
let arrow_token = self.peek_token();
if arrow_token.is_on_new_line {
self.error(diagnostics::no_line_break_is_allowed_before_arrow(arrow_token.span()));
}
self.parse_single_param_function_expression(span, true, false)
} else {
self.parse_assignment_expression()
if let Some(arrow_expr) = self.try_parse_async_simple_arrow_function_expression()? {
return Ok(arrow_expr);
}
self.parse_assignment_expression()
}
/// `AssignmentExpression`[In, Yield, Await] :
@ -1011,7 +976,7 @@ impl<'a> ParserImpl<'a> {
self.bump_any();
let right = self.parse_assignment_expression_base()?;
let right = self.parse_assignment_expression_or_higher()?;
Ok(self.ast.assignment_expression(self.end_span(span), operator, left, right))
}
@ -1023,7 +988,7 @@ impl<'a> ParserImpl<'a> {
) -> Result<Expression<'a>> {
let mut expressions = self.ast.new_vec_single(first_expression);
while self.eat(Kind::Comma) {
let expression = self.parse_assignment_expression_base()?;
let expression = self.parse_assignment_expression_or_higher()?;
expressions.push(expression);
}
Ok(self.ast.sequence_expression(self.end_span(span), expressions))

View file

@ -1,28 +1,13 @@
use std::cell::Cell;
use oxc_allocator::Box;
use oxc_ast::{ast::*, AstBuilder};
use oxc_ast::ast::*;
use oxc_diagnostics::Result;
use oxc_span::{GetSpan, Span};
use oxc_span::Span;
use super::list::FormalParameterList;
use crate::{diagnostics, lexer::Kind, list::SeparatedList, Context, ParserImpl, StatementContext};
type ArrowFunctionHead<'a> = (
Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
Box<'a, FormalParameters<'a>>,
Option<Box<'a, TSTypeAnnotation<'a>>>,
bool,
Span,
);
#[derive(Debug, Clone, Copy)]
pub enum IsParenthesizedArrowFunction {
True,
False,
Maybe,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum FunctionKind {
Declaration { single_statement: bool },
@ -215,61 +200,6 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.function_expression(function))
}
pub(crate) fn parse_single_param_function_expression(
&mut self,
span: Span,
r#async: bool,
generator: bool,
) -> Result<Expression<'a>> {
let has_await = self.ctx.has_await();
let has_yield = self.ctx.has_yield();
self.ctx = self.ctx.union_await_if(r#async).union_yield_if(generator);
let params_span = self.start_span();
let param = self.parse_binding_identifier()?;
let ident = self.ast.binding_pattern_identifier(param);
let pattern = self.ast.binding_pattern(ident, None, false);
let params_span = self.end_span(params_span);
let formal_parameter = self.ast.formal_parameter(
params_span,
pattern,
None,
false,
false,
AstBuilder::new_vec(&self.ast),
);
let params = self.ast.formal_parameters(
params_span,
FormalParameterKind::ArrowFormalParameters,
self.ast.new_vec_single(formal_parameter),
None,
);
self.expect(Kind::Arrow)?;
self.ctx = self.ctx.and_await(r#async).and_yield(generator);
let expression = !self.at(Kind::LCurly);
let body = if expression {
let expr = self.parse_assignment_expression_base()?;
let span = expr.span();
let expr_stmt = self.ast.expression_statement(span, expr);
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
} else {
self.parse_function_body()?
};
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
Ok(self.ast.arrow_function_expression(
self.end_span(span),
expression,
r#async,
params,
body,
None,
None,
))
}
/// Section 15.4 Method Definitions
/// `ClassElementName` ( `UniqueFormalParameters` ) { `FunctionBody` }
/// `GeneratorMethod`
@ -324,7 +254,7 @@ impl<'a> ParserImpl<'a> {
);
if !not_assignment_expr || delegate {
self.ctx = self.ctx.union_yield_if(true);
argument = Some(self.parse_assignment_expression_base()?);
argument = Some(self.parse_assignment_expression_or_higher()?);
self.ctx = self.ctx.and_yield(has_yield);
}
}
@ -356,191 +286,4 @@ impl<'a> ParserImpl<'a> {
id
}
pub(crate) fn is_parenthesized_arrow_function_expression(
&mut self,
r#async: bool,
) -> IsParenthesizedArrowFunction {
let offset = u8::from(r#async);
match self.nth_kind(offset) {
Kind::LParen => match self.nth_kind(offset + 1) {
// '()' is an arrow expression if followed by an '=>', a type annotation or body.
// Otherwise, a parenthesized expression with a missing inner expression
Kind::RParen => {
let kind = self.nth_kind(offset + 2);
if self.ts_enabled() && kind == Kind::Colon {
IsParenthesizedArrowFunction::Maybe
} else if matches!(kind, Kind::Arrow | Kind::LCurly) {
IsParenthesizedArrowFunction::True
} else {
IsParenthesizedArrowFunction::False
}
}
// Rest parameter
// '(...ident' is not a parenthesized expression
// '(...null' is a parenthesized expression
Kind::Dot3 => match self.nth_kind(offset + 1) {
Kind::Ident => IsParenthesizedArrowFunction::True,
kind if kind.is_literal() => IsParenthesizedArrowFunction::False,
_ => IsParenthesizedArrowFunction::Maybe,
},
// '([ ...', '({ ... } can either be a parenthesized object or array expression or a destructing parameter
Kind::LBrack | Kind::LCurly => IsParenthesizedArrowFunction::Maybe,
_ if self.nth_kind(offset + 1).is_binding_identifier()
|| self.nth_at(offset + 1, Kind::This) =>
{
match self.nth_kind(offset + 2) {
// '(a: ' must be a type annotation
Kind::Colon => IsParenthesizedArrowFunction::True,
// * '(a = ': an initializer or a parenthesized assignment expression
// * '(a, ': separator to next parameter or a parenthesized sequence expression
// * '(a)': a single parameter OR a parenthesized expression
Kind::Eq | Kind::Comma | Kind::RParen => {
IsParenthesizedArrowFunction::Maybe
}
// '(a?:' | '(a?,' | '(a?=' | '(a?)'
Kind::Question
if matches!(
self.nth_kind(offset + 3),
Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen
) =>
{
IsParenthesizedArrowFunction::True
}
_ => IsParenthesizedArrowFunction::False,
}
}
_ => IsParenthesizedArrowFunction::False,
},
Kind::LAngle => {
let kind = self.nth_kind(offset + 1);
// `<const` for const type parameter from TypeScript 5.0
if kind == Kind::Const {
return IsParenthesizedArrowFunction::Maybe;
}
if !kind.is_identifier() {
return IsParenthesizedArrowFunction::False;
}
if self.source_type.is_jsx() {
return match self.nth_kind(offset + 2) {
Kind::Extends => {
let third_kind = self.nth_kind(offset + 3);
if matches!(third_kind, Kind::Eq | Kind::RAngle) {
IsParenthesizedArrowFunction::False
} else if third_kind.is_identifier() {
IsParenthesizedArrowFunction::Maybe
} else {
IsParenthesizedArrowFunction::True
}
}
Kind::Eq | Kind::Comma => IsParenthesizedArrowFunction::True,
_ => IsParenthesizedArrowFunction::False,
};
}
IsParenthesizedArrowFunction::Maybe
}
_ => IsParenthesizedArrowFunction::False,
}
}
pub(crate) fn is_parenthesized_arrow_function(&mut self) -> IsParenthesizedArrowFunction {
match self.cur_kind() {
Kind::LAngle | Kind::LParen => self.is_parenthesized_arrow_function_expression(false),
Kind::Async => {
let peeked = self.peek_token();
if !peeked.is_on_new_line && matches!(peeked.kind, Kind::LAngle | Kind::LParen) {
self.is_parenthesized_arrow_function_expression(true)
} else {
IsParenthesizedArrowFunction::False
}
}
_ => IsParenthesizedArrowFunction::False,
}
}
pub(crate) fn parse_parenthesized_arrow_function_head(
&mut self,
) -> Result<ArrowFunctionHead<'a>> {
let span = self.start_span();
let r#async = self.eat(Kind::Async);
let has_await = self.ctx.has_await();
self.ctx = self.ctx.union_await_if(r#async);
let type_parameters = self.parse_ts_type_parameters()?;
let (this_param, params) =
self.parse_formal_parameters(FormalParameterKind::ArrowFormalParameters)?;
if let Some(this_param) = this_param {
// const x = (this: number) => {};
self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span));
}
let return_type = self.parse_ts_return_type_annotation()?;
self.ctx = self.ctx.and_await(has_await);
if self.cur_token().is_on_new_line {
self.error(diagnostics::lineterminator_before_arrow(self.cur_token().span()));
}
self.expect(Kind::Arrow)?;
Ok((type_parameters, params, return_type, r#async, span))
}
/// [ConciseBody](https://tc39.es/ecma262/#prod-ConciseBody)
/// [lookahead ≠ {] `ExpressionBody`[?In, ~Await]
/// { `FunctionBody`[~Yield, ~Await] }
/// `ExpressionBody`[In, Await] :
/// `AssignmentExpression`[?In, ~Yield, ?Await]
pub(crate) fn parse_arrow_function_body(
&mut self,
span: Span,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
params: Box<'a, FormalParameters<'a>>,
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
r#async: bool,
) -> Result<Expression<'a>> {
let has_await = self.ctx.has_await();
let has_yield = self.ctx.has_yield();
self.ctx = self.ctx.and_await(r#async).and_yield(false);
let expression = !self.at(Kind::LCurly);
let body = if expression {
let expr = self.parse_assignment_expression_base()?;
let span = expr.span();
let expr_stmt = self.ast.expression_statement(span, expr);
self.ast.function_body(span, self.ast.new_vec(), self.ast.new_vec_single(expr_stmt))
} else {
self.parse_function_body()?
};
self.ctx = self.ctx.and_await(has_await).and_yield(has_yield);
Ok(self.ast.arrow_function_expression(
self.end_span(span),
expression,
r#async,
params,
body,
type_parameters,
return_type,
))
}
/// Section [Arrow Function](https://tc39.es/ecma262/#sec-arrow-function-definitions)
/// `ArrowFunction`[In, Yield, Await] :
/// `ArrowParameters`[?Yield, ?Await] [no `LineTerminator` here] => `ConciseBody`[?In]
pub(crate) fn parse_parenthesized_arrow_function(&mut self) -> Result<Expression<'a>> {
let (type_parameters, params, return_type, r#async, span) =
self.parse_parenthesized_arrow_function_head()?;
self.parse_arrow_function_body(span, type_parameters, params, return_type, r#async)
}
}

View file

@ -106,7 +106,7 @@ impl<'a> SeparatedList<'a> for ArrayExpressionList<'a> {
let element = match p.cur_kind() {
Kind::Comma => Ok(p.parse_elision()),
Kind::Dot3 => p.parse_spread_element().map(ArrayExpressionElement::SpreadElement),
_ => p.parse_assignment_expression_base().map(ArrayExpressionElement::from),
_ => p.parse_assignment_expression_or_higher().map(ArrayExpressionElement::from),
};
if p.at(Kind::Comma) && p.peek_at(self.close()) {
@ -188,7 +188,7 @@ impl<'a> SeparatedList<'a> for CallArguments<'a> {
}
result
} else {
p.parse_assignment_expression_base().map(Argument::from)
p.parse_assignment_expression_or_higher().map(Argument::from)
};
self.elements.push(element?);
Ok(())
@ -215,7 +215,7 @@ impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> {
// read everything as expression and map to it to either
// ParenthesizedExpression or ArrowFormalParameters later
fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> {
let element = p.parse_assignment_expression_base()?;
let element = p.parse_assignment_expression_or_higher()?;
self.elements.push(element);
Ok(())
}

View file

@ -5,6 +5,7 @@
mod grammar;
pub mod list;
mod arrow;
mod binding;
mod class;
pub mod declaration;
@ -14,3 +15,10 @@ mod module;
mod object;
mod operator;
mod statement;
#[derive(Debug, Clone, Copy)]
pub enum Tristate {
True,
False,
Maybe,
}

View file

@ -18,10 +18,10 @@ impl<'a> ParserImpl<'a> {
let has_in = self.ctx.has_in();
self.ctx = self.ctx.and_in(true);
let expression = self.parse_assignment_expression_base()?;
let expression = self.parse_assignment_expression_or_higher()?;
let mut arguments = self.ast.new_vec();
if self.eat(Kind::Comma) && !self.at(Kind::RParen) {
arguments.push(self.parse_assignment_expression_base()?);
arguments.push(self.parse_assignment_expression_or_higher()?);
}
self.ctx = self.ctx.and_in(has_in);
@ -149,7 +149,7 @@ impl<'a> ParserImpl<'a> {
) -> Result<Box<'a, TSExportAssignment<'a>>> {
self.expect(Kind::Eq)?;
let expression = self.parse_assignment_expression_base()?;
let expression = self.parse_assignment_expression_or_higher()?;
self.asi()?;
Ok(self.ast.alloc(TSExportAssignment { span: self.end_span(start_span), expression }))
@ -337,7 +337,7 @@ impl<'a> ParserImpl<'a> {
.map(ExportDefaultDeclarationKind::FunctionDeclaration)?,
_ => {
let decl = self
.parse_assignment_expression_base()
.parse_assignment_expression_or_higher()
.map(ExportDefaultDeclarationKind::from)?;
self.asi()?;
decl

View file

@ -86,7 +86,7 @@ impl<'a> ParserImpl<'a> {
pub(crate) fn parse_spread_element(&mut self) -> Result<Box<'a, SpreadElement<'a>>> {
let span = self.start_span();
self.bump_any(); // advance `...`
let argument = self.parse_assignment_expression_base()?;
let argument = self.parse_assignment_expression_or_higher()?;
Ok(self.ast.spread_element(self.end_span(span), argument))
}
@ -102,7 +102,7 @@ impl<'a> ParserImpl<'a> {
let value = Expression::Identifier(self.ast.alloc(identifier.clone()));
// CoverInitializedName ({ foo = bar })
let init = if self.eat(Kind::Eq) {
let right = self.parse_assignment_expression_base()?;
let right = self.parse_assignment_expression_or_higher()?;
let left = AssignmentTarget::AssignmentTargetIdentifier(self.ast.alloc(identifier));
Some(self.ast.assignment_expression(
self.end_span(span),
@ -134,7 +134,7 @@ impl<'a> ParserImpl<'a> {
computed: bool,
) -> Result<Box<'a, ObjectProperty<'a>>> {
self.bump_any(); // bump `:`
let value = self.parse_assignment_expression_base()?;
let value = self.parse_assignment_expression_or_higher()?;
Ok(self.ast.object_property(
self.end_span(span),
PropertyKind::Init,
@ -172,8 +172,11 @@ impl<'a> ParserImpl<'a> {
pub(crate) fn parse_computed_property_name(&mut self) -> Result<Expression<'a>> {
self.bump_any(); // advance `[`
let expression =
self.context(Context::In, Context::empty(), Self::parse_assignment_expression_base)?;
let expression = self.context(
Context::In,
Context::empty(),
Self::parse_assignment_expression_or_higher,
)?;
self.expect(Kind::RBrack)?;
Ok(expression)

View file

@ -389,7 +389,7 @@ impl<'a> ParserImpl<'a> {
let right = if is_for_in {
self.parse_expression()
} else {
self.parse_assignment_expression_base()
self.parse_assignment_expression_or_higher()
}?;
self.expect(Kind::RParen)?;

View file

@ -42,8 +42,11 @@ impl<'a> ParserImpl<'a> {
let span = self.start_span();
let id = self.parse_ts_enum_member_name()?;
let initializer =
if self.eat(Kind::Eq) { Some(self.parse_assignment_expression_base()?) } else { None };
let initializer = if self.eat(Kind::Eq) {
Some(self.parse_assignment_expression_or_higher()?)
} else {
None
};
Ok(TSEnumMember { span: self.end_span(span), id, initializer })
}

View file

@ -0,0 +1 @@
m< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< mm< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< m$<{3[ $<{3[ m< m$<{3[

View file

@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 16/16 (100.00%)
Positive Passed: 16/16 (100.00%)
Negative Passed: 8/8 (100.00%)
Negative Passed: 9/9 (100.00%)
× Unexpected token
╭─[fail/oxc-169.js:2:1]
@ -99,6 +99,12 @@ Negative Passed: 8/8 (100.00%)
21 │ & import("pkg", {"resolution-mode": "import"}).ImportInterface;
╰────
× Unexpected token
╭─[fail/oxc-3320.tsx:1:8]
1 │ m< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< mm< $<{3[ $<{3[ $<{3[ m< m$<{3[ m< m$<{3[ $<{3[ m< m$<{3[
· ─
╰────
× The keyword 'let' is reserved
╭─[fail/oxc.js:1:1]
1 │ let.a = 1;