diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index aa05b880f..45650dda2 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -344,8 +344,8 @@ impl<'a> ParserImpl<'a> { where F: Fn(&mut Self) -> Result>, { - let mut list = self.ast.new_vec(); self.expect(open)?; + let mut list = self.ast.new_vec(); loop { let kind = self.cur_kind(); if kind == close || kind == Kind::Eof { @@ -360,4 +360,37 @@ impl<'a> ParserImpl<'a> { self.expect(close)?; Ok(list) } + + pub(crate) fn parse_delimited_list( + &mut self, + close: Kind, + separator: Kind, + trailing_separator: bool, + f: F, + ) -> Result> + where + F: Fn(&mut Self) -> Result, + { + let mut list = self.ast.new_vec(); + let mut first = true; + loop { + let kind = self.cur_kind(); + if kind == close || kind == Kind::Eof { + break; + } + if first { + first = false; + } else { + if !trailing_separator && self.at(separator) && self.peek_at(close) { + break; + } + self.expect(separator)?; + if self.at(close) { + break; + } + } + list.push(f(self)?); + } + Ok(list) + } } diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index a638fc084..9bbbb5d77 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -12,7 +12,6 @@ use oxc_syntax::{ use super::{ grammar::CoverGrammar, - list::{ArrayExpressionList, CallArguments, SequenceExpressionList}, operator::{ kind_to_precedence, map_assignment_operator, map_binary_operator, map_logical_operator, map_unary_operator, map_update_operator, @@ -21,7 +20,6 @@ use super::{ use crate::{ diagnostics, lexer::{parse_big_int, parse_float, parse_int, Kind}, - list::SeparatedList, Context, ParserImpl, }; @@ -203,9 +201,17 @@ impl<'a> ParserImpl<'a> { } fn parse_parenthesized_expression(&mut self, span: Span) -> Result> { - let list = self.context(Context::In, Context::Decorator, SequenceExpressionList::parse)?; + self.expect(Kind::LParen)?; + let mut expressions = self.context(Context::In, Context::Decorator, |p| { + p.parse_delimited_list( + Kind::RParen, + Kind::Comma, + /* trailing_separator */ false, + Self::parse_assignment_expression_or_higher, + ) + })?; + self.expect(Kind::RParen)?; - let mut expressions = list.elements; let paren_span = self.end_span(span); if expressions.is_empty() { @@ -360,8 +366,30 @@ impl<'a> ParserImpl<'a> { /// [ `ElementList`[?Yield, ?Await] , Elisionopt ] pub(crate) fn parse_array_expression(&mut self) -> Result> { let span = self.start_span(); - let list = self.context(Context::In, Context::empty(), ArrayExpressionList::parse)?; - Ok(self.ast.array_expression(self.end_span(span), list.elements, list.trailing_comma)) + self.expect(Kind::LBrack)?; + let elements = self.context(Context::In, Context::empty(), |p| { + p.parse_delimited_list( + Kind::RBrack, + Kind::Comma, + /* trailing_separator */ false, + Self::parse_array_expression_element, + ) + })?; + let trailing_comma = self.at(Kind::Comma).then(|| { + let span = self.start_span(); + self.bump_any(); + self.end_span(span) + }); + self.expect(Kind::RBrack)?; + Ok(self.ast.array_expression(self.end_span(span), elements, trailing_comma)) + } + + fn parse_array_expression_element(&mut self) -> Result> { + match self.cur_kind() { + Kind::Comma => Ok(self.parse_elision()), + Kind::Dot3 => self.parse_spread_element().map(ArrayExpressionElement::SpreadElement), + _ => self.parse_assignment_expression_or_higher().map(ArrayExpressionElement::from), + } } /// Elision : @@ -677,10 +705,19 @@ impl<'a> ParserImpl<'a> { } // parse `new ident` without arguments - let arguments = if self.at(Kind::LParen) { + let arguments = if self.eat(Kind::LParen) { // ArgumentList[Yield, Await] : // AssignmentExpression[+In, ?Yield, ?Await] - self.context(Context::In, Context::empty(), CallArguments::parse)?.elements + let call_arguments = self.context(Context::In, Context::empty(), |p| { + p.parse_delimited_list( + Kind::RParen, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_call_argument, + ) + })?; + self.expect(Kind::RParen)?; + call_arguments } else { self.ast.new_vec() }; @@ -749,16 +786,33 @@ impl<'a> ParserImpl<'a> { ) -> Result> { // ArgumentList[Yield, Await] : // AssignmentExpression[+In, ?Yield, ?Await] - let call_arguments = self.context(Context::In, Context::Decorator, CallArguments::parse)?; + self.expect(Kind::LParen)?; + let call_arguments = self.context(Context::In, Context::Decorator, |p| { + p.parse_delimited_list( + Kind::RParen, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_call_argument, + ) + })?; + self.expect(Kind::RParen)?; Ok(self.ast.call_expression( self.end_span(lhs_span), lhs, - call_arguments.elements, + call_arguments, optional, type_parameters, )) } + fn parse_call_argument(&mut self) -> Result> { + if self.at(Kind::Dot3) { + self.parse_spread_element().map(Argument::SpreadElement) + } else { + self.parse_assignment_expression_or_higher().map(Argument::from) + } + } + /// Section 13.4 Update Expression fn parse_update_expression(&mut self, lhs_span: Span) -> Result> { let kind = self.cur_kind(); diff --git a/crates/oxc_parser/src/js/list.rs b/crates/oxc_parser/src/js/list.rs index 2fa6a719a..119f50b31 100644 --- a/crates/oxc_parser/src/js/list.rs +++ b/crates/oxc_parser/src/js/list.rs @@ -1,47 +1,10 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; use oxc_diagnostics::Result; -use oxc_span::{Atom, GetSpan, Span}; -use rustc_hash::FxHashMap; +use oxc_span::GetSpan; use crate::{diagnostics, lexer::Kind, list::SeparatedList, modifiers::ModifierFlags, ParserImpl}; -/// ObjectExpression.properties -pub struct ObjectExpressionProperties<'a> { - pub elements: Vec<'a, ObjectPropertyKind<'a>>, - pub trailing_comma: Option, -} - -impl<'a> SeparatedList<'a> for ObjectExpressionProperties<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec(), trailing_comma: None } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let element = match p.cur_kind() { - Kind::Dot3 => p.parse_spread_element().map(ObjectPropertyKind::SpreadProperty), - _ => p.parse_property_definition().map(ObjectPropertyKind::ObjectProperty), - }?; - - if p.at(Kind::Comma) && p.peek_at(self.close()) { - let span = p.start_span(); - p.bump_any(); - self.trailing_comma = Some(p.end_span(span)); - } - - self.elements.push(element); - Ok(()) - } -} - /// ObjectPattern.properties pub struct ObjectPatternProperties<'a> { pub elements: Vec<'a, BindingProperty<'a>>, @@ -78,43 +41,6 @@ impl<'a> SeparatedList<'a> for ObjectPatternProperties<'a> { } } -/// ArrayExpression.elements -pub struct ArrayExpressionList<'a> { - pub elements: Vec<'a, ArrayExpressionElement<'a>>, - pub trailing_comma: Option, -} - -impl<'a> SeparatedList<'a> for ArrayExpressionList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec(), trailing_comma: None } - } - - fn open(&self) -> Kind { - Kind::LBrack - } - - fn close(&self) -> Kind { - Kind::RBrack - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - 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_or_higher().map(ArrayExpressionElement::from), - }; - - if p.at(Kind::Comma) && p.peek_at(self.close()) { - let span = p.start_span(); - p.bump_any(); - self.trailing_comma = Some(p.end_span(span)); - } - - self.elements.push(element?); - Ok(()) - } -} - /// ArrayPattern.elements pub struct ArrayPatternList<'a> { pub elements: Vec<'a, Option>>, @@ -154,87 +80,6 @@ impl<'a> SeparatedList<'a> for ArrayPatternList<'a> { } } -/// Section 13.3 Arguments for `CallExpression`, `NewExpression` -pub struct CallArguments<'a> { - pub elements: Vec<'a, Argument<'a>>, - pub rest_element_with_trilling_comma: Option, -} - -impl<'a> SeparatedList<'a> for CallArguments<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec(), rest_element_with_trilling_comma: None } - } - - fn open(&self) -> Kind { - Kind::LParen - } - - fn close(&self) -> Kind { - Kind::RParen - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let element = if p.at(Kind::Dot3) { - let result = p.parse_spread_element().map(Argument::SpreadElement); - if p.at(Kind::Comma) { - if let Ok(Argument::SpreadElement(argument)) = &result { - self.rest_element_with_trilling_comma = Some(argument.span); - } - } - result - } else { - p.parse_assignment_expression_or_higher().map(Argument::from) - }; - self.elements.push(element?); - Ok(()) - } -} - -pub struct SequenceExpressionList<'a> { - pub elements: Vec<'a, Expression<'a>>, -} - -impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LParen - } - - fn close(&self) -> Kind { - Kind::RParen - } - - // 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_or_higher(); - self.elements.push(element?); - Ok(()) - } - - fn parse_list(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - p.expect(self.open())?; - - let mut first = true; - - while !p.at(self.close()) && !p.at(Kind::Eof) { - if first { - first = false; - } else { - p.expect(self.separator())?; - } - - self.parse_element(p)?; - } - - p.expect(self.close())?; - Ok(()) - } -} - /// Function Parameters pub struct FormalParameterList<'a> { pub elements: Vec<'a, FormalParameter<'a>>, @@ -301,122 +146,3 @@ impl<'a> SeparatedList<'a> for FormalParameterList<'a> { Ok(()) } } - -/// [Assert Entries](https://tc39.es/proposal-import-assertions) -pub struct AssertEntries<'a> { - pub elements: Vec<'a, ImportAttribute<'a>>, - keys: FxHashMap, Span>, -} - -impl<'a> SeparatedList<'a> for AssertEntries<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec(), keys: FxHashMap::default() } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let span = p.start_span(); - let key = match p.cur_kind() { - Kind::Str => ImportAttributeKey::StringLiteral(p.parse_literal_string()?), - _ => ImportAttributeKey::Identifier(p.parse_identifier_name()?), - }; - - if let Some(old_span) = self.keys.get(&key.as_atom()) { - p.error(diagnostics::redeclaration(key.as_atom().as_str(), *old_span, key.span())); - } else { - self.keys.insert(key.as_atom(), key.span()); - } - - p.expect(Kind::Colon)?; - let value = p.parse_literal_string()?; - let element = ImportAttribute { span: p.end_span(span), key, value }; - self.elements.push(element); - Ok(()) - } -} - -pub struct ExportNamedSpecifiers<'a> { - pub elements: Vec<'a, ExportSpecifier<'a>>, -} - -impl<'a> SeparatedList<'a> for ExportNamedSpecifiers<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let specifier_span = p.start_span(); - let peek_kind = p.peek_kind(); - - // export { type} // name: `type` - // export { type type } // name: `type` type-export: `true` - // export { type as } // name: `as` type-export: `true` - // export { type as as } // name: `type` type-export: `false` (aliased to `as`) - // export { type as as as } // name: `as` type-export: `true`, aliased to `as` - let mut export_kind = ImportOrExportKind::Value; - if p.ts_enabled() && p.at(Kind::Type) { - if p.peek_at(Kind::As) { - if p.nth_at(2, Kind::As) { - if p.nth_at(3, Kind::Str) || p.nth_kind(3).is_identifier_name() { - export_kind = ImportOrExportKind::Type; - } - } else if !(p.nth_at(2, Kind::Str) || p.nth_kind(2).is_identifier_name()) { - export_kind = ImportOrExportKind::Type; - } - } else if (matches!(peek_kind, Kind::Str) || peek_kind.is_identifier_name()) { - export_kind = ImportOrExportKind::Type; - } - } - - if export_kind == ImportOrExportKind::Type { - p.bump_any(); - } - - let local = p.parse_module_export_name()?; - let exported = if p.eat(Kind::As) { p.parse_module_export_name()? } else { local.clone() }; - let element = - ExportSpecifier { span: p.end_span(specifier_span), local, exported, export_kind }; - self.elements.push(element); - Ok(()) - } -} - -pub struct ImportSpecifierList<'a> { - pub import_specifiers: Vec<'a, ImportDeclarationSpecifier<'a>>, -} - -impl<'a> SeparatedList<'a> for ImportSpecifierList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { import_specifiers: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let import_specifier = p.parse_import_specifier()?; - let specifier = ImportDeclarationSpecifier::ImportSpecifier(import_specifier); - self.import_specifiers.push(specifier); - Ok(()) - } -} diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index bb94be48a..fbd223779 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -1,15 +1,11 @@ use oxc_allocator::{Box, Vec}; use oxc_ast::ast::*; use oxc_diagnostics::Result; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; +use rustc_hash::FxHashMap; -use super::{ - list::{AssertEntries, ExportNamedSpecifiers, ImportSpecifierList}, - FunctionKind, -}; -use crate::{ - diagnostics, lexer::Kind, list::SeparatedList, modifiers::Modifiers, Context, ParserImpl, -}; +use super::FunctionKind; +use crate::{diagnostics, lexer::Kind, modifiers::Modifiers, Context, ParserImpl}; impl<'a> ParserImpl<'a> { /// [Import Call](https://tc39.es/ecma262/#sec-import-calls) @@ -127,8 +123,17 @@ impl<'a> ParserImpl<'a> { // import { export1 , export2 as alias2 , [...] } from "module-name"; fn parse_import_specifiers(&mut self) -> Result>> { - self.context(Context::empty(), self.ctx, ImportSpecifierList::parse) - .map(|x| x.import_specifiers) + self.expect(Kind::LCurly)?; + let list = self.context(Context::empty(), self.ctx, |p| { + p.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_import_specifier, + ) + })?; + self.expect(Kind::RCurly)?; + Ok(list) } /// [Import Attributes](https://tc39.es/proposal-import-attributes) @@ -141,10 +146,40 @@ impl<'a> ParserImpl<'a> { } }; let span = self.start_span(); - let with_entries = self.context(Context::empty(), self.ctx, AssertEntries::parse)?.elements; + self.expect(Kind::LCurly)?; + let with_entries = self.context(Context::empty(), self.ctx, |p| { + p.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /*trailing_separator*/ true, + Self::parse_import_attribute, + ) + })?; + self.expect(Kind::RCurly)?; + + let mut keys = FxHashMap::default(); + for e in &with_entries { + let key = e.key.as_atom().as_str(); + let span = e.key.span(); + if let Some(old_span) = keys.insert(key, span) { + self.error(diagnostics::redeclaration(key, old_span, span)); + } + } + Ok(Some(WithClause { span: self.end_span(span), attributes_keyword, with_entries })) } + fn parse_import_attribute(&mut self) -> Result> { + let span = self.start_span(); + let key = match self.cur_kind() { + Kind::Str => ImportAttributeKey::StringLiteral(self.parse_literal_string()?), + _ => ImportAttributeKey::Identifier(self.parse_identifier_name()?), + }; + self.expect(Kind::Colon)?; + let value = self.parse_literal_string()?; + Ok(ImportAttribute { span: self.end_span(span), key, value }) + } + pub(crate) fn parse_ts_export_assignment_declaration( &mut self, start_span: Span, @@ -220,8 +255,16 @@ impl<'a> ParserImpl<'a> { span: Span, ) -> Result>> { let export_kind = self.parse_import_or_export_kind(); - let mut specifiers = - self.context(Context::empty(), self.ctx, ExportNamedSpecifiers::parse)?.elements; + self.expect(Kind::LCurly)?; + let mut specifiers = self.context(Context::empty(), self.ctx, |p| { + p.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_export_named_specifier, + ) + })?; + self.expect(Kind::RCurly)?; let (source, with_clause) = if self.eat(Kind::From) && self.cur_kind().is_literal() { let source = self.parse_literal_string()?; (Some(source), self.parse_import_attributes()?) @@ -380,7 +423,7 @@ impl<'a> ParserImpl<'a> { // ImportSpecifier : // ImportedBinding // ModuleExportName as ImportedBinding - pub(crate) fn parse_import_specifier(&mut self) -> Result>> { + pub(crate) fn parse_import_specifier(&mut self) -> Result> { let specifier_span = self.start_span(); let peek_kind = self.peek_kind(); let mut import_kind = ImportOrExportKind::Value; @@ -411,12 +454,12 @@ impl<'a> ParserImpl<'a> { let imported = IdentifierName { span: local.span, name: local.name.clone() }; (ModuleExportName::IdentifierName(imported), local) }; - Ok(self.ast.alloc(ImportSpecifier { + Ok(ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(ImportSpecifier { span: self.end_span(specifier_span), imported, local, import_kind, - })) + }))) } // ModuleExportName : @@ -472,4 +515,37 @@ impl<'a> ParserImpl<'a> { ImportOrExportKind::Value } + + fn parse_export_named_specifier(&mut self) -> Result> { + let specifier_span = self.start_span(); + let peek_kind = self.peek_kind(); + // export { type} // name: `type` + // export { type type } // name: `type` type-export: `true` + // export { type as } // name: `as` type-export: `true` + // export { type as as } // name: `type` type-export: `false` (aliased to `as`) + // export { type as as as } // name: `as` type-export: `true`, aliased to `as` + let mut export_kind = ImportOrExportKind::Value; + if self.ts_enabled() && self.at(Kind::Type) { + if self.peek_at(Kind::As) { + if self.nth_at(2, Kind::As) { + if self.nth_at(3, Kind::Str) || self.nth_kind(3).is_identifier_name() { + export_kind = ImportOrExportKind::Type; + } + } else if !(self.nth_at(2, Kind::Str) || self.nth_kind(2).is_identifier_name()) { + export_kind = ImportOrExportKind::Type; + } + } else if (matches!(peek_kind, Kind::Str) || peek_kind.is_identifier_name()) { + export_kind = ImportOrExportKind::Type; + } + } + + if export_kind == ImportOrExportKind::Type { + self.bump_any(); + } + + let local = self.parse_module_export_name()?; + let exported = + if self.eat(Kind::As) { self.parse_module_export_name()? } else { local.clone() }; + Ok(ExportSpecifier { span: self.end_span(specifier_span), local, exported, export_kind }) + } } diff --git a/crates/oxc_parser/src/js/object.rs b/crates/oxc_parser/src/js/object.rs index a53b2ff49..4c510861b 100644 --- a/crates/oxc_parser/src/js/object.rs +++ b/crates/oxc_parser/src/js/object.rs @@ -4,10 +4,7 @@ use oxc_diagnostics::Result; use oxc_span::Span; use oxc_syntax::operator::AssignmentOperator; -use super::list::ObjectExpressionProperties; -use crate::{ - diagnostics, lexer::Kind, list::SeparatedList, modifiers::Modifier, Context, ParserImpl, -}; +use crate::{diagnostics, lexer::Kind, modifiers::Modifier, Context, ParserImpl}; impl<'a> ParserImpl<'a> { /// [Object Expression](https://tc39.es/ecma262/#sec-object-initializer) @@ -17,15 +14,35 @@ impl<'a> ParserImpl<'a> { /// { `PropertyDefinitionList`[?Yield, ?Await] , } pub(crate) fn parse_object_expression(&mut self) -> Result> { let span = self.start_span(); - let object_expression_properties = - self.context(Context::In, Context::empty(), ObjectExpressionProperties::parse)?; + self.expect(Kind::LCurly)?; + let object_expression_properties = self.context(Context::In, Context::empty(), |p| { + p.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /* trailing_separator */ false, + Self::parse_object_expression_property, + ) + })?; + let trailing_comma = self.at(Kind::Comma).then(|| { + let span = self.start_span(); + self.bump_any(); + self.end_span(span) + }); + self.expect(Kind::RCurly)?; Ok(self.ast.object_expression( self.end_span(span), - object_expression_properties.elements, - object_expression_properties.trailing_comma, + object_expression_properties, + trailing_comma, )) } + fn parse_object_expression_property(&mut self) -> Result> { + match self.cur_kind() { + Kind::Dot3 => self.parse_spread_element().map(ObjectPropertyKind::SpreadProperty), + _ => self.parse_property_definition().map(ObjectPropertyKind::ObjectProperty), + } + } + /// `PropertyDefinition`[Yield, Await] pub(crate) fn parse_property_definition(&mut self) -> Result>> { let peek_kind = self.peek_kind(); diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs deleted file mode 100644 index 99db19a3e..000000000 --- a/crates/oxc_parser/src/ts/list.rs +++ /dev/null @@ -1,172 +0,0 @@ -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_diagnostics::Result; - -use crate::{lexer::Kind, list::SeparatedList, ParserImpl}; - -pub struct TSEnumMemberList<'a> { - pub members: Vec<'a, TSEnumMember<'a>>, -} - -impl<'a> SeparatedList<'a> for TSEnumMemberList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { members: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let element = p.parse_ts_enum_member()?; - self.members.push(element); - Ok(()) - } -} - -pub struct TSTupleElementList<'a> { - pub elements: Vec<'a, TSTupleElement<'a>>, -} - -impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LBrack - } - - fn close(&self) -> Kind { - Kind::RBrack - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let ty = p.parse_tuple_element_name_or_tuple_element_type()?; - self.elements.push(ty); - Ok(()) - } -} - -pub struct TSTypeParameterList<'a> { - pub params: Vec<'a, TSTypeParameter<'a>>, -} - -impl<'a> SeparatedList<'a> for TSTypeParameterList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { params: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LAngle - } - - fn close(&self) -> Kind { - Kind::RAngle - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let param = p.parse_ts_type_parameter()?; - self.params.push(param); - Ok(()) - } -} - -pub struct TSTypeArgumentList<'a> { - pub params: Vec<'a, TSType<'a>>, - in_expression: bool, -} - -impl<'a> TSTypeArgumentList<'a> { - pub fn parse(p: &mut ParserImpl<'a>, in_expression: bool) -> Result { - let mut list = Self::new(p); - list.in_expression = in_expression; - list.parse_list(p)?; - Ok(list) - } -} - -impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { params: p.ast.new_vec(), in_expression: false } - } - - fn open(&self) -> Kind { - Kind::LAngle - } - - fn close(&self) -> Kind { - Kind::RAngle - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let ty = p.parse_ts_type()?; - self.params.push(ty); - Ok(()) - } - - fn parse_list(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - p.expect(self.open())?; - - let mut first = true; - - while !p.at(self.close()) && !p.at(Kind::Eof) { - if first { - first = false; - } else { - p.expect(self.separator())?; - if p.at(self.close()) { - break; - } - } - - self.parse_element(p)?; - } - - if self.in_expression { - // `a < b> = c`` is valid but `a < b >= c` is BinaryExpression - if matches!(p.re_lex_right_angle(), Kind::GtEq) { - return Err(p.unexpected()); - } - p.re_lex_ts_r_angle(); - } - p.expect(self.close())?; - Ok(()) - } -} - -pub struct TSImportAttributeList<'a> { - pub elements: Vec<'a, TSImportAttribute<'a>>, -} - -impl<'a> SeparatedList<'a> for TSImportAttributeList<'a> { - fn new(p: &ParserImpl<'a>) -> Self { - Self { elements: p.ast.new_vec() } - } - - fn open(&self) -> Kind { - Kind::LCurly - } - - fn close(&self) -> Kind { - Kind::RCurly - } - - fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let span = p.start_span(); - let name = match p.cur_kind() { - Kind::Str => TSImportAttributeName::StringLiteral(p.parse_literal_string()?), - _ => TSImportAttributeName::Identifier(p.parse_identifier_name()?), - }; - - p.expect(Kind::Colon)?; - let value = p.parse_expr()?; - let element = TSImportAttribute { span: p.end_span(span), name, value }; - self.elements.push(element); - Ok(()) - } -} diff --git a/crates/oxc_parser/src/ts/mod.rs b/crates/oxc_parser/src/ts/mod.rs index a3308d7bb..93468556d 100644 --- a/crates/oxc_parser/src/ts/mod.rs +++ b/crates/oxc_parser/src/ts/mod.rs @@ -1,3 +1,2 @@ -mod list; mod statement; mod types; diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index c744879a6..d9673bd7a 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -3,12 +3,10 @@ use oxc_ast::ast::*; use oxc_diagnostics::Result; use oxc_span::Span; -use super::list::TSEnumMemberList; use crate::{ diagnostics, js::{FunctionKind, VariableDeclarationContext, VariableDeclarationParent}, lexer::Kind, - list::SeparatedList, modifiers::{ModifierFlags, ModifierKind, Modifiers}, ParserImpl, }; @@ -27,17 +25,21 @@ impl<'a> ParserImpl<'a> { modifiers: &Modifiers<'a>, ) -> Result> { self.bump_any(); // bump `enum` - let id = self.parse_binding_identifier()?; - let members = TSEnumMemberList::parse(self)?.members; + self.expect(Kind::LCurly)?; + let members = self.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_enum_member, + )?; + self.expect(Kind::RCurly)?; let span = self.end_span(span); - self.verify_modifiers( modifiers, ModifierFlags::DECLARE | ModifierFlags::CONST, diagnostics::modifier_cannot_be_used_here, ); - Ok(self.ast.ts_enum_declaration( span, id, diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index f91374682..b48d203e1 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -4,13 +4,10 @@ use oxc_diagnostics::Result; use oxc_span::GetSpan; use oxc_syntax::operator::UnaryOperator; -use super::list::{TSTupleElementList, TSTypeArgumentList, TSTypeParameterList}; use crate::{ diagnostics, lexer::Kind, - list::SeparatedList, modifiers::{Modifier, ModifierFlags, ModifierKind, Modifiers}, - ts::list::TSImportAttributeList, Context, ParserImpl, }; @@ -142,7 +139,14 @@ impl<'a> ParserImpl<'a> { return Ok(None); } let span = self.start_span(); - let params = TSTypeParameterList::parse(self)?.params; + self.expect(Kind::LAngle)?; + let params = self.parse_delimited_list( + Kind::RAngle, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_type_parameter, + )?; + self.expect(Kind::RAngle)?; Ok(Some(self.ast.ts_type_parameters(self.end_span(span), params))) } @@ -756,7 +760,14 @@ impl<'a> ParserImpl<'a> { ) -> Result>>> { if self.at(Kind::LAngle) { let span = self.start_span(); - let params = TSTypeArgumentList::parse(self, false)?.params; + self.expect(Kind::LAngle)?; + let params = self.parse_delimited_list( + Kind::RAngle, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_type, + )?; + self.expect(Kind::RAngle)?; return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))); } Ok(None) @@ -768,7 +779,14 @@ impl<'a> ParserImpl<'a> { self.re_lex_l_angle(); if !self.cur_token().is_on_new_line && self.re_lex_l_angle() == Kind::LAngle { let span = self.start_span(); - let params = TSTypeArgumentList::parse(self, false)?.params; + self.expect(Kind::LAngle)?; + let params = self.parse_delimited_list( + Kind::RAngle, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_type, + )?; + self.expect(Kind::RAngle)?; return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))); } Ok(None) @@ -784,7 +802,19 @@ impl<'a> ParserImpl<'a> { if self.re_lex_l_angle() != Kind::LAngle { return Ok(None); } - let params = TSTypeArgumentList::parse(self, /* in_expression */ true)?.params; + self.expect(Kind::LAngle)?; + let params = self.parse_delimited_list( + Kind::RAngle, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_type, + )?; + // `a < b> = c`` is valid but `a < b >= c` is BinaryExpression + if matches!(self.re_lex_right_angle(), Kind::GtEq) { + return Err(self.unexpected()); + } + self.re_lex_ts_r_angle(); + self.expect(Kind::RAngle)?; if self.can_follow_type_arguments_in_expr() { return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))); } @@ -805,7 +835,14 @@ impl<'a> ParserImpl<'a> { fn parse_tuple_type(&mut self) -> Result> { let span = self.start_span(); - let elements = TSTupleElementList::parse(self)?.elements; + self.expect(Kind::LBrack)?; + let elements = self.parse_delimited_list( + Kind::RBrack, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_tuple_element_name_or_tuple_element_type, + )?; + self.expect(Kind::RBrack)?; Ok(self.ast.ts_tuple_type(self.end_span(span), elements)) } @@ -955,11 +992,30 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::LCurly)?; self.expect(Kind::With)?; self.expect(Kind::Colon)?; - let elements = TSImportAttributeList::parse(self)?.elements; + self.expect(Kind::LCurly)?; + let elements = self.parse_delimited_list( + Kind::RCurly, + Kind::Comma, + /* trailing_separator */ true, + Self::parse_ts_import_attribute, + )?; + self.expect(Kind::RCurly)?; self.expect(Kind::RCurly)?; Ok(TSImportAttributes { span, elements }) } + fn parse_ts_import_attribute(&mut self) -> Result> { + let span = self.start_span(); + let name = match self.cur_kind() { + Kind::Str => TSImportAttributeName::StringLiteral(self.parse_literal_string()?), + _ => TSImportAttributeName::Identifier(self.parse_identifier_name()?), + }; + + self.expect(Kind::Colon)?; + let value = self.parse_expr()?; + Ok(TSImportAttribute { span: self.end_span(span), name, value }) + } + fn try_parse_constraint_of_infer_type(&mut self) -> Result>> { if self.eat(Kind::Extends) { let constraint = self.context( diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index bab5b0daf..32a146dee 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -5209,10 +5209,11 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ─ ╰──── - × Unexpected token - ╭─[es2017/trailing-function-commas/7/input.js:1:8] + × Expected `)` but found `,` + ╭─[es2017/trailing-function-commas/7/input.js:1:7] 1 │ ('foo',) - · ─ + · ┬ + · ╰── `)` expected ╰──── × Expected `(` but found `await` diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index f60c6e031..6d6c34c1f 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -15592,11 +15592,12 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 8 │ this += value; ╰──── - × Unexpected token - ╭─[conformance/expressions/commaOperator/commaOperatorWithoutOperand.ts:9:7] + × Expected `)` but found `,` + ╭─[conformance/expressions/commaOperator/commaOperatorWithoutOperand.ts:9:5] 8 │ // Missing the second operand 9 │ (ANY, ); - · ─ + · ┬ + · ╰── `)` expected 10 │ (BOOLEAN, ); ╰──── @@ -16114,11 +16115,12 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" · ─ ╰──── - × Unexpected token - ╭─[conformance/externalModules/topLevelAwaitErrors.1.ts:4:10] + × Expected `)` but found `,` + ╭─[conformance/externalModules/topLevelAwaitErrors.1.ts:4:9] 3 │ // reparse call as invalid await should error 4 │ await (1,); - · ─ + · ┬ + · ╰── `)` expected 5 │ await (1); ╰────