Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
787
third-party/vendor/swc_ecma_parser/src/error.rs
vendored
Normal file
787
third-party/vendor/swc_ecma_parser/src/error.rs
vendored
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{
|
||||
errors::{DiagnosticBuilder, Handler},
|
||||
Span, Spanned,
|
||||
};
|
||||
|
||||
use crate::token::Token;
|
||||
|
||||
/// Note: this struct is 8 bytes.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Error {
|
||||
error: Box<(Span, SyntaxError)>,
|
||||
}
|
||||
|
||||
impl Spanned for Error {
|
||||
fn span(&self) -> Span {
|
||||
(*self.error).0
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[cold]
|
||||
pub(crate) fn new(span: Span, error: SyntaxError) -> Self {
|
||||
Self {
|
||||
error: Box::new((span, error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &SyntaxError {
|
||||
&self.error.1
|
||||
}
|
||||
|
||||
pub fn into_kind(self) -> SyntaxError {
|
||||
self.error.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum SyntaxError {
|
||||
Eof,
|
||||
DeclNotAllowed,
|
||||
|
||||
UsingDeclNotAllowed,
|
||||
UsingDeclNotAllowedForForInLoop,
|
||||
UsingDeclNotEnabled,
|
||||
InvalidNameInUsingDecl,
|
||||
InitRequiredForUsingDecl,
|
||||
|
||||
PrivateNameInInterface,
|
||||
|
||||
InvalidSuperCall,
|
||||
InvalidSuper,
|
||||
InvalidSuperPrivateName,
|
||||
|
||||
InvalidNewTarget,
|
||||
|
||||
InvalidImport,
|
||||
|
||||
ArrowNotAllowed,
|
||||
ExportNotAllowed,
|
||||
GetterSetterCannotBeReadonly,
|
||||
GetterParam,
|
||||
SetterParam,
|
||||
|
||||
TopLevelAwaitInScript,
|
||||
|
||||
LegacyDecimal,
|
||||
LegacyOctal,
|
||||
InvalidIdentChar,
|
||||
ExpectedDigit {
|
||||
radix: u8,
|
||||
},
|
||||
SetterParamRequired,
|
||||
RestPatInSetter,
|
||||
|
||||
UnterminatedBlockComment,
|
||||
UnterminatedStrLit,
|
||||
ExpectedUnicodeEscape,
|
||||
EscapeInReservedWord {
|
||||
word: JsWord,
|
||||
},
|
||||
UnterminatedRegExp,
|
||||
UnterminatedTpl,
|
||||
IdentAfterNum,
|
||||
UnexpectedChar {
|
||||
c: char,
|
||||
},
|
||||
InvalidStrEscape,
|
||||
InvalidUnicodeEscape,
|
||||
BadCharacterEscapeSequence {
|
||||
expected: &'static str,
|
||||
},
|
||||
NumLitTerminatedWithExp,
|
||||
LegacyCommentInModule,
|
||||
|
||||
/// "implements", "interface", "let", "package", "private", "protected",
|
||||
/// "public", "static", or "yield"
|
||||
InvalidIdentInStrict(JsWord),
|
||||
|
||||
InvalidIdentInAsync,
|
||||
/// 'eval' and 'arguments' are invalid identifier in strict mode.
|
||||
EvalAndArgumentsInStrict,
|
||||
ArgumentsInClassField,
|
||||
IllegalLanguageModeDirective,
|
||||
UnaryInExp {
|
||||
left: String,
|
||||
left_span: Span,
|
||||
},
|
||||
Hash,
|
||||
LineBreakInThrow,
|
||||
LineBreakBeforeArrow,
|
||||
|
||||
/// Unexpected token
|
||||
Unexpected {
|
||||
got: String,
|
||||
expected: &'static str,
|
||||
},
|
||||
UnexpectedTokenWithSuggestions {
|
||||
candidate_list: Vec<&'static str>,
|
||||
},
|
||||
ReservedWordInImport,
|
||||
AssignProperty,
|
||||
Expected(&'static Token, String),
|
||||
ExpectedSemiForExprStmt {
|
||||
expr: Span,
|
||||
},
|
||||
|
||||
AwaitStar,
|
||||
ReservedWordInObjShorthandOrPat,
|
||||
|
||||
NullishCoalescingWithLogicalOp,
|
||||
|
||||
MultipleDefault {
|
||||
/// Span of the previous default case
|
||||
previous: Span,
|
||||
},
|
||||
CommaAfterRestElement,
|
||||
NonLastRestParam,
|
||||
SpreadInParenExpr,
|
||||
/// `()`
|
||||
EmptyParenExpr,
|
||||
InvalidPat,
|
||||
InvalidExpr,
|
||||
NotSimpleAssign,
|
||||
ExpectedIdent,
|
||||
ExpectedSemi,
|
||||
DuplicateLabel(JsWord),
|
||||
AsyncGenerator,
|
||||
NonTopLevelImportExport,
|
||||
ImportExportInScript,
|
||||
ImportMetaInScript,
|
||||
PatVarWithoutInit,
|
||||
WithInStrict,
|
||||
ReturnNotAllowed,
|
||||
TooManyVarInForInHead,
|
||||
VarInitializerInForInHead,
|
||||
LabelledGeneratorOrAsync,
|
||||
LabelledFunctionInStrict,
|
||||
YieldParamInGen,
|
||||
AwaitParamInAsync,
|
||||
|
||||
AwaitForStmt,
|
||||
|
||||
AwaitInFunction,
|
||||
|
||||
UnterminatedJSXContents,
|
||||
EmptyJSXAttr,
|
||||
InvalidJSXValue,
|
||||
JSXExpectedClosingTagForLtGt,
|
||||
JSXExpectedClosingTag {
|
||||
tag: JsWord,
|
||||
},
|
||||
InvalidLeadingDecorator,
|
||||
DecoratorOnExport,
|
||||
|
||||
TsRequiredAfterOptional,
|
||||
TsInvalidParamPropPat,
|
||||
|
||||
SpaceBetweenHashAndIdent,
|
||||
|
||||
AsyncConstructor,
|
||||
PropertyNamedConstructor,
|
||||
PrivateConstructor,
|
||||
PrivateNameModifier(JsWord),
|
||||
ConstructorAccessor,
|
||||
ReadOnlyMethod,
|
||||
GeneratorConstructor,
|
||||
DuplicateConstructor,
|
||||
TsBindingPatCannotBeOptional,
|
||||
SuperCallOptional,
|
||||
OptChainCannotFollowConstructorCall,
|
||||
TaggedTplInOptChain,
|
||||
|
||||
TrailingCommaInsideImport,
|
||||
|
||||
ExportDefaultWithOutFrom,
|
||||
|
||||
DotsWithoutIdentifier,
|
||||
|
||||
NumericSeparatorIsAllowedOnlyBetweenTwoDigits,
|
||||
|
||||
ImportBindingIsString(JsWord),
|
||||
ExportBindingIsString,
|
||||
|
||||
ConstDeclarationsRequireInitialization,
|
||||
|
||||
DuplicatedRegExpFlags(char),
|
||||
UnknownRegExpFlags,
|
||||
|
||||
TS1003,
|
||||
TS1005,
|
||||
TS1009,
|
||||
TS1014,
|
||||
TS1015,
|
||||
TS1029(JsWord, JsWord),
|
||||
TS1030(JsWord),
|
||||
TS1031,
|
||||
TS1038,
|
||||
TS1042,
|
||||
TS1047,
|
||||
TS1048,
|
||||
TS1056,
|
||||
TS1085,
|
||||
TS1089(JsWord),
|
||||
TS1092,
|
||||
TS1096,
|
||||
TS1098,
|
||||
TS1100,
|
||||
TS1102,
|
||||
TS1105,
|
||||
TS1106,
|
||||
TS1107,
|
||||
TS1109,
|
||||
TS1110,
|
||||
TS1114,
|
||||
TS1115,
|
||||
TS1116,
|
||||
TS1123,
|
||||
TS1141,
|
||||
TS1162,
|
||||
TS1164,
|
||||
TS1171,
|
||||
TS1172,
|
||||
TS1173,
|
||||
TS1174,
|
||||
TS1175,
|
||||
TS1183,
|
||||
TS1184,
|
||||
TS1185,
|
||||
TS1093,
|
||||
TS1196,
|
||||
TS1242,
|
||||
TS1243(JsWord, JsWord),
|
||||
TS1244,
|
||||
TS1245,
|
||||
TS1267,
|
||||
TS1273(JsWord),
|
||||
TS1274(JsWord),
|
||||
TS1277(JsWord),
|
||||
TS2206,
|
||||
TS2207,
|
||||
TS2369,
|
||||
TS2371,
|
||||
TS2406,
|
||||
TS2410,
|
||||
TS2414,
|
||||
TS2427,
|
||||
TS2452,
|
||||
TS2483,
|
||||
TS2491,
|
||||
TS2499,
|
||||
TS2703,
|
||||
TS4112,
|
||||
TS8038,
|
||||
TSTypeAnnotationAfterAssign,
|
||||
TsNonNullAssertionNotAllowed(JsWord),
|
||||
|
||||
WithLabel {
|
||||
inner: Box<Error>,
|
||||
span: Span,
|
||||
note: &'static str,
|
||||
},
|
||||
|
||||
ReservedTypeAssertion,
|
||||
ReservedArrowTypeParam,
|
||||
}
|
||||
|
||||
impl SyntaxError {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn msg(&self) -> Cow<'static, str> {
|
||||
match self {
|
||||
SyntaxError::PrivateNameInInterface => {
|
||||
"private names are not allowed in interface".into()
|
||||
}
|
||||
SyntaxError::TopLevelAwaitInScript => {
|
||||
"top level await is only allowed in module".into()
|
||||
}
|
||||
SyntaxError::LegacyDecimal => {
|
||||
"Legacy decimal escape is not permitted in strict mode".into()
|
||||
}
|
||||
SyntaxError::LegacyOctal => {
|
||||
"Legacy octal escape is not permitted in strict mode".into()
|
||||
}
|
||||
SyntaxError::InvalidIdentChar => "Invalid character in identifier".into(),
|
||||
SyntaxError::ExpectedDigit { radix } => format!(
|
||||
"Expected {} digit",
|
||||
match radix {
|
||||
2 => "a binary",
|
||||
8 => "an octal",
|
||||
10 => "a decimal",
|
||||
16 => "a hexadecimal",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::UnterminatedBlockComment => "Unterminated block comment".into(),
|
||||
SyntaxError::UnterminatedStrLit => "Unterminated string constant".into(),
|
||||
SyntaxError::ExpectedUnicodeEscape => "Expected unicode escape".into(),
|
||||
SyntaxError::EscapeInReservedWord { ref word } => {
|
||||
format!("Unexpected escape sequence in reserved word: {}", word).into()
|
||||
}
|
||||
SyntaxError::UnterminatedRegExp => "Unterminated regexp literal".into(),
|
||||
SyntaxError::UnterminatedTpl => "Unterminated template".into(),
|
||||
SyntaxError::IdentAfterNum => "Identifier cannot follow number".into(),
|
||||
SyntaxError::UnexpectedChar { c } => format!("Unexpected character {:?}", c).into(),
|
||||
SyntaxError::InvalidStrEscape => "Invalid string escape".into(),
|
||||
SyntaxError::InvalidUnicodeEscape => "Invalid unicode escape".into(),
|
||||
SyntaxError::BadCharacterEscapeSequence { expected } => {
|
||||
format!("Bad character escape sequence, expected {}", expected).into()
|
||||
}
|
||||
SyntaxError::LegacyCommentInModule => {
|
||||
"Legacy comments cannot be used in module code".into()
|
||||
}
|
||||
SyntaxError::NumLitTerminatedWithExp => "Expected +, - or decimal digit after e".into(),
|
||||
|
||||
SyntaxError::InvalidIdentInStrict(identifier_name) => format!(
|
||||
"`{}` cannot be used as an identifier in strict mode",
|
||||
identifier_name
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::InvalidIdentInAsync => {
|
||||
"`await` cannot be used as an identifier in an async context".into()
|
||||
}
|
||||
SyntaxError::EvalAndArgumentsInStrict => "'eval' and 'arguments' cannot be used as a \
|
||||
binding identifier in strict mode"
|
||||
.into(),
|
||||
SyntaxError::ArgumentsInClassField => {
|
||||
"'arguments' is only allowed in functions and class methods".into()
|
||||
}
|
||||
SyntaxError::IllegalLanguageModeDirective => {
|
||||
"Illegal 'use strict' directive in function with non-simple parameter list.".into()
|
||||
}
|
||||
SyntaxError::UnaryInExp { .. } => {
|
||||
"'**' cannot be applied to unary/await expression.".into()
|
||||
}
|
||||
SyntaxError::Hash => "Unexpected token '#'".into(),
|
||||
SyntaxError::LineBreakInThrow => "LineBreak cannot follow 'throw'".into(),
|
||||
SyntaxError::LineBreakBeforeArrow => {
|
||||
"Unexpected line break between arrow head and arrow".into()
|
||||
}
|
||||
SyntaxError::Unexpected {
|
||||
ref got,
|
||||
ref expected,
|
||||
} => format!("Unexpected token `{}`. Expected {}", got, expected).into(),
|
||||
|
||||
SyntaxError::ReservedWordInImport => "cannot import as reserved word".into(),
|
||||
SyntaxError::AssignProperty => "assignment property is invalid syntax".into(),
|
||||
SyntaxError::Expected(token, ref got) => {
|
||||
format!("Expected '{:?}', got '{}'", token, got).into()
|
||||
}
|
||||
SyntaxError::ExpectedSemiForExprStmt { .. } => "Expected ';', '}' or <eof>".into(),
|
||||
|
||||
SyntaxError::AwaitStar => "await* has been removed from the async functions proposal. \
|
||||
Use Promise.all() instead."
|
||||
.into(),
|
||||
|
||||
SyntaxError::ReservedWordInObjShorthandOrPat => {
|
||||
"Cannot use a reserved word as a shorthand property".into()
|
||||
}
|
||||
|
||||
SyntaxError::MultipleDefault { .. } => {
|
||||
"A switch block cannot have multiple defaults".into()
|
||||
}
|
||||
SyntaxError::CommaAfterRestElement => {
|
||||
"Trailing comma isn't permitted after a rest element".into()
|
||||
}
|
||||
SyntaxError::NonLastRestParam => "Rest element must be final element".into(),
|
||||
SyntaxError::SpreadInParenExpr => {
|
||||
"Parenthesized expression cannot contain spread operator".into()
|
||||
}
|
||||
SyntaxError::EmptyParenExpr => "Parenthesized expression cannot be empty".into(),
|
||||
SyntaxError::InvalidPat => "Not a pattern".into(),
|
||||
SyntaxError::InvalidExpr => "Not an expression".into(),
|
||||
// TODO
|
||||
SyntaxError::NotSimpleAssign => "Cannot assign to this".into(),
|
||||
SyntaxError::ExpectedIdent => "Expected ident".into(),
|
||||
SyntaxError::ExpectedSemi => "Expected ';' or line break".into(),
|
||||
SyntaxError::DuplicateLabel(ref label) => {
|
||||
format!("Label {} is already declared", label).into()
|
||||
}
|
||||
SyntaxError::AsyncGenerator => "An async function cannot be generator".into(),
|
||||
SyntaxError::NonTopLevelImportExport => {
|
||||
"'import', and 'export' are not permitted here".into()
|
||||
}
|
||||
SyntaxError::ImportExportInScript => {
|
||||
"'import', and 'export' cannot be used outside of module code".into()
|
||||
}
|
||||
SyntaxError::ImportMetaInScript => {
|
||||
"'import.meta' cannot be used outside of module code.".into()
|
||||
}
|
||||
|
||||
SyntaxError::PatVarWithoutInit => "Destructuring bindings require initializers".into(),
|
||||
SyntaxError::WithInStrict => "With statement are not allowed in strict mode".into(),
|
||||
SyntaxError::ReturnNotAllowed => "Return statement is not allowed here".into(),
|
||||
SyntaxError::TooManyVarInForInHead => "Expected one variable binding".into(),
|
||||
SyntaxError::VarInitializerInForInHead => {
|
||||
"Unexpected initializer in for in/of loop".into()
|
||||
}
|
||||
SyntaxError::LabelledGeneratorOrAsync => {
|
||||
"Generator or async function cannot be labelled".into()
|
||||
}
|
||||
SyntaxError::LabelledFunctionInStrict => {
|
||||
"Function cannot be labelled in strict mode".into()
|
||||
}
|
||||
SyntaxError::YieldParamInGen => {
|
||||
"'yield' cannot be used as a parameter within generator".into()
|
||||
}
|
||||
SyntaxError::AwaitParamInAsync => {
|
||||
"`await` expressions cannot be used in a parameter initializer.".into()
|
||||
}
|
||||
SyntaxError::AwaitForStmt => {
|
||||
"for await syntax is valid only for for-of statement".into()
|
||||
}
|
||||
|
||||
SyntaxError::AwaitInFunction => "await isn't allowed in non-async function".into(),
|
||||
|
||||
SyntaxError::UnterminatedJSXContents => "Unterminated JSX contents".into(),
|
||||
SyntaxError::EmptyJSXAttr => {
|
||||
"JSX attributes must only be assigned a non-empty expression".into()
|
||||
}
|
||||
SyntaxError::InvalidJSXValue => {
|
||||
"JSX value should be either an expression or a quoted JSX text".into()
|
||||
}
|
||||
SyntaxError::JSXExpectedClosingTagForLtGt => {
|
||||
"Expected corresponding JSX closing tag for <>".into()
|
||||
}
|
||||
SyntaxError::JSXExpectedClosingTag { ref tag } => {
|
||||
format!("Expected corresponding JSX closing tag for <{}>", tag).into()
|
||||
}
|
||||
SyntaxError::InvalidLeadingDecorator => {
|
||||
"Leading decorators must be attached to a class declaration".into()
|
||||
}
|
||||
SyntaxError::DecoratorOnExport => "Using the export keyword between a decorator and a \
|
||||
class is not allowed. Please use `export @dec \
|
||||
class` instead."
|
||||
.into(),
|
||||
SyntaxError::TsRequiredAfterOptional => {
|
||||
"A required element cannot follow an optional element.".into()
|
||||
}
|
||||
SyntaxError::SuperCallOptional => "Super call cannot be optional".into(),
|
||||
SyntaxError::OptChainCannotFollowConstructorCall => {
|
||||
"Constructor in/after an optional chaining is not allowed.".into()
|
||||
}
|
||||
SyntaxError::TaggedTplInOptChain => {
|
||||
"Tagged template literal is not allowed in optional chain.".into()
|
||||
}
|
||||
SyntaxError::TsInvalidParamPropPat => {
|
||||
"Typescript parameter property must be an identifier or assignment pattern".into()
|
||||
}
|
||||
SyntaxError::SpaceBetweenHashAndIdent => {
|
||||
"Unexpected space between # and identifier".into()
|
||||
}
|
||||
SyntaxError::AsyncConstructor => "Constructor can't be an async function".into(),
|
||||
SyntaxError::PropertyNamedConstructor => {
|
||||
"Classes may not have a non-static field named 'constructor'".into()
|
||||
}
|
||||
SyntaxError::PrivateConstructor => {
|
||||
"Classes can't have a private field named '#constructor'.".into()
|
||||
}
|
||||
SyntaxError::DuplicateConstructor => "A class can only have one constructor".into(),
|
||||
SyntaxError::PrivateNameModifier(modifier) => format!(
|
||||
"'{}' modifier cannot be used with a private identifier",
|
||||
modifier
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::ConstructorAccessor => "Class constructor can't be an accessor.".into(),
|
||||
|
||||
SyntaxError::ReadOnlyMethod => "A method cannot be readonly".into(),
|
||||
SyntaxError::TsBindingPatCannotBeOptional => "A binding pattern parameter cannot be \
|
||||
optional in an implementation signature."
|
||||
.into(),
|
||||
|
||||
SyntaxError::TrailingCommaInsideImport => {
|
||||
"Trailing comma is disallowed inside import(...) arguments".into()
|
||||
}
|
||||
|
||||
SyntaxError::ExportDefaultWithOutFrom => {
|
||||
"export default statements required from '...';".into()
|
||||
}
|
||||
|
||||
SyntaxError::DotsWithoutIdentifier => {
|
||||
"`...` must be followed by an identifier in declaration contexts".into()
|
||||
}
|
||||
|
||||
SyntaxError::NumericSeparatorIsAllowedOnlyBetweenTwoDigits => {
|
||||
"A numeric separator is only allowed between two digits".into()
|
||||
}
|
||||
|
||||
SyntaxError::NullishCoalescingWithLogicalOp => {
|
||||
"Nullish coalescing operator(??) requires parens when mixing with logical operators"
|
||||
.into()
|
||||
}
|
||||
|
||||
SyntaxError::TS1056 => {
|
||||
"jsc.target should be es5 or upper to use getter / setter".into()
|
||||
}
|
||||
SyntaxError::TS1110 => "type expected".into(),
|
||||
SyntaxError::TS1141 => "literal in an import type should be string literal".into(),
|
||||
|
||||
SyntaxError::Eof => "Unexpected eof".into(),
|
||||
|
||||
SyntaxError::TS2703 => {
|
||||
"The operand of a delete operator must be a property reference.".into()
|
||||
}
|
||||
SyntaxError::DeclNotAllowed => "Declaration is not allowed".into(),
|
||||
SyntaxError::UsingDeclNotAllowed => "Using declaration is not allowed".into(),
|
||||
SyntaxError::UsingDeclNotAllowedForForInLoop => {
|
||||
"Using declaration is not allowed in for-in loop".into()
|
||||
}
|
||||
SyntaxError::UsingDeclNotEnabled => {
|
||||
"Using declaration is not enabled. Set jsc.parser.usingDecl to true".into()
|
||||
}
|
||||
SyntaxError::InvalidNameInUsingDecl => {
|
||||
"Using declaration only allows identifiers".into()
|
||||
}
|
||||
SyntaxError::InitRequiredForUsingDecl => {
|
||||
"Using declaration requires initializer".into()
|
||||
}
|
||||
SyntaxError::InvalidSuperCall => "Invalid `super()`".into(),
|
||||
SyntaxError::InvalidSuper => "Invalid access to super".into(),
|
||||
SyntaxError::InvalidSuperPrivateName => {
|
||||
"Index super with private name is not allowed".into()
|
||||
}
|
||||
SyntaxError::InvalidNewTarget => "'new.target' is only allowed in the body of a \
|
||||
function declaration, function expression, or class."
|
||||
.into(),
|
||||
SyntaxError::InvalidImport => "Import is not allowed here".into(),
|
||||
SyntaxError::ArrowNotAllowed => "An arrow function is not allowed here".into(),
|
||||
SyntaxError::ExportNotAllowed => "`export` is not allowed here".into(),
|
||||
SyntaxError::GetterSetterCannotBeReadonly => {
|
||||
"A getter or a setter cannot be readonly".into()
|
||||
}
|
||||
SyntaxError::GetterParam => "A `get` accessor cannot have parameters".into(),
|
||||
SyntaxError::SetterParam => "A `set` accessor must have exactly one parameter".into(),
|
||||
SyntaxError::RestPatInSetter => "Rest pattern is not allowed in setter".into(),
|
||||
|
||||
SyntaxError::GeneratorConstructor => "A constructor cannot be generator".into(),
|
||||
|
||||
SyntaxError::ImportBindingIsString(s) => format!(
|
||||
"A string literal cannot be used as an imported binding.\n- Did you mean `import \
|
||||
{{ \"{}\" as foo }}`?",
|
||||
s
|
||||
)
|
||||
.into(),
|
||||
|
||||
SyntaxError::ExportBindingIsString => {
|
||||
"A string literal cannot be used as an exported binding without `from`.".into()
|
||||
}
|
||||
|
||||
SyntaxError::ConstDeclarationsRequireInitialization => {
|
||||
"'const' declarations must be initialized".into()
|
||||
}
|
||||
|
||||
SyntaxError::DuplicatedRegExpFlags(flag) => {
|
||||
format!("Duplicated regular expression flag '{}'.", flag).into()
|
||||
}
|
||||
SyntaxError::UnknownRegExpFlags => "Unknown regular expression flags.".into(),
|
||||
|
||||
SyntaxError::TS1003 => "Expected an identifier".into(),
|
||||
SyntaxError::TS1005 => "Expected a semicolon".into(),
|
||||
SyntaxError::TS1009 => "Trailing comma is not allowed".into(),
|
||||
SyntaxError::TS1014 => "A rest parameter must be last in a parameter list".into(),
|
||||
SyntaxError::TS1015 => "Parameter cannot have question mark and initializer".into(),
|
||||
SyntaxError::TS1029(left, right) => {
|
||||
format!("'{}' modifier must precede '{}' modifier.", left, right).into()
|
||||
}
|
||||
SyntaxError::TS1030(word) => format!("'{}' modifier already seen.", word).into(),
|
||||
SyntaxError::TS1031 => {
|
||||
"`declare` modifier cannot appear on class elements of this kind".into()
|
||||
}
|
||||
SyntaxError::TS1038 => {
|
||||
"`declare` modifier not allowed for code already in an ambient context".into()
|
||||
}
|
||||
SyntaxError::TS1042 => "`async` modifier cannot be used here".into(),
|
||||
SyntaxError::TS1047 => "A rest parameter cannot be optional".into(),
|
||||
SyntaxError::TS1048 => "A rest parameter cannot have an initializer".into(),
|
||||
SyntaxError::TS1085 => "Legacy octal literals are not available when targeting \
|
||||
ECMAScript 5 and higher"
|
||||
.into(),
|
||||
SyntaxError::TS1089(word) => format!(
|
||||
"'{}' modifier cannot appear on a constructor declaration",
|
||||
word
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::TS1092 => {
|
||||
"Type parameters cannot appear on a constructor declaration".into()
|
||||
}
|
||||
SyntaxError::TS1096 => "An index signature must have exactly one parameter".into(),
|
||||
SyntaxError::TS1098 => "Type parameter list cannot be empty".into(),
|
||||
SyntaxError::TS1100 => "Invalid use of 'arguments' in strict mode".into(),
|
||||
SyntaxError::TS1102 => {
|
||||
"'delete' cannot be called on an identifier in strict mode".into()
|
||||
}
|
||||
SyntaxError::TS1105 => "A 'break' statement can only be used within an enclosing \
|
||||
iteration or switch statement"
|
||||
.into(),
|
||||
SyntaxError::TS1106 => {
|
||||
"The left-hand side of a `for...of` statement may not be `async`".into()
|
||||
}
|
||||
SyntaxError::TS1107 => "Jump target cannot cross function boundary".into(),
|
||||
SyntaxError::TS1109 => "Expression expected".into(),
|
||||
SyntaxError::TS1114 => "Duplicate label".into(),
|
||||
SyntaxError::TS1115 => "A 'continue' statement can only jump to a label of an \
|
||||
enclosing iteration statement"
|
||||
.into(),
|
||||
SyntaxError::TS1116 => {
|
||||
"A 'break' statement can only jump to a label of an enclosing statement".into()
|
||||
}
|
||||
SyntaxError::TS1123 => "Variable declaration list cannot be empty".into(),
|
||||
SyntaxError::TS1162 => "An object member cannot be declared optional".into(),
|
||||
SyntaxError::TS1164 => "Computed property names are not allowed in enums".into(),
|
||||
SyntaxError::TS1171 => {
|
||||
"A comma expression is not allowed in a computed property name".into()
|
||||
}
|
||||
SyntaxError::TS1172 => "`extends` clause already seen.".into(),
|
||||
SyntaxError::TS1173 => "'extends' clause must precede 'implements' clause.".into(),
|
||||
SyntaxError::TS1174 => "Classes can only extend a single class".into(),
|
||||
SyntaxError::TS1175 => "`implements` clause already seen".into(),
|
||||
SyntaxError::TS1183 => {
|
||||
"An implementation cannot be declared in ambient contexts".into()
|
||||
}
|
||||
SyntaxError::TS1184 => "Modifiers cannot appear here".into(),
|
||||
SyntaxError::TS1185 => "Merge conflict marker encountered.".into(),
|
||||
SyntaxError::TS1093 => {
|
||||
"Type annotation cannot appear on a constructor declaration".into()
|
||||
}
|
||||
SyntaxError::TS1196 => "Catch clause variable cannot have a type annotation".into(),
|
||||
SyntaxError::TS1242 => {
|
||||
"`abstract` modifier can only appear on a class or method declaration".into()
|
||||
}
|
||||
SyntaxError::TS1244 => {
|
||||
"Abstract methods can only appear within an abstract class.".into()
|
||||
}
|
||||
SyntaxError::TS1243(left, right) => format!(
|
||||
"'{}' modifier cannot be used with '{}' modifier.",
|
||||
left, right
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::TS1245 => "Abstract method cannot have an implementation.".into(),
|
||||
SyntaxError::TS1267 => "Abstract property cannot have an initializer.".into(),
|
||||
SyntaxError::TS1273(word) => {
|
||||
format!("'{}' modifier cannot appear on a type parameter", word).into()
|
||||
}
|
||||
SyntaxError::TS1274(word) => format!(
|
||||
"'{}' modifier can only appear on a type parameter of a class, interface or type \
|
||||
alias",
|
||||
word
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::TS1277(word) => format!(
|
||||
"'{}' modifier can only appear on a type parameter of a function, method or class",
|
||||
word
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::TS2206 => "The 'type' modifier cannot be used on a named import when \
|
||||
'import type' is used on its import statement."
|
||||
.into(),
|
||||
SyntaxError::TS2207 => "The 'type' modifier cannot be used on a named export when \
|
||||
'export type' is used on its export statement."
|
||||
.into(),
|
||||
SyntaxError::TS2369 => {
|
||||
"A parameter property is only allowed in a constructor implementation".into()
|
||||
}
|
||||
SyntaxError::TS2371 => "A parameter initializer is only allowed in a function or \
|
||||
constructor implementation"
|
||||
.into(),
|
||||
SyntaxError::TS2406 => "The left-hand side of an assignment expression must be a \
|
||||
variable or a property access."
|
||||
.into(),
|
||||
SyntaxError::TS2410 => "The 'with' statement is not supported. All symbols in a \
|
||||
'with' block will have type 'any'."
|
||||
.into(),
|
||||
SyntaxError::TS2414 => "Invalid class name".into(),
|
||||
SyntaxError::TS2427 => "interface name is invalid".into(),
|
||||
SyntaxError::TS2452 => "An enum member cannot have a numeric name".into(),
|
||||
SyntaxError::TS2483 => {
|
||||
"The left-hand side of a 'for...of' statement cannot use a type annotation".into()
|
||||
}
|
||||
SyntaxError::TS2491 => "The left-hand side of a 'for...in' statement cannot be a \
|
||||
destructuring pattern"
|
||||
.into(),
|
||||
SyntaxError::TS2499 => "An interface can only extend an identifier/qualified-name \
|
||||
with optional type arguments."
|
||||
.into(),
|
||||
SyntaxError::TS4112 => "This member cannot have an 'override' modifier because its \
|
||||
containing class does not extend another class."
|
||||
.into(),
|
||||
SyntaxError::TS8038 => "Decorators may not appear after `export` or `export default` \
|
||||
if they also appear before `export`."
|
||||
.into(),
|
||||
SyntaxError::TSTypeAnnotationAfterAssign => {
|
||||
"Type annotations must come before default assignments".into()
|
||||
}
|
||||
SyntaxError::TsNonNullAssertionNotAllowed(word) => format!(
|
||||
"Typescript non-null assertion operator is not allowed with '{}'",
|
||||
word
|
||||
)
|
||||
.into(),
|
||||
SyntaxError::SetterParamRequired => "Setter should have exactly one parameter".into(),
|
||||
SyntaxError::UnexpectedTokenWithSuggestions {
|
||||
candidate_list: token_list,
|
||||
} => {
|
||||
let did_you_mean = if token_list.len() <= 2 {
|
||||
token_list.join(" or ")
|
||||
} else {
|
||||
token_list[0..token_list.len() - 1].join(" , ")
|
||||
+ &*format!("or {}", token_list[token_list.len() - 1])
|
||||
};
|
||||
format!("Unexpected token. Did you mean {}?", did_you_mean).into()
|
||||
}
|
||||
SyntaxError::WithLabel { inner, .. } => inner.error.1.msg(),
|
||||
SyntaxError::ReservedTypeAssertion => "This syntax is reserved in files with the .mts \
|
||||
or .cts extension. Use an `as` expression \
|
||||
instead."
|
||||
.into(),
|
||||
SyntaxError::ReservedArrowTypeParam => "This syntax is reserved in files with the \
|
||||
.mts or .cts extension. Add a trailing comma, \
|
||||
as in `<T,>() => ...`."
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder {
|
||||
if let SyntaxError::WithLabel { inner, note, span } = self.error.1 {
|
||||
let mut db = inner.into_diagnostic(handler);
|
||||
db.span_label(span, note);
|
||||
return db;
|
||||
}
|
||||
|
||||
let span = self.span();
|
||||
|
||||
let kind = self.into_kind();
|
||||
let msg = kind.msg();
|
||||
|
||||
let mut db = handler.struct_span_err(span, &msg);
|
||||
|
||||
match kind {
|
||||
SyntaxError::ExpectedSemiForExprStmt { expr } => {
|
||||
db.span_label(
|
||||
expr,
|
||||
"This is the expression part of an expression statement",
|
||||
);
|
||||
}
|
||||
SyntaxError::MultipleDefault { previous } => {
|
||||
db.span_label(previous, "previous default case is declared at here");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
db
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_error() {
|
||||
assert_eq!(std::mem::size_of::<Error>(), 8);
|
||||
}
|
||||
87
third-party/vendor/swc_ecma_parser/src/lexer/comments_buffer.rs
vendored
Normal file
87
third-party/vendor/swc_ecma_parser/src/lexer/comments_buffer.rs
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use std::{iter::Rev, rc::Rc, vec::IntoIter};
|
||||
|
||||
use swc_common::{comments::Comment, BytePos};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BufferedComment {
|
||||
pub kind: BufferedCommentKind,
|
||||
pub pos: BytePos,
|
||||
pub comment: Comment,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum BufferedCommentKind {
|
||||
Leading,
|
||||
Trailing,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CommentsBuffer {
|
||||
comments: OneDirectionalList<BufferedComment>,
|
||||
pending_leading: OneDirectionalList<Comment>,
|
||||
}
|
||||
|
||||
impl CommentsBuffer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
comments: OneDirectionalList::new(),
|
||||
pending_leading: OneDirectionalList::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, comment: BufferedComment) {
|
||||
self.comments.push(comment);
|
||||
}
|
||||
|
||||
pub fn push_pending_leading(&mut self, comment: Comment) {
|
||||
self.pending_leading.push(comment);
|
||||
}
|
||||
|
||||
pub fn take_comments(&mut self) -> Rev<IntoIter<BufferedComment>> {
|
||||
self.comments.take_all()
|
||||
}
|
||||
|
||||
pub fn take_pending_leading(&mut self) -> Rev<IntoIter<Comment>> {
|
||||
self.pending_leading.take_all()
|
||||
}
|
||||
}
|
||||
|
||||
/// A one direction linked list that can be cheaply
|
||||
/// cloned with the clone maintaining its position in the list.
|
||||
#[derive(Clone)]
|
||||
struct OneDirectionalList<T: Clone> {
|
||||
last_node: Option<Rc<OneDirectionalListNode<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Clone> OneDirectionalList<T> {
|
||||
pub fn new() -> Self {
|
||||
Self { last_node: None }
|
||||
}
|
||||
|
||||
pub fn take_all(&mut self) -> Rev<IntoIter<T>> {
|
||||
// these are stored in reverse, so we need to reverse them back
|
||||
let mut items = Vec::new();
|
||||
let mut current_node = self.last_node.take();
|
||||
while let Some(node) = current_node {
|
||||
let mut node = match Rc::try_unwrap(node) {
|
||||
Ok(n) => n,
|
||||
Err(n) => n.as_ref().clone(),
|
||||
};
|
||||
items.push(node.item);
|
||||
current_node = node.previous.take();
|
||||
}
|
||||
items.into_iter().rev()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, item: T) {
|
||||
let previous = self.last_node.take();
|
||||
let new_item = OneDirectionalListNode { item, previous };
|
||||
self.last_node = Some(Rc::new(new_item));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct OneDirectionalListNode<T: Clone> {
|
||||
item: T,
|
||||
previous: Option<Rc<OneDirectionalListNode<T>>>,
|
||||
}
|
||||
1
third-party/vendor/swc_ecma_parser/src/lexer/input.rs
vendored
Normal file
1
third-party/vendor/swc_ecma_parser/src/lexer/input.rs
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub use swc_common::input::*;
|
||||
626
third-party/vendor/swc_ecma_parser/src/lexer/jsx.rs
vendored
Normal file
626
third-party/vendor/swc_ecma_parser/src/lexer/jsx.rs
vendored
Normal file
|
|
@ -0,0 +1,626 @@
|
|||
use either::Either;
|
||||
use swc_atoms::Atom;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub(super) fn read_jsx_token(&mut self) -> LexResult<Option<Token>> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
let mut chunk_start = self.input.cur_pos();
|
||||
let mut out = String::new();
|
||||
|
||||
loop {
|
||||
let cur = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
let start = self.state.start;
|
||||
self.error(start, SyntaxError::UnterminatedJSXContents)?
|
||||
}
|
||||
};
|
||||
let cur_pos = self.input.cur_pos();
|
||||
|
||||
match cur {
|
||||
'<' if self.had_line_break_before_last() && self.is_str("<<<<<< ") => {
|
||||
let span = Span::new(cur_pos, cur_pos + BytePos(7), Default::default());
|
||||
|
||||
self.emit_error_span(span, SyntaxError::TS1185);
|
||||
self.skip_line_comment(6);
|
||||
self.skip_space::<true>()?;
|
||||
return self.read_token();
|
||||
}
|
||||
'<' | '{' => {
|
||||
//
|
||||
if cur_pos == self.state.start {
|
||||
if cur == '<' && self.state.is_expr_allowed {
|
||||
unsafe {
|
||||
// Safety: cur() was Some('<')
|
||||
self.input.bump();
|
||||
}
|
||||
return Ok(Token::JSXTagStart).map(Some);
|
||||
}
|
||||
return self.read_token();
|
||||
}
|
||||
out.push_str(unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
});
|
||||
|
||||
return Ok(Token::JSXText {
|
||||
raw: Atom::new(out),
|
||||
})
|
||||
.map(Some);
|
||||
}
|
||||
'>' => {
|
||||
self.emit_error(
|
||||
cur_pos,
|
||||
SyntaxError::UnexpectedTokenWithSuggestions {
|
||||
candidate_list: vec!["`{'>'}`", "`>`"],
|
||||
},
|
||||
);
|
||||
unsafe {
|
||||
// Safety: cur() was Some('>')
|
||||
self.input.bump()
|
||||
}
|
||||
}
|
||||
'}' => {
|
||||
self.emit_error(
|
||||
cur_pos,
|
||||
SyntaxError::UnexpectedTokenWithSuggestions {
|
||||
candidate_list: vec!["`{'}'}`", "`}`"],
|
||||
},
|
||||
);
|
||||
unsafe {
|
||||
// Safety: cur() was Some('}')
|
||||
self.input.bump()
|
||||
}
|
||||
}
|
||||
'&' => {
|
||||
out.push_str(unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
});
|
||||
|
||||
let jsx_entity = self.read_jsx_entity()?;
|
||||
|
||||
out.push(jsx_entity.0);
|
||||
chunk_start = self.input.cur_pos();
|
||||
}
|
||||
|
||||
_ => {
|
||||
if cur.is_line_terminator() {
|
||||
out.push_str(unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
});
|
||||
match self.read_jsx_new_line(true)? {
|
||||
Either::Left(s) => out.push_str(s),
|
||||
Either::Right(c) => out.push(c),
|
||||
}
|
||||
chunk_start = cur_pos;
|
||||
} else {
|
||||
unsafe {
|
||||
// Safety: cur() was Some(c)
|
||||
self.input.bump()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_entity(&mut self) -> LexResult<(char, String)> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
fn from_code(s: &str, radix: u32) -> LexResult<char> {
|
||||
// TODO(kdy1): unwrap -> Err
|
||||
let c = char::from_u32(
|
||||
u32::from_str_radix(s, radix).expect("failed to parse string as number"),
|
||||
)
|
||||
.expect("failed to parse number as char");
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn is_hex(s: &str) -> bool {
|
||||
s.chars().all(|c| c.is_ascii_hexdigit())
|
||||
}
|
||||
|
||||
fn is_dec(s: &str) -> bool {
|
||||
s.chars().all(|c| c.is_ascii_digit())
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
|
||||
let c = self.input.cur();
|
||||
debug_assert_eq!(c, Some('&'));
|
||||
unsafe {
|
||||
// Safety: cur() was Some('&')
|
||||
self.input.bump();
|
||||
}
|
||||
|
||||
let start_pos = self.input.cur_pos();
|
||||
|
||||
for _ in 0..10 {
|
||||
let c = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => break,
|
||||
};
|
||||
unsafe {
|
||||
// Safety: cur() was Some(c)
|
||||
self.input.bump();
|
||||
}
|
||||
|
||||
if c == ';' {
|
||||
if let Some(stripped) = s.strip_prefix('#') {
|
||||
if stripped.starts_with('x') {
|
||||
if is_hex(&s[2..]) {
|
||||
let value = from_code(&s[2..], 16)?;
|
||||
|
||||
return Ok((value, format!("&{};", s)));
|
||||
}
|
||||
} else if is_dec(stripped) {
|
||||
let value = from_code(stripped, 10)?;
|
||||
|
||||
return Ok((value, format!("&{};", s)));
|
||||
}
|
||||
} else if let Some(entity) = xhtml(&s) {
|
||||
return Ok((entity, format!("&{};", s)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
s.push(c)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Safety: start_pos is a valid position because we got it from self.input
|
||||
self.input.reset_to(start_pos);
|
||||
}
|
||||
|
||||
Ok(('&', "&".to_string()))
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_new_line(
|
||||
&mut self,
|
||||
normalize_crlf: bool,
|
||||
) -> LexResult<Either<&'static str, char>> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
let ch = self.input.cur().unwrap();
|
||||
unsafe {
|
||||
// Safety: cur() was Some(ch)
|
||||
self.input.bump();
|
||||
}
|
||||
|
||||
let out = if ch == '\r' && self.input.cur() == Some('\n') {
|
||||
unsafe {
|
||||
// Safety: cur() was Some('\n')
|
||||
self.input.bump();
|
||||
}
|
||||
Either::Left(if normalize_crlf { "\n" } else { "\r\n" })
|
||||
} else {
|
||||
Either::Right(ch)
|
||||
};
|
||||
let cur_pos = self.input.cur_pos();
|
||||
self.state.cur_line += 1;
|
||||
self.state.line_start = cur_pos;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub(super) fn read_jsx_str(&mut self, quote: char) -> LexResult<Token> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
|
||||
let mut raw = String::new();
|
||||
|
||||
raw.push(quote);
|
||||
|
||||
unsafe {
|
||||
// Safety: cur() was Some(quote)
|
||||
self.input.bump(); // `quote`
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
let mut chunk_start = self.input.cur_pos();
|
||||
|
||||
loop {
|
||||
let ch = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
let start = self.state.start;
|
||||
self.emit_error(start, SyntaxError::UnterminatedStrLit);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let cur_pos = self.input.cur_pos();
|
||||
|
||||
if ch == '\\' {
|
||||
let value = unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
};
|
||||
|
||||
out.push_str(value);
|
||||
out.push('\\');
|
||||
raw.push_str(value);
|
||||
raw.push('\\');
|
||||
|
||||
self.bump();
|
||||
|
||||
chunk_start = self.input.cur_pos();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ch == quote {
|
||||
break;
|
||||
}
|
||||
|
||||
if ch == '&' {
|
||||
let value = unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
};
|
||||
|
||||
out.push_str(value);
|
||||
raw.push_str(value);
|
||||
|
||||
let jsx_entity = self.read_jsx_entity()?;
|
||||
|
||||
out.push(jsx_entity.0);
|
||||
raw.push_str(&jsx_entity.1);
|
||||
|
||||
chunk_start = self.input.cur_pos();
|
||||
} else if ch.is_line_terminator() {
|
||||
let value = unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
};
|
||||
|
||||
out.push_str(value);
|
||||
raw.push_str(value);
|
||||
|
||||
match self.read_jsx_new_line(false)? {
|
||||
Either::Left(s) => {
|
||||
out.push_str(s);
|
||||
raw.push_str(s);
|
||||
}
|
||||
Either::Right(c) => {
|
||||
out.push(c);
|
||||
raw.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
chunk_start = cur_pos + BytePos(ch.len_utf8() as _);
|
||||
} else {
|
||||
unsafe {
|
||||
// Safety: cur() was Some(ch)
|
||||
self.input.bump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cur_pos = self.input.cur_pos();
|
||||
let value = unsafe {
|
||||
// Safety: We already checked for the range
|
||||
self.input.slice(chunk_start, cur_pos)
|
||||
};
|
||||
|
||||
out.push_str(value);
|
||||
raw.push_str(value);
|
||||
|
||||
// it might be at the end of the file when
|
||||
// the string literal is unterminated
|
||||
if self.input.peek_ahead().is_some() {
|
||||
unsafe {
|
||||
// Safety: We called peek_ahead() which means cur() was Some
|
||||
self.input.bump();
|
||||
}
|
||||
}
|
||||
|
||||
raw.push(quote);
|
||||
|
||||
Ok(Token::Str {
|
||||
value: out.into(),
|
||||
raw: Atom::new(raw),
|
||||
})
|
||||
}
|
||||
|
||||
/// Read a JSX identifier (valid tag or attribute name).
|
||||
///
|
||||
/// Optimized version since JSX identifiers can"t contain
|
||||
/// escape characters and so can be read as single slice.
|
||||
/// Also assumes that first character was already checked
|
||||
/// by isIdentifierStart in readToken.
|
||||
pub(super) fn read_jsx_word(&mut self) -> LexResult<Token> {
|
||||
debug_assert!(self.syntax.jsx());
|
||||
debug_assert!(self.input.cur().is_some());
|
||||
debug_assert!(self.input.cur().unwrap().is_ident_start());
|
||||
|
||||
let mut first = true;
|
||||
let slice = self.input.uncons_while(|c| {
|
||||
if first {
|
||||
first = false;
|
||||
c.is_ident_start()
|
||||
} else {
|
||||
c.is_ident_part() || c == '-'
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Token::JSXName { name: slice.into() })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! xhtml {
|
||||
(
|
||||
$(
|
||||
$i:ident : $s:expr,
|
||||
)*
|
||||
) => {
|
||||
fn xhtml(s: &str) -> Option<char> {
|
||||
match s{
|
||||
$(stringify!($i) => Some($s),)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
xhtml!(
|
||||
quot: '\u{0022}',
|
||||
amp: '&',
|
||||
apos: '\u{0027}',
|
||||
lt: '<',
|
||||
gt: '>',
|
||||
nbsp: '\u{00A0}',
|
||||
iexcl: '\u{00A1}',
|
||||
cent: '\u{00A2}',
|
||||
pound: '\u{00A3}',
|
||||
curren: '\u{00A4}',
|
||||
yen: '\u{00A5}',
|
||||
brvbar: '\u{00A6}',
|
||||
sect: '\u{00A7}',
|
||||
uml: '\u{00A8}',
|
||||
copy: '\u{00A9}',
|
||||
ordf: '\u{00AA}',
|
||||
laquo: '\u{00AB}',
|
||||
not: '\u{00AC}',
|
||||
shy: '\u{00AD}',
|
||||
reg: '\u{00AE}',
|
||||
macr: '\u{00AF}',
|
||||
deg: '\u{00B0}',
|
||||
plusmn: '\u{00B1}',
|
||||
sup2: '\u{00B2}',
|
||||
sup3: '\u{00B3}',
|
||||
acute: '\u{00B4}',
|
||||
micro: '\u{00B5}',
|
||||
para: '\u{00B6}',
|
||||
middot: '\u{00B7}',
|
||||
cedil: '\u{00B8}',
|
||||
sup1: '\u{00B9}',
|
||||
ordm: '\u{00BA}',
|
||||
raquo: '\u{00BB}',
|
||||
frac14: '\u{00BC}',
|
||||
frac12: '\u{00BD}',
|
||||
frac34: '\u{00BE}',
|
||||
iquest: '\u{00BF}',
|
||||
Agrave: '\u{00C0}',
|
||||
Aacute: '\u{00C1}',
|
||||
Acirc: '\u{00C2}',
|
||||
Atilde: '\u{00C3}',
|
||||
Auml: '\u{00C4}',
|
||||
Aring: '\u{00C5}',
|
||||
AElig: '\u{00C6}',
|
||||
Ccedil: '\u{00C7}',
|
||||
Egrave: '\u{00C8}',
|
||||
Eacute: '\u{00C9}',
|
||||
Ecirc: '\u{00CA}',
|
||||
Euml: '\u{00CB}',
|
||||
Igrave: '\u{00CC}',
|
||||
Iacute: '\u{00CD}',
|
||||
Icirc: '\u{00CE}',
|
||||
Iuml: '\u{00CF}',
|
||||
ETH: '\u{00D0}',
|
||||
Ntilde: '\u{00D1}',
|
||||
Ograve: '\u{00D2}',
|
||||
Oacute: '\u{00D3}',
|
||||
Ocirc: '\u{00D4}',
|
||||
Otilde: '\u{00D5}',
|
||||
Ouml: '\u{00D6}',
|
||||
times: '\u{00D7}',
|
||||
Oslash: '\u{00D8}',
|
||||
Ugrave: '\u{00D9}',
|
||||
Uacute: '\u{00DA}',
|
||||
Ucirc: '\u{00DB}',
|
||||
Uuml: '\u{00DC}',
|
||||
Yacute: '\u{00DD}',
|
||||
THORN: '\u{00DE}',
|
||||
szlig: '\u{00DF}',
|
||||
agrave: '\u{00E0}',
|
||||
aacute: '\u{00E1}',
|
||||
acirc: '\u{00E2}',
|
||||
atilde: '\u{00E3}',
|
||||
auml: '\u{00E4}',
|
||||
aring: '\u{00E5}',
|
||||
aelig: '\u{00E6}',
|
||||
ccedil: '\u{00E7}',
|
||||
egrave: '\u{00E8}',
|
||||
eacute: '\u{00E9}',
|
||||
ecirc: '\u{00EA}',
|
||||
euml: '\u{00EB}',
|
||||
igrave: '\u{00EC}',
|
||||
iacute: '\u{00ED}',
|
||||
icirc: '\u{00EE}',
|
||||
iuml: '\u{00EF}',
|
||||
eth: '\u{00F0}',
|
||||
ntilde: '\u{00F1}',
|
||||
ograve: '\u{00F2}',
|
||||
oacute: '\u{00F3}',
|
||||
ocirc: '\u{00F4}',
|
||||
otilde: '\u{00F5}',
|
||||
ouml: '\u{00F6}',
|
||||
divide: '\u{00F7}',
|
||||
oslash: '\u{00F8}',
|
||||
ugrave: '\u{00F9}',
|
||||
uacute: '\u{00FA}',
|
||||
ucirc: '\u{00FB}',
|
||||
uuml: '\u{00FC}',
|
||||
yacute: '\u{00FD}',
|
||||
thorn: '\u{00FE}',
|
||||
yuml: '\u{00FF}',
|
||||
OElig: '\u{0152}',
|
||||
oelig: '\u{0153}',
|
||||
Scaron: '\u{0160}',
|
||||
scaron: '\u{0161}',
|
||||
Yuml: '\u{0178}',
|
||||
fnof: '\u{0192}',
|
||||
circ: '\u{02C6}',
|
||||
tilde: '\u{02DC}',
|
||||
Alpha: '\u{0391}',
|
||||
Beta: '\u{0392}',
|
||||
Gamma: '\u{0393}',
|
||||
Delta: '\u{0394}',
|
||||
Epsilon: '\u{0395}',
|
||||
Zeta: '\u{0396}',
|
||||
Eta: '\u{0397}',
|
||||
Theta: '\u{0398}',
|
||||
Iota: '\u{0399}',
|
||||
Kappa: '\u{039A}',
|
||||
Lambda: '\u{039B}',
|
||||
Mu: '\u{039C}',
|
||||
Nu: '\u{039D}',
|
||||
Xi: '\u{039E}',
|
||||
Omicron: '\u{039F}',
|
||||
Pi: '\u{03A0}',
|
||||
Rho: '\u{03A1}',
|
||||
Sigma: '\u{03A3}',
|
||||
Tau: '\u{03A4}',
|
||||
Upsilon: '\u{03A5}',
|
||||
Phi: '\u{03A6}',
|
||||
Chi: '\u{03A7}',
|
||||
Psi: '\u{03A8}',
|
||||
Omega: '\u{03A9}',
|
||||
alpha: '\u{03B1}',
|
||||
beta: '\u{03B2}',
|
||||
gamma: '\u{03B3}',
|
||||
delta: '\u{03B4}',
|
||||
epsilon: '\u{03B5}',
|
||||
zeta: '\u{03B6}',
|
||||
eta: '\u{03B7}',
|
||||
theta: '\u{03B8}',
|
||||
iota: '\u{03B9}',
|
||||
kappa: '\u{03BA}',
|
||||
lambda: '\u{03BB}',
|
||||
mu: '\u{03BC}',
|
||||
nu: '\u{03BD}',
|
||||
xi: '\u{03BE}',
|
||||
omicron: '\u{03BF}',
|
||||
pi: '\u{03C0}',
|
||||
rho: '\u{03C1}',
|
||||
sigmaf: '\u{03C2}',
|
||||
sigma: '\u{03C3}',
|
||||
tau: '\u{03C4}',
|
||||
upsilon: '\u{03C5}',
|
||||
phi: '\u{03C6}',
|
||||
chi: '\u{03C7}',
|
||||
psi: '\u{03C8}',
|
||||
omega: '\u{03C9}',
|
||||
thetasym: '\u{03D1}',
|
||||
upsih: '\u{03D2}',
|
||||
piv: '\u{03D6}',
|
||||
ensp: '\u{2002}',
|
||||
emsp: '\u{2003}',
|
||||
thinsp: '\u{2009}',
|
||||
zwnj: '\u{200C}',
|
||||
zwj: '\u{200D}',
|
||||
lrm: '\u{200E}',
|
||||
rlm: '\u{200F}',
|
||||
ndash: '\u{2013}',
|
||||
mdash: '\u{2014}',
|
||||
lsquo: '\u{2018}',
|
||||
rsquo: '\u{2019}',
|
||||
sbquo: '\u{201A}',
|
||||
ldquo: '\u{201C}',
|
||||
rdquo: '\u{201D}',
|
||||
bdquo: '\u{201E}',
|
||||
dagger: '\u{2020}',
|
||||
Dagger: '\u{2021}',
|
||||
bull: '\u{2022}',
|
||||
hellip: '\u{2026}',
|
||||
permil: '\u{2030}',
|
||||
prime: '\u{2032}',
|
||||
Prime: '\u{2033}',
|
||||
lsaquo: '\u{2039}',
|
||||
rsaquo: '\u{203A}',
|
||||
oline: '\u{203E}',
|
||||
frasl: '\u{2044}',
|
||||
euro: '\u{20AC}',
|
||||
image: '\u{2111}',
|
||||
weierp: '\u{2118}',
|
||||
real: '\u{211C}',
|
||||
trade: '\u{2122}',
|
||||
alefsym: '\u{2135}',
|
||||
larr: '\u{2190}',
|
||||
uarr: '\u{2191}',
|
||||
rarr: '\u{2192}',
|
||||
darr: '\u{2193}',
|
||||
harr: '\u{2194}',
|
||||
crarr: '\u{21B5}',
|
||||
lArr: '\u{21D0}',
|
||||
uArr: '\u{21D1}',
|
||||
rArr: '\u{21D2}',
|
||||
dArr: '\u{21D3}',
|
||||
hArr: '\u{21D4}',
|
||||
forall: '\u{2200}',
|
||||
part: '\u{2202}',
|
||||
exist: '\u{2203}',
|
||||
empty: '\u{2205}',
|
||||
nabla: '\u{2207}',
|
||||
isin: '\u{2208}',
|
||||
notin: '\u{2209}',
|
||||
ni: '\u{220B}',
|
||||
prod: '\u{220F}',
|
||||
sum: '\u{2211}',
|
||||
minus: '\u{2212}',
|
||||
lowast: '\u{2217}',
|
||||
radic: '\u{221A}',
|
||||
prop: '\u{221D}',
|
||||
infin: '\u{221E}',
|
||||
ang: '\u{2220}',
|
||||
and: '\u{2227}',
|
||||
or: '\u{2228}',
|
||||
cap: '\u{2229}',
|
||||
cup: '\u{222A}',
|
||||
int: '\u{222B}',
|
||||
there4: '\u{2234}',
|
||||
sim: '\u{223C}',
|
||||
cong: '\u{2245}',
|
||||
asymp: '\u{2248}',
|
||||
ne: '\u{2260}',
|
||||
equiv: '\u{2261}',
|
||||
le: '\u{2264}',
|
||||
ge: '\u{2265}',
|
||||
sub: '\u{2282}',
|
||||
sup: '\u{2283}',
|
||||
nsub: '\u{2284}',
|
||||
sube: '\u{2286}',
|
||||
supe: '\u{2287}',
|
||||
oplus: '\u{2295}',
|
||||
otimes: '\u{2297}',
|
||||
perp: '\u{22A5}',
|
||||
sdot: '\u{22C5}',
|
||||
lceil: '\u{2308}',
|
||||
rceil: '\u{2309}',
|
||||
lfloor: '\u{230A}',
|
||||
rfloor: '\u{230B}',
|
||||
lang: '\u{2329}',
|
||||
rang: '\u{232A}',
|
||||
loz: '\u{25CA}',
|
||||
spades: '\u{2660}',
|
||||
clubs: '\u{2663}',
|
||||
hearts: '\u{2665}',
|
||||
diams: '\u{2666}',
|
||||
);
|
||||
1281
third-party/vendor/swc_ecma_parser/src/lexer/mod.rs
vendored
Normal file
1281
third-party/vendor/swc_ecma_parser/src/lexer/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
852
third-party/vendor/swc_ecma_parser/src/lexer/number.rs
vendored
Normal file
852
third-party/vendor/swc_ecma_parser/src/lexer/number.rs
vendored
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
//! Lexer methods related to reading numbers.
|
||||
//!
|
||||
//!
|
||||
//! See https://tc39.github.io/ecma262/#sec-literals-numeric-literals
|
||||
use std::{borrow::Cow, fmt::Write};
|
||||
|
||||
use either::Either;
|
||||
use num_bigint::BigInt as BigIntValue;
|
||||
use num_traits::{Num as NumTrait, ToPrimitive};
|
||||
use smartstring::{LazyCompact, SmartString};
|
||||
use swc_common::SyntaxContext;
|
||||
use tracing::trace;
|
||||
|
||||
use super::*;
|
||||
use crate::error::SyntaxError;
|
||||
|
||||
struct LazyBigInt<const RADIX: u8> {
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl<const RADIX: u8> LazyBigInt<RADIX> {
|
||||
fn new(value: String) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_value(self) -> BigIntValue {
|
||||
BigIntValue::parse_bytes(self.value.as_bytes(), RADIX as _)
|
||||
.expect("failed to parse string as a bigint")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
/// Reads an integer, octal integer, or floating-point number
|
||||
pub(super) fn read_number(
|
||||
&mut self,
|
||||
starts_with_dot: bool,
|
||||
) -> LexResult<Either<(f64, Atom), (Box<BigIntValue>, Atom)>> {
|
||||
debug_assert!(self.cur().is_some());
|
||||
|
||||
if starts_with_dot {
|
||||
debug_assert_eq!(
|
||||
self.cur(),
|
||||
Some('.'),
|
||||
"read_number(starts_with_dot = true) expects current char to be '.'"
|
||||
);
|
||||
}
|
||||
|
||||
let start = self.cur_pos();
|
||||
let mut raw_val = SmartString::<LazyCompact>::new();
|
||||
let mut raw_str = SmartString::<LazyCompact>::new();
|
||||
|
||||
let val = if starts_with_dot {
|
||||
// first char is '.'
|
||||
0f64
|
||||
} else {
|
||||
let starts_with_zero = self.cur().unwrap() == '0';
|
||||
|
||||
// Use read_number_no_dot to support long numbers.
|
||||
let (val, s, mut raw, not_octal) = self.read_number_no_dot_as_str::<10>()?;
|
||||
|
||||
if self.eat(b'n') {
|
||||
raw.push('n');
|
||||
|
||||
return Ok(Either::Right((
|
||||
Box::new(s.into_value()),
|
||||
self.atoms.borrow_mut().intern(&*raw),
|
||||
)));
|
||||
}
|
||||
|
||||
write!(raw_val, "{}", &s.value).unwrap();
|
||||
|
||||
raw_str.push_str(&raw);
|
||||
|
||||
if starts_with_zero {
|
||||
// TODO: I guess it would be okay if I don't use -ffast-math
|
||||
// (or something like that), but needs review.
|
||||
if val == 0.0f64 {
|
||||
// If only one zero is used, it's decimal.
|
||||
// And if multiple zero is used, it's octal.
|
||||
//
|
||||
// e.g. `0` is decimal (so it can be part of float)
|
||||
//
|
||||
// e.g. `000` is octal
|
||||
if start.0 != self.last_pos().0 - 1 {
|
||||
// `-1` is utf 8 length of `0`
|
||||
return self.make_legacy_octal(start, 0f64).map(|value| {
|
||||
Either::Left((value, self.atoms.borrow_mut().intern(&*raw)))
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// strict mode hates non-zero decimals starting with zero.
|
||||
// e.g. 08.1 is strict mode violation but 0.1 is valid float.
|
||||
|
||||
if val.fract() == 0.0 {
|
||||
let val_str = &s.value;
|
||||
|
||||
// if it contains '8' or '9', it's decimal.
|
||||
if not_octal {
|
||||
// Continue parsing
|
||||
self.emit_strict_mode_error(start, SyntaxError::LegacyDecimal);
|
||||
} else {
|
||||
// It's Legacy octal, and we should reinterpret value.
|
||||
let val = BigIntValue::from_str_radix(val_str, 8)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"failed to parse {} using `from_str_radix`: {:?}",
|
||||
val_str, err
|
||||
)
|
||||
})
|
||||
.to_f64()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("failed to parse {} into float using BigInt", val_str)
|
||||
});
|
||||
|
||||
return self.make_legacy_octal(start, val).map(|value| {
|
||||
Either::Left((value, self.atoms.borrow_mut().intern(&*raw)))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val
|
||||
};
|
||||
|
||||
// At this point, number cannot be an octal literal.
|
||||
|
||||
let mut val: f64 = val;
|
||||
|
||||
// `0.a`, `08.a`, `102.a` are invalid.
|
||||
//
|
||||
// `.1.a`, `.1e-4.a` are valid,
|
||||
if self.cur() == Some('.') {
|
||||
raw_val.push('.');
|
||||
raw_str.push('.');
|
||||
|
||||
self.bump();
|
||||
|
||||
if starts_with_dot {
|
||||
debug_assert!(self.cur().is_some());
|
||||
debug_assert!(self.cur().unwrap().is_ascii_digit());
|
||||
}
|
||||
|
||||
let mut raw = Raw(Some(Default::default()));
|
||||
// Read numbers after dot
|
||||
let dec_val = self.read_int::<10>(0, &mut raw)?;
|
||||
|
||||
raw_str.push_str(raw.0.as_ref().unwrap());
|
||||
|
||||
val = {
|
||||
if dec_val.is_some() {
|
||||
raw_val.push_str(raw.0.as_ref().unwrap());
|
||||
}
|
||||
|
||||
// Remove number separator from number
|
||||
if raw_val.contains('_') {
|
||||
Cow::Owned(raw_val.replace('_', ""))
|
||||
} else {
|
||||
Cow::Borrowed(&*raw_val)
|
||||
}
|
||||
.parse()
|
||||
.expect("failed to parse float using rust's impl")
|
||||
};
|
||||
}
|
||||
|
||||
// Handle 'e' and 'E'
|
||||
//
|
||||
// .5e1 = 5
|
||||
// 1e2 = 100
|
||||
// 1e+2 = 100
|
||||
// 1e-2 = 0.01
|
||||
match self.cur() {
|
||||
Some(e @ 'e') | Some(e @ 'E') => {
|
||||
self.bump();
|
||||
|
||||
let next = match self.cur() {
|
||||
Some(next) => next,
|
||||
None => {
|
||||
let pos = self.cur_pos();
|
||||
self.error(pos, SyntaxError::NumLitTerminatedWithExp)?
|
||||
}
|
||||
};
|
||||
|
||||
raw_val.push('e');
|
||||
raw_str.push(e);
|
||||
|
||||
let positive = if next == '+' || next == '-' {
|
||||
self.bump(); // remove '+', '-'
|
||||
|
||||
raw_str.push(next);
|
||||
|
||||
next == '+'
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let mut raw = Raw(Some(Default::default()));
|
||||
let exp = self.read_number_no_dot::<10>(&mut raw)?;
|
||||
|
||||
raw_str.push_str(&raw.0.take().unwrap());
|
||||
|
||||
val = if exp == f64::INFINITY {
|
||||
if positive && val != 0.0 {
|
||||
f64::INFINITY
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
let flag = if positive { '+' } else { '-' };
|
||||
|
||||
raw_val.push(flag);
|
||||
|
||||
write!(raw_val, "{}", exp).unwrap();
|
||||
|
||||
if raw_val.contains('_') {
|
||||
Cow::Owned(raw_val.replace('_', ""))
|
||||
} else {
|
||||
Cow::Borrowed(&*raw_val)
|
||||
}
|
||||
.parse()
|
||||
.expect("failed to parse float literal")
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.ensure_not_ident()?;
|
||||
|
||||
Ok(Either::Left((
|
||||
val,
|
||||
self.atoms.borrow_mut().intern(&*raw_str),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Returns `Left(value)` or `Right(BigInt)`
|
||||
pub(super) fn read_radix_number<const RADIX: u8>(
|
||||
&mut self,
|
||||
) -> LexResult<Either<(f64, Atom), (Box<BigIntValue>, Atom)>> {
|
||||
debug_assert!(
|
||||
RADIX == 2 || RADIX == 8 || RADIX == 16,
|
||||
"radix should be one of 2, 8, 16, but got {}",
|
||||
RADIX
|
||||
);
|
||||
debug_assert_eq!(self.cur(), Some('0'));
|
||||
|
||||
self.with_buf(|l, buf| {
|
||||
l.bump();
|
||||
|
||||
buf.push('0');
|
||||
|
||||
let c = match l.input.cur() {
|
||||
Some(c) => {
|
||||
l.bump();
|
||||
|
||||
c
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
|
||||
buf.push(c);
|
||||
|
||||
let (val, s, raw, _) = l.read_number_no_dot_as_str::<RADIX>()?;
|
||||
|
||||
buf.push_str(&raw);
|
||||
|
||||
if l.eat(b'n') {
|
||||
buf.push('n');
|
||||
|
||||
return Ok(Either::Right((
|
||||
Box::new(s.into_value()),
|
||||
l.atoms.borrow_mut().intern(&**buf),
|
||||
)));
|
||||
}
|
||||
|
||||
l.ensure_not_ident()?;
|
||||
|
||||
Ok(Either::Left((val, l.atoms.borrow_mut().intern(&**buf))))
|
||||
})
|
||||
}
|
||||
|
||||
/// This can read long integers like
|
||||
/// "13612536612375123612312312312312312312312".
|
||||
fn read_number_no_dot<const RADIX: u8>(&mut self, raw: &mut Raw) -> LexResult<f64> {
|
||||
debug_assert!(
|
||||
RADIX == 2 || RADIX == 8 || RADIX == 10 || RADIX == 16,
|
||||
"radix for read_number_no_dot should be one of 2, 8, 10, 16, but got {}",
|
||||
RADIX
|
||||
);
|
||||
let start = self.cur_pos();
|
||||
|
||||
let mut read_any = false;
|
||||
|
||||
let res = self.read_digits::<_, f64, RADIX>(
|
||||
|total, radix, v| {
|
||||
read_any = true;
|
||||
|
||||
Ok((f64::mul_add(total, radix as f64, v as f64), true))
|
||||
},
|
||||
raw,
|
||||
true,
|
||||
);
|
||||
|
||||
if !read_any {
|
||||
self.error(start, SyntaxError::ExpectedDigit { radix: RADIX })?;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// This can read long integers like
|
||||
/// "13612536612375123612312312312312312312312".
|
||||
///
|
||||
///
|
||||
/// Returned bool is `true` is there was `8` or `9`.
|
||||
fn read_number_no_dot_as_str<const RADIX: u8>(
|
||||
&mut self,
|
||||
) -> LexResult<(f64, LazyBigInt<RADIX>, SmartString<LazyCompact>, bool)> {
|
||||
debug_assert!(
|
||||
RADIX == 2 || RADIX == 8 || RADIX == 10 || RADIX == 16,
|
||||
"radix for read_number_no_dot should be one of 2, 8, 10, 16, but got {}",
|
||||
RADIX
|
||||
);
|
||||
let start = self.cur_pos();
|
||||
|
||||
let mut non_octal = false;
|
||||
let mut read_any = false;
|
||||
|
||||
let mut raw = Raw(Some(Default::default()));
|
||||
|
||||
self.read_digits::<_, f64, RADIX>(
|
||||
|total, radix, v| {
|
||||
read_any = true;
|
||||
|
||||
if v == 8 || v == 9 {
|
||||
non_octal = true;
|
||||
}
|
||||
|
||||
Ok((f64::mul_add(total, radix as f64, v as f64), true))
|
||||
},
|
||||
&mut raw,
|
||||
true,
|
||||
)?;
|
||||
|
||||
if !read_any {
|
||||
self.error(start, SyntaxError::ExpectedDigit { radix: RADIX })?;
|
||||
}
|
||||
|
||||
let raw_str = raw.0.take().unwrap();
|
||||
// Remove number separator from number
|
||||
let raw_number_str = raw_str.replace('_', "");
|
||||
let parsed_float = BigIntValue::from_str_radix(&raw_number_str, RADIX as u32)
|
||||
.expect("failed to parse float using BigInt")
|
||||
.to_f64()
|
||||
.expect("failed to parse float using BigInt");
|
||||
Ok((
|
||||
parsed_float,
|
||||
LazyBigInt::new(raw_number_str),
|
||||
raw_str,
|
||||
non_octal,
|
||||
))
|
||||
}
|
||||
|
||||
/// Ensure that ident cannot directly follow numbers.
|
||||
fn ensure_not_ident(&mut self) -> LexResult<()> {
|
||||
match self.cur() {
|
||||
Some(c) if c.is_ident_start() => {
|
||||
let span = pos_span(self.cur_pos());
|
||||
self.error_span(span, SyntaxError::IdentAfterNum)?
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read an integer in the given radix. Return `None` if zero digits
|
||||
/// were read, the integer value otherwise.
|
||||
/// When `len` is not zero, this
|
||||
/// will return `None` unless the integer has exactly `len` digits.
|
||||
pub(super) fn read_int<const RADIX: u8>(
|
||||
&mut self,
|
||||
len: u8,
|
||||
raw: &mut Raw,
|
||||
) -> LexResult<Option<f64>> {
|
||||
let mut count = 0u16;
|
||||
let v = self.read_digits::<_, Option<f64>, RADIX>(
|
||||
|opt: Option<f64>, radix, val| {
|
||||
count += 1;
|
||||
let total = opt.unwrap_or_default() * radix as f64 + val as f64;
|
||||
|
||||
Ok((Some(total), count != len as u16))
|
||||
},
|
||||
raw,
|
||||
true,
|
||||
)?;
|
||||
if len != 0 && count != len as u16 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_int_u32<const RADIX: u8>(
|
||||
&mut self,
|
||||
len: u8,
|
||||
raw: &mut Raw,
|
||||
) -> LexResult<Option<u32>> {
|
||||
let start = self.state.start;
|
||||
|
||||
let mut count = 0;
|
||||
let v = self.read_digits::<_, Option<u32>, RADIX>(
|
||||
|opt: Option<u32>, radix, val| {
|
||||
count += 1;
|
||||
|
||||
let total = opt
|
||||
.unwrap_or_default()
|
||||
.checked_mul(radix as u32)
|
||||
.and_then(|v| v.checked_add(val))
|
||||
.ok_or_else(|| {
|
||||
let span = Span::new(start, start, SyntaxContext::empty());
|
||||
Error::new(span, SyntaxError::InvalidUnicodeEscape)
|
||||
})?;
|
||||
|
||||
Ok((Some(total), count != len))
|
||||
},
|
||||
raw,
|
||||
true,
|
||||
)?;
|
||||
if len != 0 && count != len {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// `op`- |total, radix, value| -> (total * radix + value, continue)
|
||||
fn read_digits<F, Ret, const RADIX: u8>(
|
||||
&mut self,
|
||||
mut op: F,
|
||||
raw: &mut Raw,
|
||||
allow_num_separator: bool,
|
||||
) -> LexResult<Ret>
|
||||
where
|
||||
F: FnMut(Ret, u8, u32) -> LexResult<(Ret, bool)>,
|
||||
Ret: Copy + Default,
|
||||
{
|
||||
debug_assert!(
|
||||
RADIX == 2 || RADIX == 8 || RADIX == 10 || RADIX == 16,
|
||||
"radix for read_int should be one of 2, 8, 10, 16, but got {}",
|
||||
RADIX
|
||||
);
|
||||
|
||||
if cfg!(feature = "debug") {
|
||||
trace!("read_digits(radix = {}), cur = {:?}", RADIX, self.cur());
|
||||
}
|
||||
|
||||
let start = self.cur_pos();
|
||||
let mut total: Ret = Default::default();
|
||||
let mut prev = None;
|
||||
|
||||
while let Some(c) = self.cur() {
|
||||
if allow_num_separator && c == '_' {
|
||||
let is_allowed = |c: Option<char>| {
|
||||
if c.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let c = c.unwrap();
|
||||
|
||||
c.is_digit(RADIX as _)
|
||||
};
|
||||
let is_forbidden = |c: Option<char>| {
|
||||
if c.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if RADIX == 16 {
|
||||
matches!(c.unwrap(), '.' | 'X' | '_' | 'x')
|
||||
} else {
|
||||
matches!(c.unwrap(), '.' | 'B' | 'E' | 'O' | '_' | 'b' | 'e' | 'o')
|
||||
}
|
||||
};
|
||||
|
||||
let next = self.input.peek();
|
||||
|
||||
if !is_allowed(next) || is_forbidden(prev) || is_forbidden(next) {
|
||||
self.emit_error(
|
||||
start,
|
||||
SyntaxError::NumericSeparatorIsAllowedOnlyBetweenTwoDigits,
|
||||
);
|
||||
}
|
||||
|
||||
// Ignore this _ character
|
||||
unsafe {
|
||||
// Safety: cur() returns Some(c) where c is a valid char
|
||||
self.input.bump();
|
||||
}
|
||||
raw.push(c);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// e.g. (val for a) = 10 where radix = 16
|
||||
let val = if let Some(val) = c.to_digit(RADIX as _) {
|
||||
val
|
||||
} else {
|
||||
return Ok(total);
|
||||
};
|
||||
|
||||
raw.push(c);
|
||||
|
||||
self.bump();
|
||||
|
||||
let (t, cont) = op(total, RADIX, val)?;
|
||||
|
||||
total = t;
|
||||
|
||||
if !cont {
|
||||
return Ok(total);
|
||||
}
|
||||
|
||||
prev = Some(c);
|
||||
}
|
||||
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
fn make_legacy_octal(&mut self, start: BytePos, val: f64) -> LexResult<f64> {
|
||||
self.ensure_not_ident()?;
|
||||
|
||||
if self.syntax.typescript() && self.target >= EsVersion::Es5 {
|
||||
self.emit_error(start, SyntaxError::TS1085);
|
||||
}
|
||||
|
||||
self.emit_strict_mode_error(start, SyntaxError::LegacyOctal);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{f64::INFINITY, panic};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn lex<F, Ret>(s: &'static str, f: F) -> Ret
|
||||
where
|
||||
F: FnOnce(&mut Lexer<'_>) -> Ret,
|
||||
{
|
||||
crate::with_test_sess(s, |_, input| {
|
||||
let mut l = Lexer::new(
|
||||
Syntax::Es(Default::default()),
|
||||
Default::default(),
|
||||
input,
|
||||
None,
|
||||
);
|
||||
let ret = f(&mut l);
|
||||
assert_eq!(l.input.cur(), None);
|
||||
Ok(ret)
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn num(s: &'static str) -> (f64, Atom) {
|
||||
lex(s, |l| {
|
||||
l.read_number(s.starts_with('.')).unwrap().left().unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn int<const RADIX: u8>(s: &'static str) -> u32 {
|
||||
lex(s, |l| {
|
||||
l.read_int_u32::<RADIX>(0, &mut Raw(None))
|
||||
.unwrap()
|
||||
.expect("read_int returned None")
|
||||
})
|
||||
}
|
||||
|
||||
const LONG: &str = "1e10000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000";
|
||||
#[test]
|
||||
fn num_inf() {
|
||||
assert_eq!(num(LONG), (INFINITY, LONG.into()));
|
||||
}
|
||||
|
||||
/// Number >= 2^53
|
||||
#[test]
|
||||
fn num_big_exp() {
|
||||
assert_eq!((1e30, "1e30".into()), num("1e30"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_very_big_exp() {
|
||||
const LARGE_POSITIVE_EXP: &str =
|
||||
"1e100000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000";
|
||||
const LARGE_NEGATIVE_EXP: &str =
|
||||
"1e-10000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
000000000000000000000000000000000000000000000000000000";
|
||||
const ZERO_WITH_LARGE_POSITIVE_EXP: &str =
|
||||
"0e100000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000";
|
||||
const ZERO_WITH_LARGE_NEGATIVE_EXP: &str =
|
||||
"0e-10000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
000000000000000000000000000000000000000000000000000000";
|
||||
const LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP: &str =
|
||||
"10000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
000000000000000000000000000000000000000000000000000000\
|
||||
e-100000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
00000000000000000000000000000000000000000000000000000000000000000\
|
||||
000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
assert_eq!(
|
||||
num(LARGE_POSITIVE_EXP),
|
||||
(INFINITY, LARGE_POSITIVE_EXP.into())
|
||||
);
|
||||
assert_eq!(num(LARGE_NEGATIVE_EXP), (0.0, LARGE_NEGATIVE_EXP.into()));
|
||||
assert_eq!(
|
||||
num(ZERO_WITH_LARGE_POSITIVE_EXP),
|
||||
(0.0, ZERO_WITH_LARGE_POSITIVE_EXP.into())
|
||||
);
|
||||
assert_eq!(
|
||||
num(ZERO_WITH_LARGE_NEGATIVE_EXP),
|
||||
(0.0, ZERO_WITH_LARGE_NEGATIVE_EXP.into())
|
||||
);
|
||||
assert_eq!(
|
||||
num(LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP),
|
||||
(0.0, LARGE_MANTISSA_WITH_LARGE_NEGATIVE_EXP.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_big_many_zero() {
|
||||
assert_eq!(
|
||||
(
|
||||
1_000_000_000_000_000_000_000_000_000_000f64,
|
||||
"1000000000000000000000000000000".into()
|
||||
),
|
||||
num("1000000000000000000000000000000")
|
||||
);
|
||||
assert_eq!(
|
||||
(3.402_823_466_385_288_6e38, "34028234663852886e22".into()),
|
||||
num("34028234663852886e22"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn big_number_with_fract() {
|
||||
assert_eq!(
|
||||
(77777777777777777.1f64, "77777777777777777.1".into()),
|
||||
num("77777777777777777.1")
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_480() {
|
||||
assert_eq!((9.09, "9.09".into()), num("9.09"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_legacy_octal() {
|
||||
assert_eq!((0o12 as f64, "0012".into()), num("0012"));
|
||||
assert_eq!((10f64, "012".into()), num("012"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_int_1() {
|
||||
assert_eq!(60, int::<10>("60"));
|
||||
assert_eq!(0o73, int::<8>("73"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_int_short() {
|
||||
assert_eq!(7, int::<10>("7"));
|
||||
assert_eq!(10, int::<10>("10"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_radix_number() {
|
||||
assert_eq!(
|
||||
(0o73 as f64, "0o73".into()),
|
||||
lex("0o73", |l| l
|
||||
.read_radix_number::<8>()
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_num_sep() {
|
||||
assert_eq!(1_000, int::<10>("1_000"));
|
||||
assert_eq!(0xaebece, int::<16>("AE_BE_CE"));
|
||||
assert_eq!(0b1010000110000101, int::<2>("1010_0001_1000_0101"));
|
||||
assert_eq!(0o0666, int::<8>("0_6_6_6"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_bigint() {
|
||||
assert_eq!(
|
||||
lex(
|
||||
"10000000000000000000000000000000000000000000000000000n",
|
||||
|l| l.read_number(false).unwrap().right().unwrap()
|
||||
),
|
||||
(
|
||||
Box::new(
|
||||
"10000000000000000000000000000000000000000000000000000"
|
||||
.parse::<BigIntValue>()
|
||||
.unwrap()
|
||||
),
|
||||
Atom::from("10000000000000000000000000000000000000000000000000000n")
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_bin_number() {
|
||||
const LONG: &str =
|
||||
"0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111";
|
||||
const VERY_LARGE_BINARY_NUMBER: &str =
|
||||
"0B1111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
111111111111111111111111111111111111111111111111111111111111111111\
|
||||
0010111110001111111111";
|
||||
assert_eq!(
|
||||
lex(LONG, |l| l
|
||||
.read_radix_number::<2>()
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap()),
|
||||
(9.671_406_556_917_009e24, LONG.into())
|
||||
);
|
||||
assert_eq!(
|
||||
lex(VERY_LARGE_BINARY_NUMBER, |l| l
|
||||
.read_radix_number::<2>()
|
||||
.unwrap()
|
||||
.left()
|
||||
.unwrap()),
|
||||
(1.0972248137587377e304, VERY_LARGE_BINARY_NUMBER.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_float_number() {
|
||||
const LONG: &str = "9.671406556917009e+24";
|
||||
|
||||
assert_eq!(num(LONG), (9.671_406_556_917_009e24, LONG.into()));
|
||||
}
|
||||
|
||||
/// Valid even on strict mode.
|
||||
const VALID_CASES: &[&str] = &[".0", "0.e-1", "0e8", ".8e1", "0.8e1", "1.18e1"];
|
||||
const INVALID_CASES_ON_STRICT: &[&str] = &["08e1", "08.1", "08.8e1", "08", "01"];
|
||||
const INVALID_CASES: &[&str] = &["01.8e1", "012e1", "00e1", "00.0"];
|
||||
|
||||
fn test_floats(strict: bool, success: bool, cases: &'static [&'static str]) {
|
||||
for case in cases {
|
||||
println!(
|
||||
"Testing {} (when strict = {}); Expects success = {}",
|
||||
case, strict, success
|
||||
);
|
||||
// lazy way to get expected values
|
||||
let expected: f64 = (i64::from_str_radix(case, 8).map(|v| v as f64))
|
||||
.or_else(|_| case.parse::<i64>().map(|v| v as f64))
|
||||
.or_else(|_| case.parse::<f64>())
|
||||
.unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"failed to parse '{}' as float using str.parse(): {}",
|
||||
case, err
|
||||
)
|
||||
});
|
||||
|
||||
let vec = panic::catch_unwind(|| {
|
||||
crate::with_test_sess(case, |_, input| {
|
||||
let mut l = Lexer::new(Syntax::default(), Default::default(), input, None);
|
||||
l.ctx.strict = strict;
|
||||
Ok(l.map(|ts| ts.token).collect::<Vec<_>>())
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
if success {
|
||||
let vec = match vec {
|
||||
Ok(vec) => vec,
|
||||
Err(err) => panic::resume_unwind(err),
|
||||
};
|
||||
|
||||
assert_eq!(vec.len(), 1);
|
||||
|
||||
let token = vec.into_iter().next().unwrap();
|
||||
let value = match token {
|
||||
Token::Num { value, .. } => value,
|
||||
_ => {
|
||||
panic!("expected num token in test")
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(expected, value);
|
||||
} else if let Ok(vec) = vec {
|
||||
assert_ne!(
|
||||
vec![Num {
|
||||
value: expected,
|
||||
raw: expected.to_string().into()
|
||||
}],
|
||||
vec
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn strict_mode() {
|
||||
// test_floats(true, true, VALID_CASES);
|
||||
// test_floats(true, false, INVALID_CASES_ON_STRICT);
|
||||
// test_floats(true, false, INVALID_CASES);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn non_strict() {
|
||||
test_floats(false, true, VALID_CASES);
|
||||
test_floats(false, true, INVALID_CASES_ON_STRICT);
|
||||
test_floats(false, false, INVALID_CASES);
|
||||
}
|
||||
}
|
||||
861
third-party/vendor/swc_ecma_parser/src/lexer/state.rs
vendored
Normal file
861
third-party/vendor/swc_ecma_parser/src/lexer/state.rs
vendored
Normal file
|
|
@ -0,0 +1,861 @@
|
|||
use std::mem::take;
|
||||
|
||||
use swc_common::{BytePos, Span};
|
||||
use tracing::trace;
|
||||
|
||||
use super::{
|
||||
comments_buffer::{BufferedComment, BufferedCommentKind},
|
||||
Context, Input, Lexer,
|
||||
};
|
||||
use crate::{
|
||||
error::{Error, SyntaxError},
|
||||
input::Tokens,
|
||||
lexer::util::CharExt,
|
||||
token::*,
|
||||
EsVersion, Syntax,
|
||||
};
|
||||
|
||||
/// State of lexer.
|
||||
///
|
||||
/// Ported from babylon.
|
||||
#[derive(Clone)]
|
||||
pub(super) struct State {
|
||||
pub is_expr_allowed: bool,
|
||||
pub next_regexp: Option<BytePos>,
|
||||
/// if line break exists between previous token and new token?
|
||||
pub had_line_break: bool,
|
||||
/// if line break exists before last?
|
||||
pub had_line_break_before_last: bool,
|
||||
/// TODO: Remove this field.
|
||||
is_first: bool,
|
||||
pub start: BytePos,
|
||||
pub cur_line: usize,
|
||||
pub line_start: BytePos,
|
||||
pub prev_hi: BytePos,
|
||||
|
||||
context: TokenContexts,
|
||||
syntax: Syntax,
|
||||
|
||||
token_type: Option<TokenType>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum TokenType {
|
||||
Template,
|
||||
Dot,
|
||||
Colon,
|
||||
LBrace,
|
||||
RParen,
|
||||
Semi,
|
||||
BinOp(BinOpToken),
|
||||
Keyword(Keyword),
|
||||
JSXName,
|
||||
JSXText,
|
||||
JSXTagStart,
|
||||
JSXTagEnd,
|
||||
Arrow,
|
||||
Other {
|
||||
before_expr: bool,
|
||||
can_have_trailing_comment: bool,
|
||||
},
|
||||
}
|
||||
impl TokenType {
|
||||
#[inline]
|
||||
const fn before_expr(self) -> bool {
|
||||
match self {
|
||||
TokenType::JSXName
|
||||
| TokenType::JSXTagStart
|
||||
| TokenType::JSXTagEnd
|
||||
| TokenType::Template
|
||||
| TokenType::Dot
|
||||
| TokenType::RParen => false,
|
||||
|
||||
TokenType::JSXText
|
||||
| TokenType::Colon
|
||||
| TokenType::LBrace
|
||||
| TokenType::Semi
|
||||
| TokenType::Arrow => true,
|
||||
|
||||
TokenType::BinOp(b) => b.before_expr(),
|
||||
TokenType::Keyword(k) => k.before_expr(),
|
||||
TokenType::Other { before_expr, .. } => before_expr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Token> for TokenType {
|
||||
#[inline]
|
||||
fn from(t: &Token) -> Self {
|
||||
match *t {
|
||||
Token::Template { .. } => TokenType::Template,
|
||||
Token::Dot => TokenType::Dot,
|
||||
Token::Colon => TokenType::Colon,
|
||||
Token::LBrace => TokenType::LBrace,
|
||||
Token::RParen => TokenType::RParen,
|
||||
Token::Semi => TokenType::Semi,
|
||||
Token::JSXTagEnd => TokenType::JSXTagEnd,
|
||||
Token::JSXTagStart => TokenType::JSXTagStart,
|
||||
Token::JSXText { .. } => TokenType::JSXText,
|
||||
Token::JSXName { .. } => TokenType::JSXName,
|
||||
Token::BinOp(op) => TokenType::BinOp(op),
|
||||
Token::Arrow => TokenType::Arrow,
|
||||
|
||||
Token::Word(Word::Keyword(k)) => TokenType::Keyword(k),
|
||||
_ => TokenType::Other {
|
||||
before_expr: t.before_expr(),
|
||||
can_have_trailing_comment: matches!(
|
||||
*t,
|
||||
Token::Num { .. }
|
||||
| Token::Str { .. }
|
||||
| Token::Word(Word::Ident(..))
|
||||
| Token::DollarLBrace
|
||||
| Token::Regex(..)
|
||||
| Token::BigInt { .. }
|
||||
| Token::JSXText { .. }
|
||||
| Token::RBrace
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokens for Lexer<'_> {
|
||||
#[inline]
|
||||
fn set_ctx(&mut self, ctx: Context) {
|
||||
if ctx.module && !self.module_errors.borrow().is_empty() {
|
||||
let mut module_errors = self.module_errors.borrow_mut();
|
||||
self.errors.borrow_mut().append(&mut *module_errors);
|
||||
}
|
||||
self.ctx = ctx
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ctx(&self) -> Context {
|
||||
self.ctx
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn syntax(&self) -> Syntax {
|
||||
self.syntax
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn target(&self) -> EsVersion {
|
||||
self.target
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start_pos(&self) -> BytePos {
|
||||
self.start_pos
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_expr_allowed(&mut self, allow: bool) {
|
||||
self.set_expr_allowed(allow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_next_regexp(&mut self, start: Option<BytePos>) {
|
||||
self.state.next_regexp = start;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn token_context(&self) -> &TokenContexts {
|
||||
&self.state.context
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn token_context_mut(&mut self) -> &mut TokenContexts {
|
||||
&mut self.state.context
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_token_context(&mut self, c: TokenContexts) {
|
||||
self.state.context = c;
|
||||
}
|
||||
|
||||
fn add_error(&self, error: Error) {
|
||||
self.errors.borrow_mut().push(error);
|
||||
}
|
||||
|
||||
fn add_module_mode_error(&self, error: Error) {
|
||||
if self.ctx.module {
|
||||
self.add_error(error);
|
||||
return;
|
||||
}
|
||||
self.module_errors.borrow_mut().push(error);
|
||||
}
|
||||
|
||||
fn take_errors(&mut self) -> Vec<Error> {
|
||||
take(&mut self.errors.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Lexer<'a> {
|
||||
type Item = TokenAndSpan;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut start = self.cur_pos();
|
||||
|
||||
let res = (|| -> Result<Option<_>, _> {
|
||||
if let Some(start) = self.state.next_regexp {
|
||||
return Ok(Some(self.read_regexp(start)?));
|
||||
}
|
||||
|
||||
if self.state.is_first {
|
||||
if let Some(shebang) = self.read_shebang()? {
|
||||
return Ok(Some(Token::Shebang(shebang)));
|
||||
}
|
||||
}
|
||||
|
||||
self.state.had_line_break = self.state.is_first;
|
||||
self.state.is_first = false;
|
||||
|
||||
// skip spaces before getting next character, if we are allowed to.
|
||||
if self.state.can_skip_space() {
|
||||
self.skip_space::<true>()?;
|
||||
start = self.input.cur_pos();
|
||||
};
|
||||
|
||||
match self.input.cur() {
|
||||
Some(..) => {}
|
||||
// End of input.
|
||||
None => {
|
||||
if let Some(comments) = self.comments.as_mut() {
|
||||
let comments_buffer = self.comments_buffer.as_mut().unwrap();
|
||||
let last = self.state.prev_hi;
|
||||
|
||||
// move the pending to the leading or trailing
|
||||
for c in comments_buffer.take_pending_leading() {
|
||||
// if the file had no tokens and no shebang, then treat any
|
||||
// comments in the leading comments buffer as leading.
|
||||
// Otherwise treat them as trailing.
|
||||
if last == self.start_pos {
|
||||
comments_buffer.push(BufferedComment {
|
||||
kind: BufferedCommentKind::Leading,
|
||||
pos: last,
|
||||
comment: c,
|
||||
});
|
||||
} else {
|
||||
comments_buffer.push(BufferedComment {
|
||||
kind: BufferedCommentKind::Trailing,
|
||||
pos: last,
|
||||
comment: c,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// now fill the user's passed in comments
|
||||
for comment in comments_buffer.take_comments() {
|
||||
match comment.kind {
|
||||
BufferedCommentKind::Leading => {
|
||||
comments.add_leading(comment.pos, comment.comment);
|
||||
}
|
||||
BufferedCommentKind::Trailing => {
|
||||
comments.add_trailing(comment.pos, comment.comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// println!(
|
||||
// "\tContext: ({:?}) {:?}",
|
||||
// self.input.cur().unwrap(),
|
||||
// self.state.context.0
|
||||
// );
|
||||
|
||||
self.state.start = start;
|
||||
|
||||
if self.syntax.jsx() && !self.ctx.in_property_name && !self.ctx.in_type {
|
||||
//jsx
|
||||
if self.state.context.current() == Some(TokenContext::JSXExpr) {
|
||||
return self.read_jsx_token();
|
||||
}
|
||||
|
||||
let c = self.cur();
|
||||
if let Some(c) = c {
|
||||
if self.state.context.current() == Some(TokenContext::JSXOpeningTag)
|
||||
|| self.state.context.current() == Some(TokenContext::JSXClosingTag)
|
||||
{
|
||||
if c.is_ident_start() {
|
||||
return self.read_jsx_word().map(Some);
|
||||
}
|
||||
|
||||
if c == '>' {
|
||||
unsafe {
|
||||
// Safety: cur() is Some('>')
|
||||
self.input.bump();
|
||||
}
|
||||
return Ok(Some(Token::JSXTagEnd));
|
||||
}
|
||||
|
||||
if (c == '\'' || c == '"')
|
||||
&& self.state.context.current() == Some(TokenContext::JSXOpeningTag)
|
||||
{
|
||||
return self.read_jsx_str(c).map(Some);
|
||||
}
|
||||
}
|
||||
|
||||
if c == '<' && self.state.is_expr_allowed && self.input.peek() != Some('!') {
|
||||
let had_line_break_before_last = self.had_line_break_before_last();
|
||||
let cur_pos = self.input.cur_pos();
|
||||
|
||||
unsafe {
|
||||
// Safety: cur() is Some('<')
|
||||
self.input.bump();
|
||||
}
|
||||
|
||||
if had_line_break_before_last && self.is_str("<<<<<< ") {
|
||||
let span = Span::new(cur_pos, cur_pos + BytePos(7), Default::default());
|
||||
|
||||
self.emit_error_span(span, SyntaxError::TS1185);
|
||||
self.skip_line_comment(6);
|
||||
self.skip_space::<true>()?;
|
||||
return self.read_token();
|
||||
}
|
||||
|
||||
return Ok(Some(Token::JSXTagStart));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(TokenContext::Tpl {
|
||||
start: start_pos_of_tpl,
|
||||
}) = self.state.context.current()
|
||||
{
|
||||
return self.read_tmpl_token(start_pos_of_tpl).map(Some);
|
||||
}
|
||||
|
||||
self.read_token()
|
||||
})();
|
||||
|
||||
let token = match res.map_err(Token::Error).map_err(Some) {
|
||||
Ok(t) => t,
|
||||
Err(e) => e,
|
||||
};
|
||||
|
||||
let span = self.span(start);
|
||||
if let Some(ref token) = token {
|
||||
if let Some(comments) = self.comments_buffer.as_mut() {
|
||||
for comment in comments.take_pending_leading() {
|
||||
comments.push(BufferedComment {
|
||||
kind: BufferedCommentKind::Leading,
|
||||
pos: start,
|
||||
comment,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.state.update(start, token);
|
||||
self.state.prev_hi = self.last_pos();
|
||||
self.state.had_line_break_before_last = self.had_line_break_before_last();
|
||||
}
|
||||
|
||||
token.map(|token| {
|
||||
// Attach span to token.
|
||||
TokenAndSpan {
|
||||
token,
|
||||
had_line_break: self.had_line_break_before_last(),
|
||||
span,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(syntax: Syntax, start_pos: BytePos) -> Self {
|
||||
let context = TokenContexts(vec![TokenContext::BraceStmt]);
|
||||
|
||||
State {
|
||||
is_expr_allowed: true,
|
||||
next_regexp: None,
|
||||
is_first: true,
|
||||
had_line_break: false,
|
||||
had_line_break_before_last: false,
|
||||
prev_hi: start_pos,
|
||||
context,
|
||||
token_type: None,
|
||||
start: BytePos(0),
|
||||
line_start: BytePos(0),
|
||||
cur_line: 1,
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn can_skip_space(&self) -> bool {
|
||||
!self
|
||||
.context
|
||||
.current()
|
||||
.map(|t| t.preserve_space())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn can_have_trailing_line_comment(&self) -> bool {
|
||||
match self.token_type {
|
||||
Some(TokenType::BinOp(..)) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_have_trailing_comment(&self) -> bool {
|
||||
match self.token_type {
|
||||
Some(TokenType::Keyword(..)) => false,
|
||||
Some(TokenType::Semi) | Some(TokenType::LBrace) => true,
|
||||
Some(TokenType::Other {
|
||||
can_have_trailing_comment,
|
||||
..
|
||||
}) => can_have_trailing_comment,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_was_tpl_element(&self) -> bool {
|
||||
matches!(self.token_type, Some(TokenType::Template))
|
||||
}
|
||||
|
||||
fn update(&mut self, start: BytePos, next: &Token) {
|
||||
if cfg!(feature = "debug") {
|
||||
trace!(
|
||||
"updating state: next={:?}, had_line_break={} ",
|
||||
next,
|
||||
self.had_line_break
|
||||
);
|
||||
}
|
||||
|
||||
let prev = self.token_type.take();
|
||||
self.token_type = Some(TokenType::from(next));
|
||||
|
||||
self.is_expr_allowed = Self::is_expr_allowed_on_next(
|
||||
&mut self.context,
|
||||
self.syntax,
|
||||
prev,
|
||||
start,
|
||||
next,
|
||||
self.had_line_break,
|
||||
self.had_line_break_before_last,
|
||||
self.is_expr_allowed,
|
||||
);
|
||||
}
|
||||
|
||||
/// `is_expr_allowed`: previous value.
|
||||
/// `start`: start of newly produced token.
|
||||
fn is_expr_allowed_on_next(
|
||||
context: &mut TokenContexts,
|
||||
syntax: Syntax,
|
||||
prev: Option<TokenType>,
|
||||
start: BytePos,
|
||||
next: &Token,
|
||||
had_line_break: bool,
|
||||
had_line_break_before_last: bool,
|
||||
is_expr_allowed: bool,
|
||||
) -> bool {
|
||||
let is_next_keyword = matches!(*next, Word(Word::Keyword(..)));
|
||||
|
||||
if is_next_keyword && prev == Some(TokenType::Dot) {
|
||||
false
|
||||
} else {
|
||||
// ported updateContext
|
||||
match *next {
|
||||
tok!(')') | tok!('}') => {
|
||||
// TODO: Verify
|
||||
if context.len() == 1 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let out = context.pop().unwrap();
|
||||
|
||||
// let a = function(){}
|
||||
if out == TokenContext::BraceStmt
|
||||
&& matches!(
|
||||
context.current(),
|
||||
Some(TokenContext::FnExpr | TokenContext::ClassExpr)
|
||||
)
|
||||
{
|
||||
context.pop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ${} in template
|
||||
if out == TokenContext::TplQuasi {
|
||||
match context.current() {
|
||||
Some(TokenContext::Tpl { .. }) => return false,
|
||||
_ => return true,
|
||||
}
|
||||
}
|
||||
|
||||
// expression cannot follow expression
|
||||
!out.is_expr()
|
||||
}
|
||||
|
||||
tok!("function") => {
|
||||
// This is required to lex
|
||||
// `x = function(){}/42/i`
|
||||
if is_expr_allowed
|
||||
&& !context.is_brace_block(prev, had_line_break, is_expr_allowed)
|
||||
{
|
||||
context.push(TokenContext::FnExpr);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
tok!("class") => {
|
||||
if is_expr_allowed
|
||||
&& !context.is_brace_block(prev, had_line_break, is_expr_allowed)
|
||||
{
|
||||
context.push(TokenContext::ClassExpr);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
tok!(':')
|
||||
if matches!(
|
||||
context.current(),
|
||||
Some(TokenContext::FnExpr | TokenContext::ClassExpr)
|
||||
) =>
|
||||
{
|
||||
// `function`/`class` keyword is object prop
|
||||
//
|
||||
// ```JavaScript
|
||||
// { function: expr, class: expr }
|
||||
// ```
|
||||
context.pop(); // Remove FnExpr or ClassExpr
|
||||
true
|
||||
}
|
||||
|
||||
// for (a of b) {}
|
||||
tok!("of")
|
||||
if Some(TokenContext::ParenStmt { is_for_loop: true }) == context.current() =>
|
||||
{
|
||||
// e.g. for (a of _) => true
|
||||
!prev
|
||||
.expect("context.current() if ParenStmt, so prev token cannot be None")
|
||||
.before_expr()
|
||||
}
|
||||
|
||||
Word(Word::Ident(..)) => {
|
||||
// variable declaration
|
||||
match prev {
|
||||
Some(prev) => match prev {
|
||||
// handle automatic semicolon insertion.
|
||||
TokenType::Keyword(Let)
|
||||
| TokenType::Keyword(Const)
|
||||
| TokenType::Keyword(Var)
|
||||
if had_line_break_before_last =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
tok!('{') => {
|
||||
let cur = context.current();
|
||||
if syntax.jsx() && cur == Some(TokenContext::JSXOpeningTag) {
|
||||
context.push(TokenContext::BraceExpr)
|
||||
} else if syntax.jsx() && cur == Some(TokenContext::JSXExpr) {
|
||||
context.push(TokenContext::TplQuasi);
|
||||
} else {
|
||||
let next_ctxt =
|
||||
if context.is_brace_block(prev, had_line_break, is_expr_allowed) {
|
||||
TokenContext::BraceStmt
|
||||
} else {
|
||||
TokenContext::BraceExpr
|
||||
};
|
||||
context.push(next_ctxt);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
tok!('/') if syntax.jsx() && prev == Some(TokenType::JSXTagStart) => {
|
||||
context.pop();
|
||||
context.pop(); // do not consider JSX expr -> JSX open tag -> ... anymore
|
||||
context.push(TokenContext::JSXClosingTag); // reconsider as closing tag context
|
||||
false
|
||||
}
|
||||
|
||||
tok!("${") => {
|
||||
context.push(TokenContext::TplQuasi);
|
||||
true
|
||||
}
|
||||
|
||||
tok!('(') => {
|
||||
// if, for, with, while is statement
|
||||
|
||||
context.push(match prev {
|
||||
Some(TokenType::Keyword(k)) => match k {
|
||||
If | With | While => TokenContext::ParenStmt { is_for_loop: false },
|
||||
For => TokenContext::ParenStmt { is_for_loop: true },
|
||||
_ => TokenContext::ParenExpr,
|
||||
},
|
||||
_ => TokenContext::ParenExpr,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
// remains unchanged.
|
||||
tok!("++") | tok!("--") => is_expr_allowed,
|
||||
|
||||
tok!('`') => {
|
||||
// If we are in template, ` terminates template.
|
||||
if let Some(TokenContext::Tpl { .. }) = context.current() {
|
||||
context.pop();
|
||||
} else {
|
||||
context.push(TokenContext::Tpl { start });
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// tt.jsxTagStart.updateContext
|
||||
Token::JSXTagStart => {
|
||||
context.push(TokenContext::JSXExpr); // treat as beginning of JSX expression
|
||||
context.push(TokenContext::JSXOpeningTag); // start opening tag context
|
||||
false
|
||||
}
|
||||
|
||||
// tt.jsxTagEnd.updateContext
|
||||
Token::JSXTagEnd => {
|
||||
let out = context.pop();
|
||||
if (out == Some(TokenContext::JSXOpeningTag)
|
||||
&& prev == Some(TokenType::BinOp(BinOpToken::Div)))
|
||||
|| out == Some(TokenContext::JSXClosingTag)
|
||||
{
|
||||
context.pop();
|
||||
context.current() == Some(TokenContext::JSXExpr)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
_ => next.before_expr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TokenContexts(pub(crate) Vec<TokenContext>);
|
||||
|
||||
impl TokenContexts {
|
||||
/// Returns true if following `LBrace` token is `block statement` according
|
||||
/// to `ctx`, `prev`, `is_expr_allowed`.
|
||||
fn is_brace_block(
|
||||
&self,
|
||||
prev: Option<TokenType>,
|
||||
had_line_break: bool,
|
||||
is_expr_allowed: bool,
|
||||
) -> bool {
|
||||
if let Some(TokenType::Colon) = prev {
|
||||
match self.current() {
|
||||
Some(TokenContext::BraceStmt) => return true,
|
||||
// `{ a: {} }`
|
||||
// ^ ^
|
||||
Some(TokenContext::BraceExpr) => return false,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
match prev {
|
||||
// function a() {
|
||||
// return { a: "" };
|
||||
// }
|
||||
// function a() {
|
||||
// return
|
||||
// {
|
||||
// function b(){}
|
||||
// };
|
||||
// }
|
||||
Some(TokenType::Keyword(Return)) | Some(TokenType::Keyword(Yield)) => {
|
||||
return had_line_break;
|
||||
}
|
||||
|
||||
Some(TokenType::Keyword(Else))
|
||||
| Some(TokenType::Semi)
|
||||
| None
|
||||
| Some(TokenType::RParen) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If previous token was `{`
|
||||
Some(TokenType::LBrace) => {
|
||||
// https://github.com/swc-project/swc/issues/3241#issuecomment-1029584460
|
||||
// <Blah blah={function (): void {}} />
|
||||
if self.current() == Some(TokenContext::BraceExpr) {
|
||||
let len = self.len();
|
||||
if let Some(TokenContext::JSXOpeningTag) = self.0.get(len - 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return self.current() == Some(TokenContext::BraceStmt);
|
||||
}
|
||||
|
||||
// `class C<T> { ... }`
|
||||
Some(TokenType::BinOp(Lt)) | Some(TokenType::BinOp(Gt)) => return true,
|
||||
|
||||
// () => {}
|
||||
Some(TokenType::Arrow) => return true,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if had_line_break {
|
||||
if let Some(TokenType::Other {
|
||||
before_expr: false, ..
|
||||
}) = prev
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
!is_expr_allowed
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop(&mut self) -> Option<TokenContext> {
|
||||
let opt = self.0.pop();
|
||||
if cfg!(feature = "debug") {
|
||||
trace!("context.pop({:?}): {:?}", opt, self.0);
|
||||
}
|
||||
opt
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current(&self) -> Option<TokenContext> {
|
||||
self.0.last().cloned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, t: TokenContext) {
|
||||
self.0.push(t);
|
||||
|
||||
if cfg!(feature = "debug") {
|
||||
trace!("context.push({:?}): {:?}", t, self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The algorithm used to determine whether a regexp can appear at a
|
||||
/// given point in the program is loosely based on sweet.js' approach.
|
||||
/// See https://github.com/mozilla/sweet.js/wiki/design
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TokenContext {
|
||||
BraceStmt,
|
||||
BraceExpr,
|
||||
TplQuasi,
|
||||
ParenStmt {
|
||||
/// Is this `for` loop?
|
||||
is_for_loop: bool,
|
||||
},
|
||||
ParenExpr,
|
||||
Tpl {
|
||||
/// Start of a template literal.
|
||||
start: BytePos,
|
||||
},
|
||||
FnExpr,
|
||||
ClassExpr,
|
||||
JSXOpeningTag,
|
||||
JSXClosingTag,
|
||||
JSXExpr,
|
||||
}
|
||||
|
||||
impl TokenContext {
|
||||
pub(crate) const fn is_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::BraceExpr
|
||||
| Self::TplQuasi
|
||||
| Self::ParenExpr
|
||||
| Self::Tpl { .. }
|
||||
| Self::FnExpr
|
||||
| Self::ClassExpr
|
||||
| Self::JSXExpr
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) const fn preserve_space(&self) -> bool {
|
||||
match self {
|
||||
Self::Tpl { .. } | Self::JSXExpr => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn with_lexer<F, Ret>(
|
||||
syntax: Syntax,
|
||||
target: EsVersion,
|
||||
s: &str,
|
||||
f: F,
|
||||
) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(&mut Lexer<'_>) -> Result<Ret, ()>,
|
||||
{
|
||||
crate::with_test_sess(s, |_, fm| {
|
||||
let mut l = Lexer::new(syntax, target, fm, None);
|
||||
let res = f(&mut l);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let c = vec![TokenContext::BraceStmt];
|
||||
#[cfg(debug_assertions)]
|
||||
debug_assert_eq!(l.state.context.0, c);
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex(syntax: Syntax, s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(syntax, Default::default(), s, |l| Ok(l.collect())).unwrap()
|
||||
}
|
||||
|
||||
/// lex `s` within module context.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex_module_errors(syntax: Syntax, s: &'static str) -> Vec<Error> {
|
||||
with_lexer(syntax, Default::default(), s, |l| {
|
||||
l.ctx.strict = true;
|
||||
l.ctx.module = true;
|
||||
|
||||
let _: Vec<_> = l.collect();
|
||||
|
||||
Ok(l.take_errors())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex_tokens(syntax: Syntax, s: &'static str) -> Vec<Token> {
|
||||
with_lexer(syntax, Default::default(), s, |l| {
|
||||
Ok(l.map(|ts| ts.token).collect())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns `(tokens, recovered_errors)`. `(tokens)` may contain an error token
|
||||
/// if the lexer fails to recover from it.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn lex_errors(syntax: Syntax, s: &'static str) -> (Vec<Token>, Vec<Error>) {
|
||||
with_lexer(syntax, EsVersion::Es2020, s, |l| {
|
||||
let tokens = l.map(|ts| ts.token).collect();
|
||||
let errors = l.take_errors();
|
||||
Ok((tokens, errors))
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
181
third-party/vendor/swc_ecma_parser/src/lexer/table.rs
vendored
Normal file
181
third-party/vendor/swc_ecma_parser/src/lexer/table.rs
vendored
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
//! Lookup table for byte handlers.
|
||||
//!
|
||||
//! Idea is taken from ratel.
|
||||
//!
|
||||
//! https://github.com/ratel-rust/ratel-core/blob/e55a1310ba69a3f5ce2a9a6eef643feced02ac08/ratel/src/lexer/mod.rs#L665
|
||||
|
||||
use either::Either;
|
||||
use swc_common::input::Input;
|
||||
|
||||
use super::{pos_span, util::CharExt, LexResult, Lexer};
|
||||
use crate::{
|
||||
error::SyntaxError,
|
||||
token::{AssignOpToken, BinOpToken, Token},
|
||||
};
|
||||
|
||||
pub(super) type ByteHandler = Option<for<'aa> fn(&mut Lexer<'aa>) -> LexResult<Option<Token>>>;
|
||||
|
||||
/// Lookup table mapping any incoming byte to a handler function defined below.
|
||||
pub(super) static BYTE_HANDLERS: [ByteHandler; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F //
|
||||
EOF, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 0
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 1
|
||||
___, EXL, QOT, HSH, IDT, PRC, AMP, QOT, PNO, PNC, ATR, PLS, COM, MIN, PRD, SLH, // 2
|
||||
ZER, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, COL, SEM, LSS, EQL, MOR, QST, // 3
|
||||
AT_, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, // 4
|
||||
IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, BTO, IDT, BTC, CRT, IDT, // 5
|
||||
TPL, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, // 6
|
||||
IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, IDT, BEO, PIP, BEC, TLD, ERR, // 7
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 8
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 9
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // A
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // B
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // C
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // D
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // E
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // F
|
||||
];
|
||||
|
||||
const ___: ByteHandler = None;
|
||||
|
||||
const EOF: ByteHandler = Some(|lexer| {
|
||||
lexer.input.bump_bytes(1);
|
||||
|
||||
Ok(None)
|
||||
});
|
||||
|
||||
const ERR: ByteHandler = Some(|lexer| {
|
||||
let c = unsafe {
|
||||
// Safety: Byte handler is only called for non-last chracters
|
||||
lexer.input.cur().unwrap_unchecked()
|
||||
};
|
||||
|
||||
let start = lexer.cur_pos();
|
||||
unsafe {
|
||||
// Safety: Byte handler is only called for non-last chracters
|
||||
lexer.input.bump();
|
||||
}
|
||||
lexer.error_span(pos_span(start), SyntaxError::UnexpectedChar { c })?
|
||||
});
|
||||
|
||||
/// Identifier
|
||||
const IDT: ByteHandler = Some(|lexer| lexer.read_ident_or_keyword().map(Some));
|
||||
|
||||
/// `0`
|
||||
const ZER: ByteHandler = Some(|lexer| lexer.read_token_zero().map(Some));
|
||||
|
||||
/// Numbers
|
||||
const DIG: ByteHandler = Some(|lexer| {
|
||||
lexer
|
||||
.read_number(false)
|
||||
.map(|v| match v {
|
||||
Either::Left((value, raw)) => Token::Num { value, raw },
|
||||
Either::Right((value, raw)) => Token::BigInt { value, raw },
|
||||
})
|
||||
.map(Some)
|
||||
});
|
||||
|
||||
/// String literals with `'` or `"`
|
||||
const QOT: ByteHandler = Some(|lexer| lexer.read_str_lit().map(Some));
|
||||
|
||||
/// Unicode
|
||||
const UNI: ByteHandler = Some(|lexer| {
|
||||
let c = unsafe {
|
||||
// Safety: Byte handler is only called for non-last chracters
|
||||
lexer.input.cur().unwrap_unchecked()
|
||||
};
|
||||
|
||||
// Identifier or keyword. '\uXXXX' sequences are allowed in
|
||||
// identifiers, so '\' also dispatches to that.
|
||||
if c == '\\' || c.is_ident_start() {
|
||||
return lexer.read_ident_or_keyword().map(Some);
|
||||
}
|
||||
|
||||
let start = lexer.cur_pos();
|
||||
unsafe {
|
||||
// Safety: Byte handler is only called for non-last chracters
|
||||
lexer.input.bump();
|
||||
}
|
||||
lexer.error_span(pos_span(start), SyntaxError::UnexpectedChar { c })?
|
||||
});
|
||||
|
||||
/// `:`
|
||||
const COL: ByteHandler = Some(|lexer| lexer.read_token_colon().map(Some));
|
||||
|
||||
/// `%`
|
||||
const PRC: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(b'%').map(Some));
|
||||
|
||||
/// `*`
|
||||
const ATR: ByteHandler = Some(|lexer| lexer.read_token_mul_mod(b'*').map(Some));
|
||||
|
||||
/// `?`
|
||||
const QST: ByteHandler = Some(|lexer| lexer.read_token_question_mark().map(Some));
|
||||
|
||||
/// `&`
|
||||
const AMP: ByteHandler = Some(|lexer| lexer.read_token_logical(b'&').map(Some));
|
||||
|
||||
/// `|`
|
||||
const PIP: ByteHandler = Some(|lexer| lexer.read_token_logical(b'|').map(Some));
|
||||
|
||||
macro_rules! single_char {
|
||||
($name:ident, $c:literal, $token:ident) => {
|
||||
const $name: ByteHandler = Some(|lexer| {
|
||||
lexer.input.bump_bytes(1);
|
||||
Ok(Some(Token::$token))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
single_char!(SEM, b';', Semi);
|
||||
single_char!(COM, b',', Comma);
|
||||
single_char!(TPL, b'`', BackQuote);
|
||||
single_char!(TLD, b'~', Tilde);
|
||||
single_char!(AT_, b'@', At);
|
||||
|
||||
single_char!(PNO, b'(', LParen);
|
||||
single_char!(PNC, b')', RParen);
|
||||
|
||||
single_char!(BTO, b'[', LBracket);
|
||||
single_char!(BTC, b']', RBracket);
|
||||
|
||||
single_char!(BEO, b'{', LBrace);
|
||||
single_char!(BEC, b'}', RBrace);
|
||||
|
||||
/// `^`
|
||||
const CRT: ByteHandler = Some(|lexer| {
|
||||
// Bitwise xor
|
||||
lexer.input.bump_bytes(1);
|
||||
Ok(Some(if lexer.input.cur_as_ascii() == Some(b'=') {
|
||||
lexer.input.bump_bytes(1);
|
||||
Token::AssignOp(AssignOpToken::BitXorAssign)
|
||||
} else {
|
||||
Token::BinOp(BinOpToken::BitXor)
|
||||
}))
|
||||
});
|
||||
|
||||
/// `+`
|
||||
const PLS: ByteHandler = Some(|lexer| lexer.read_token_plus_minus(b'+'));
|
||||
|
||||
/// `-`
|
||||
const MIN: ByteHandler = Some(|lexer| lexer.read_token_plus_minus(b'-'));
|
||||
|
||||
/// `!`
|
||||
const EXL: ByteHandler = Some(|lexer| lexer.read_token_bang_or_eq(b'!'));
|
||||
|
||||
/// `=`
|
||||
const EQL: ByteHandler = Some(|lexer| lexer.read_token_bang_or_eq(b'='));
|
||||
|
||||
/// `.`
|
||||
const PRD: ByteHandler = Some(|lexer| lexer.read_token_dot().map(Some));
|
||||
|
||||
/// `<`
|
||||
const LSS: ByteHandler = Some(|lexer| lexer.read_token_lt_gt());
|
||||
|
||||
/// `>`
|
||||
const MOR: ByteHandler = Some(|lexer| lexer.read_token_lt_gt());
|
||||
|
||||
/// `/`
|
||||
const SLH: ByteHandler = Some(|lexer| lexer.read_slash());
|
||||
|
||||
/// `#`
|
||||
const HSH: ByteHandler = Some(|lexer| lexer.read_token_number_sign());
|
||||
2154
third-party/vendor/swc_ecma_parser/src/lexer/tests.rs
vendored
Normal file
2154
third-party/vendor/swc_ecma_parser/src/lexer/tests.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
430
third-party/vendor/swc_ecma_parser/src/lexer/util.rs
vendored
Normal file
430
third-party/vendor/swc_ecma_parser/src/lexer/util.rs
vendored
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
//! Ported from [babylon/util/identifier.js][]
|
||||
//!
|
||||
//!
|
||||
//! [babylon/util/identifier.js]:https://github.com/babel/babel/blob/master/packages/babylon/src/util/identifier.js
|
||||
use std::char;
|
||||
|
||||
use smartstring::{LazyCompact, SmartString};
|
||||
use swc_common::{
|
||||
comments::{Comment, CommentKind},
|
||||
BytePos, Span, SyntaxContext,
|
||||
};
|
||||
use swc_ecma_ast::Ident;
|
||||
use tracing::warn;
|
||||
|
||||
use super::{
|
||||
comments_buffer::BufferedComment, input::Input, whitespace::SkipWhitespace, Char, LexResult,
|
||||
Lexer,
|
||||
};
|
||||
use crate::{
|
||||
error::{Error, SyntaxError},
|
||||
lexer::comments_buffer::BufferedCommentKind,
|
||||
Tokens,
|
||||
};
|
||||
|
||||
/// Collector for raw string.
|
||||
///
|
||||
/// Methods of this struct is noop if the value is [None].
|
||||
pub(super) struct Raw(pub Option<SmartString<LazyCompact>>);
|
||||
|
||||
impl Raw {
|
||||
#[inline]
|
||||
pub fn push_str(&mut self, s: &str) {
|
||||
if let Some(ref mut st) = self.0 {
|
||||
st.push_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, c: char) {
|
||||
if let Some(ref mut st) = self.0 {
|
||||
st.push(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub const BACKSPACE: char = 8 as char;
|
||||
// pub const SHIFT_OUT: char = 14 as char;
|
||||
// pub const OGHAM_SPACE_MARK: char = '\u{1680}'; // ' '
|
||||
// pub const LINE_FEED: char = '\n';
|
||||
// pub const LINE_SEPARATOR: char = '\u{2028}';
|
||||
// pub const PARAGRAPH_SEPARATOR: char = '\u{2029}';
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub(super) fn span(&self, start: BytePos) -> Span {
|
||||
let end = self.last_pos();
|
||||
if cfg!(debug_assertions) && start > end {
|
||||
unreachable!(
|
||||
"assertion failed: (span.start <= span.end).
|
||||
start = {}, end = {}",
|
||||
start.0, end.0
|
||||
)
|
||||
}
|
||||
Span {
|
||||
lo: start,
|
||||
hi: end,
|
||||
ctxt: SyntaxContext::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn bump(&mut self) {
|
||||
unsafe {
|
||||
// Safety: Actually this is not safe but this is an internal method.
|
||||
self.input.bump()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn is(&mut self, c: u8) -> bool {
|
||||
self.input.is_byte(c)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn is_str(&self, s: &str) -> bool {
|
||||
self.input.is_str(s)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn eat(&mut self, c: u8) -> bool {
|
||||
self.input.eat_byte(c)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn cur(&mut self) -> Option<char> {
|
||||
self.input.cur()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn peek(&mut self) -> Option<char> {
|
||||
self.input.peek()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn peek_ahead(&mut self) -> Option<char> {
|
||||
self.input.peek_ahead()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn cur_pos(&mut self) -> BytePos {
|
||||
self.input.cur_pos()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn last_pos(&self) -> BytePos {
|
||||
self.input.last_pos()
|
||||
}
|
||||
|
||||
/// Shorthand for `let span = self.span(start); self.error_span(span)`
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn error<T>(&mut self, start: BytePos, kind: SyntaxError) -> LexResult<T> {
|
||||
let span = self.span(start);
|
||||
self.error_span(Span::new(span.lo, span.hi, span.ctxt), kind)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn error_span<T>(&mut self, span: Span, kind: SyntaxError) -> LexResult<T> {
|
||||
Err(Error::new(span, kind))
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_error(&mut self, start: BytePos, kind: SyntaxError) {
|
||||
let span = self.span(start);
|
||||
self.emit_error_span(Span::new(span.lo, span.hi, span.ctxt), kind)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_error_span(&mut self, span: Span, kind: SyntaxError) {
|
||||
if self.ctx.ignore_error {
|
||||
return;
|
||||
}
|
||||
|
||||
warn!("Lexer error at {:?}", span);
|
||||
let err = Error::new(span, kind);
|
||||
self.errors.borrow_mut().push(err);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_strict_mode_error(&mut self, start: BytePos, kind: SyntaxError) {
|
||||
let span = self.span(start);
|
||||
self.emit_strict_mode_error_span(Span::new(span.lo, span.hi, span.ctxt), kind)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_strict_mode_error_span(&mut self, span: Span, kind: SyntaxError) {
|
||||
if self.ctx.strict {
|
||||
self.emit_error_span(span, kind);
|
||||
return;
|
||||
}
|
||||
|
||||
let err = Error::new(span, kind);
|
||||
|
||||
self.add_module_mode_error(err);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_module_mode_error(&mut self, start: BytePos, kind: SyntaxError) {
|
||||
let span = self.span(start);
|
||||
self.emit_module_mode_error_span(Span::new(span.lo, span.hi, span.ctxt), kind)
|
||||
}
|
||||
|
||||
/// Some codes are valid in a strict mode script but invalid in module
|
||||
/// code.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub(super) fn emit_module_mode_error_span(&mut self, span: Span, kind: SyntaxError) {
|
||||
let err = Error::new(span, kind);
|
||||
|
||||
self.add_module_mode_error(err);
|
||||
}
|
||||
|
||||
/// Skip comments or whitespaces.
|
||||
///
|
||||
/// See https://tc39.github.io/ecma262/#sec-white-space
|
||||
pub(super) fn skip_space<const LEX_COMMENTS: bool>(&mut self) -> LexResult<()> {
|
||||
loop {
|
||||
let (offset, newline) = {
|
||||
let mut skip = SkipWhitespace {
|
||||
input: self.input.as_str(),
|
||||
newline: false,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
skip.scan();
|
||||
|
||||
(skip.offset, skip.newline)
|
||||
};
|
||||
|
||||
self.input.bump_bytes(offset);
|
||||
self.state.had_line_break |= newline;
|
||||
|
||||
if LEX_COMMENTS && self.input.is_byte(b'/') {
|
||||
if self.peek() == Some('/') {
|
||||
self.skip_line_comment(2);
|
||||
continue;
|
||||
} else if self.peek() == Some('*') {
|
||||
self.skip_block_comment()?;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub(super) fn skip_line_comment(&mut self, start_skip: usize) {
|
||||
let start = self.cur_pos();
|
||||
self.input.bump_bytes(start_skip);
|
||||
let slice_start = self.cur_pos();
|
||||
|
||||
// foo // comment for foo
|
||||
// bar
|
||||
//
|
||||
// foo
|
||||
// // comment for bar
|
||||
// bar
|
||||
//
|
||||
let is_for_next = self.state.had_line_break || !self.state.can_have_trailing_line_comment();
|
||||
|
||||
let idx = self
|
||||
.input
|
||||
.as_str()
|
||||
.find(['\r', '\n', '\u{2028}', '\u{2029}'])
|
||||
.map_or(self.input.as_str().len(), |v| {
|
||||
self.state.had_line_break = true;
|
||||
v
|
||||
});
|
||||
|
||||
self.input.bump_bytes(idx);
|
||||
let end = self.cur_pos();
|
||||
|
||||
if let Some(comments) = self.comments_buffer.as_mut() {
|
||||
let s = unsafe {
|
||||
// Safety: We know that the start and the end are valid
|
||||
self.input.slice(slice_start, end)
|
||||
};
|
||||
let cmt = Comment {
|
||||
kind: CommentKind::Line,
|
||||
span: Span::new(start, end, SyntaxContext::empty()),
|
||||
text: self.atoms.borrow_mut().intern(s),
|
||||
};
|
||||
|
||||
if is_for_next {
|
||||
comments.push_pending_leading(cmt);
|
||||
} else {
|
||||
comments.push(BufferedComment {
|
||||
kind: BufferedCommentKind::Trailing,
|
||||
pos: self.state.prev_hi,
|
||||
comment: cmt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Safety: We got end from self.input
|
||||
self.input.reset_to(end);
|
||||
}
|
||||
}
|
||||
|
||||
/// Expects current char to be '/' and next char to be '*'.
|
||||
#[inline(never)]
|
||||
pub(super) fn skip_block_comment(&mut self) -> LexResult<()> {
|
||||
let start = self.cur_pos();
|
||||
|
||||
debug_assert_eq!(self.cur(), Some('/'));
|
||||
debug_assert_eq!(self.peek(), Some('*'));
|
||||
|
||||
self.input.bump_bytes(2);
|
||||
|
||||
// jsdoc
|
||||
let slice_start = self.cur_pos();
|
||||
let mut was_star = if self.input.is_byte(b'*') {
|
||||
self.bump();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut is_for_next = self.state.had_line_break || !self.state.can_have_trailing_comment();
|
||||
|
||||
while let Some(c) = self.cur() {
|
||||
if was_star && c == '/' {
|
||||
debug_assert_eq!(self.cur(), Some('/'));
|
||||
self.bump(); // '/'
|
||||
|
||||
let end = self.cur_pos();
|
||||
|
||||
self.skip_space::<false>()?;
|
||||
|
||||
if self.input.is_byte(b';') {
|
||||
is_for_next = false;
|
||||
}
|
||||
|
||||
if let Some(comments) = self.comments_buffer.as_mut() {
|
||||
let src = unsafe {
|
||||
// Safety: We got slice_start and end from self.input so those are valid.
|
||||
self.input.slice(slice_start, end)
|
||||
};
|
||||
let s = &src[..src.len() - 2];
|
||||
let cmt = Comment {
|
||||
kind: CommentKind::Block,
|
||||
span: Span::new(start, end, SyntaxContext::empty()),
|
||||
text: self.atoms.borrow_mut().intern(s),
|
||||
};
|
||||
|
||||
let _ = self.input.peek();
|
||||
if is_for_next {
|
||||
comments.push_pending_leading(cmt);
|
||||
} else {
|
||||
comments.push(BufferedComment {
|
||||
kind: BufferedCommentKind::Trailing,
|
||||
pos: self.state.prev_hi,
|
||||
comment: cmt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
if c.is_line_terminator() {
|
||||
self.state.had_line_break = true;
|
||||
}
|
||||
|
||||
was_star = c == '*';
|
||||
self.bump();
|
||||
}
|
||||
|
||||
self.error(start, SyntaxError::UnterminatedBlockComment)?
|
||||
}
|
||||
}
|
||||
|
||||
/// Implemented for `char`.
|
||||
pub trait CharExt: Copy {
|
||||
fn to_char(self) -> Option<char>;
|
||||
|
||||
/// Test whether a given character code starts an identifier.
|
||||
///
|
||||
/// https://tc39.github.io/ecma262/#prod-IdentifierStart
|
||||
#[inline]
|
||||
fn is_ident_start(self) -> bool {
|
||||
let c = match self.to_char() {
|
||||
Some(c) => c,
|
||||
None => return false,
|
||||
};
|
||||
Ident::is_valid_start(c)
|
||||
}
|
||||
|
||||
/// Test whether a given character is part of an identifier.
|
||||
#[inline]
|
||||
fn is_ident_part(self) -> bool {
|
||||
let c = match self.to_char() {
|
||||
Some(c) => c,
|
||||
None => return false,
|
||||
};
|
||||
Ident::is_valid_continue(c)
|
||||
}
|
||||
|
||||
/// See https://tc39.github.io/ecma262/#sec-line-terminators
|
||||
#[inline]
|
||||
fn is_line_terminator(self) -> bool {
|
||||
let c = match self.to_char() {
|
||||
Some(c) => c,
|
||||
None => return false,
|
||||
};
|
||||
matches!(c, '\r' | '\n' | '\u{2028}' | '\u{2029}')
|
||||
}
|
||||
|
||||
/// See https://tc39.github.io/ecma262/#sec-literals-string-literals
|
||||
#[inline]
|
||||
fn is_line_break(self) -> bool {
|
||||
let c = match self.to_char() {
|
||||
Some(c) => c,
|
||||
None => return false,
|
||||
};
|
||||
matches!(c, '\r' | '\n')
|
||||
}
|
||||
|
||||
/// See https://tc39.github.io/ecma262/#sec-white-space
|
||||
#[inline]
|
||||
fn is_ws(self) -> bool {
|
||||
let c = match self.to_char() {
|
||||
Some(c) => c,
|
||||
None => return false,
|
||||
};
|
||||
match c {
|
||||
'\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}' | '\u{feff}' => true,
|
||||
_ => {
|
||||
if self.is_line_terminator() {
|
||||
// NOTE: Line terminator is not whitespace.
|
||||
false
|
||||
} else {
|
||||
c.is_whitespace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CharExt for Char {
|
||||
#[inline(always)]
|
||||
fn to_char(self) -> Option<char> {
|
||||
char::from_u32(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CharExt for char {
|
||||
#[inline(always)]
|
||||
fn to_char(self) -> Option<char> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
100
third-party/vendor/swc_ecma_parser/src/lexer/whitespace.rs
vendored
Normal file
100
third-party/vendor/swc_ecma_parser/src/lexer/whitespace.rs
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/// Returns true if it's done
|
||||
pub(super) type ByteHandler = Option<for<'aa> fn(&mut SkipWhitespace<'aa>) -> usize>;
|
||||
|
||||
/// Lookup table for whitespace
|
||||
static BYTE_HANDLERS: [ByteHandler; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F //
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, SPC, NLN, SPC, SPC, NLN, ___, ___, // 0
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 1
|
||||
SPC, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 2
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 3
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 4
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 5
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 6
|
||||
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 7
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 8
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 9
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // A
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // B
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // C
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // D
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // E
|
||||
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // F
|
||||
];
|
||||
|
||||
/// Stop
|
||||
const ___: ByteHandler = None;
|
||||
|
||||
/// Newline
|
||||
const NLN: ByteHandler = Some(|skip| {
|
||||
skip.newline = true;
|
||||
|
||||
1
|
||||
});
|
||||
|
||||
/// Space
|
||||
const SPC: ByteHandler = Some(|_| 1);
|
||||
|
||||
/// Unicode
|
||||
const UNI: ByteHandler = Some(|skip| {
|
||||
let s = unsafe {
|
||||
// Safety: `skip.offset` is always valid
|
||||
skip.input.get_unchecked(skip.offset..)
|
||||
};
|
||||
|
||||
let c = unsafe {
|
||||
// Safety: Byte handlers are called only when `skip.input` is not empty
|
||||
s.chars().next().unwrap_unchecked()
|
||||
};
|
||||
|
||||
match c {
|
||||
// white spaces
|
||||
'\u{feff}' => {}
|
||||
// line breaks
|
||||
'\u{2028}' | '\u{2029}' => {
|
||||
skip.newline = true;
|
||||
}
|
||||
|
||||
_ if c.is_whitespace() => {}
|
||||
|
||||
_ => return 0,
|
||||
}
|
||||
|
||||
c.len_utf8()
|
||||
});
|
||||
|
||||
/// API is taked from oxc by Boshen (https://github.com/Boshen/oxc/pull/26)
|
||||
pub(super) struct SkipWhitespace<'a> {
|
||||
pub input: &'a str,
|
||||
|
||||
/// Total offset
|
||||
pub offset: usize,
|
||||
|
||||
/// Found newline
|
||||
pub newline: bool,
|
||||
}
|
||||
|
||||
impl SkipWhitespace<'_> {
|
||||
#[inline(always)]
|
||||
pub fn scan(&mut self) {
|
||||
let mut byte;
|
||||
loop {
|
||||
byte = match self.input.as_bytes().get(self.offset).copied() {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let handler = unsafe { *(&BYTE_HANDLERS as *const ByteHandler).offset(byte as isize) };
|
||||
|
||||
if let Some(handler) = handler {
|
||||
let delta = handler(self);
|
||||
if delta == 0 {
|
||||
return;
|
||||
}
|
||||
self.offset += delta;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
508
third-party/vendor/swc_ecma_parser/src/lib.rs
vendored
Normal file
508
third-party/vendor/swc_ecma_parser/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
//! EcmaScript/TypeScript parser for the rust programming language.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! ## Heavily tested
|
||||
//!
|
||||
//! Passes almost all tests from [tc39/test262][].
|
||||
//!
|
||||
//! ## Error reporting
|
||||
//!
|
||||
//! ```sh
|
||||
//! error: 'implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', or 'yield' cannot be used as an identifier in strict mode
|
||||
//! --> invalid.js:3:10
|
||||
//! |
|
||||
//! 3 | function yield() {
|
||||
//! | ^^^^^
|
||||
//! ```
|
||||
//!
|
||||
//! ## Error recovery
|
||||
//!
|
||||
//! The parser can recover from some parsing errors. For example, parser returns
|
||||
//! `Ok(Module)` for the code below, while emitting error to handler.
|
||||
//!
|
||||
//! ```ts
|
||||
//! const CONST = 9000 % 2;
|
||||
//! const enum D {
|
||||
//! // Comma is required, but parser can recover because of the newline.
|
||||
//! d = 10
|
||||
//! g = CONST
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Example (lexer)
|
||||
//!
|
||||
//! See `lexer.rs` in examples directory.
|
||||
//!
|
||||
//! # Example (parser)
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate swc_common;
|
||||
//! extern crate swc_ecma_parser;
|
||||
//! use swc_common::sync::Lrc;
|
||||
//! use swc_common::{
|
||||
//! errors::{ColorConfig, Handler},
|
||||
//! FileName, FilePathMapping, SourceMap,
|
||||
//! };
|
||||
//! use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let cm: Lrc<SourceMap> = Default::default();
|
||||
//! let handler =
|
||||
//! Handler::with_tty_emitter(ColorConfig::Auto, true, false,
|
||||
//! Some(cm.clone()));
|
||||
//!
|
||||
//! // Real usage
|
||||
//! // let fm = cm
|
||||
//! // .load_file(Path::new("test.js"))
|
||||
//! // .expect("failed to load test.js");
|
||||
//! let fm = cm.new_source_file(
|
||||
//! FileName::Custom("test.js".into()),
|
||||
//! "function foo() {}".into(),
|
||||
//! );
|
||||
//! let lexer = Lexer::new(
|
||||
//! // We want to parse ecmascript
|
||||
//! Syntax::Es(Default::default()),
|
||||
//! // EsVersion defaults to es5
|
||||
//! Default::default(),
|
||||
//! StringInput::from(&*fm),
|
||||
//! None,
|
||||
//! );
|
||||
//!
|
||||
//! let mut parser = Parser::new_from(lexer);
|
||||
//!
|
||||
//! for e in parser.take_errors() {
|
||||
//! e.into_diagnostic(&handler).emit();
|
||||
//! }
|
||||
//!
|
||||
//! let _module = parser
|
||||
//! .parse_module()
|
||||
//! .map_err(|mut e| {
|
||||
//! // Unrecoverable fatal error occurred
|
||||
//! e.into_diagnostic(&handler).emit()
|
||||
//! })
|
||||
//! .expect("failed to parser module");
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Cargo features
|
||||
//!
|
||||
//! ### `typescript`
|
||||
//!
|
||||
//! Enables typescript parser.
|
||||
//!
|
||||
//! ### `verify`
|
||||
//!
|
||||
//! Verify more errors, using `swc_ecma_visit`.
|
||||
//!
|
||||
//! ## Known issues
|
||||
//!
|
||||
//! ### Null character after `\`
|
||||
//!
|
||||
//! Because [String] of rust should only contain valid utf-8 characters while
|
||||
//! javascript allows non-utf8 characters, the parser stores invalid utf8
|
||||
//! characters in escaped form.
|
||||
//!
|
||||
//! As a result, swc needs a way to distinguish invalid-utf8 code points and
|
||||
//! input specified by the user. The parser stores a null character right after
|
||||
//! `\\` for non-utf8 code points. Note that other parts of swc is aware of this
|
||||
//! fact.
|
||||
//!
|
||||
//! Note that this can be changed at anytime with a breaking change.
|
||||
//!
|
||||
//! [tc39/test262]:https://github.com/tc39/test262
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(unused)]
|
||||
#![allow(clippy::nonminimal_bool)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::unnecessary_unwrap)]
|
||||
#![allow(clippy::vec_box)]
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
#![allow(clippy::match_like_matches_macro)]
|
||||
|
||||
use error::Error;
|
||||
use lexer::Lexer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use swc_common::{comments::Comments, input::SourceFileInput, SourceFile};
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
pub use self::{
|
||||
lexer::input::{Input, StringInput},
|
||||
parser::*,
|
||||
};
|
||||
#[deprecated(note = "Use `EsVersion` instead")]
|
||||
pub type JscTarget = EsVersion;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod error;
|
||||
pub mod lexer;
|
||||
mod parser;
|
||||
pub mod token;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields, tag = "syntax")]
|
||||
pub enum Syntax {
|
||||
/// Standard
|
||||
#[serde(rename = "ecmascript")]
|
||||
Es(EsConfig),
|
||||
/// This variant requires the cargo feature `typescript` to be enabled.
|
||||
#[cfg(feature = "typescript")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "typescript")))]
|
||||
#[serde(rename = "typescript")]
|
||||
Typescript(TsConfig),
|
||||
}
|
||||
|
||||
impl Default for Syntax {
|
||||
fn default() -> Self {
|
||||
Syntax::Es(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Syntax {
|
||||
fn auto_accessors(self) -> bool {
|
||||
match self {
|
||||
Syntax::Es(EsConfig {
|
||||
auto_accessors: true,
|
||||
..
|
||||
}) => true,
|
||||
Syntax::Typescript(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_attributes(self) -> bool {
|
||||
match self {
|
||||
Syntax::Es(EsConfig {
|
||||
import_attributes, ..
|
||||
}) => import_attributes,
|
||||
Syntax::Typescript(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Should we parse jsx?
|
||||
pub fn jsx(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Syntax::Es(EsConfig { jsx: true, .. }) | Syntax::Typescript(TsConfig { tsx: true, .. })
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fn_bind(self) -> bool {
|
||||
matches!(self, Syntax::Es(EsConfig { fn_bind: true, .. }))
|
||||
}
|
||||
|
||||
pub fn decorators(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Syntax::Es(EsConfig {
|
||||
decorators: true,
|
||||
..
|
||||
}) | Syntax::Typescript(TsConfig {
|
||||
decorators: true,
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
pub fn decorators_before_export(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Syntax::Es(EsConfig {
|
||||
decorators_before_export: true,
|
||||
..
|
||||
}) | Syntax::Typescript(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Should we parse typescript?
|
||||
#[cfg(not(feature = "typescript"))]
|
||||
pub const fn typescript(self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Should we parse typescript?
|
||||
#[cfg(feature = "typescript")]
|
||||
pub const fn typescript(self) -> bool {
|
||||
matches!(self, Syntax::Typescript(..))
|
||||
}
|
||||
|
||||
pub fn export_default_from(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Syntax::Es(EsConfig {
|
||||
export_default_from: true,
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dts(self) -> bool {
|
||||
match self {
|
||||
Syntax::Typescript(t) => t.dts,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn allow_super_outside_method(self) -> bool {
|
||||
match self {
|
||||
Syntax::Es(EsConfig {
|
||||
allow_super_outside_method,
|
||||
..
|
||||
}) => allow_super_outside_method,
|
||||
Syntax::Typescript(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn allow_return_outside_function(self) -> bool {
|
||||
match self {
|
||||
Syntax::Es(EsConfig {
|
||||
allow_return_outside_function,
|
||||
..
|
||||
}) => allow_return_outside_function,
|
||||
Syntax::Typescript(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn early_errors(self) -> bool {
|
||||
match self {
|
||||
Syntax::Typescript(t) => !t.no_early_errors,
|
||||
Syntax::Es(..) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn disallow_ambiguous_jsx_like(self) -> bool {
|
||||
match self {
|
||||
Syntax::Typescript(t) => t.disallow_ambiguous_jsx_like,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explicit_resource_management(&self) -> bool {
|
||||
match self {
|
||||
Syntax::Es(EsConfig {
|
||||
explicit_resource_management: using_decl,
|
||||
..
|
||||
}) => *using_decl,
|
||||
Syntax::Typescript(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TsConfig {
|
||||
#[serde(default)]
|
||||
pub tsx: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub decorators: bool,
|
||||
|
||||
/// `.d.ts`
|
||||
#[serde(skip, default)]
|
||||
pub dts: bool,
|
||||
|
||||
#[serde(skip, default)]
|
||||
pub no_early_errors: bool,
|
||||
|
||||
/// babel: `disallowAmbiguousJSXLike`
|
||||
/// Even when JSX parsing is not enabled, this option disallows using syntax
|
||||
/// that would be ambiguous with JSX (`<X> y` type assertions and
|
||||
/// `<X>()=>{}` type arguments)
|
||||
/// see: https://babeljs.io/docs/en/babel-plugin-transform-typescript#disallowambiguousjsxlike
|
||||
#[serde(skip, default)]
|
||||
pub disallow_ambiguous_jsx_like: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EsConfig {
|
||||
#[serde(default)]
|
||||
pub jsx: bool,
|
||||
|
||||
/// Support function bind expression.
|
||||
#[serde(rename = "functionBind")]
|
||||
#[serde(default)]
|
||||
pub fn_bind: bool,
|
||||
|
||||
/// Enable decorators.
|
||||
#[serde(default)]
|
||||
pub decorators: bool,
|
||||
|
||||
/// babel: `decorators.decoratorsBeforeExport`
|
||||
///
|
||||
/// Effective only if `decorator` is true.
|
||||
#[serde(rename = "decoratorsBeforeExport")]
|
||||
#[serde(default)]
|
||||
pub decorators_before_export: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub export_default_from: bool,
|
||||
|
||||
/// Stage 3.
|
||||
#[serde(default, alias = "importAssertions")]
|
||||
pub import_attributes: bool,
|
||||
|
||||
#[serde(default, rename = "allowSuperOutsideMethod")]
|
||||
pub allow_super_outside_method: bool,
|
||||
|
||||
#[serde(default, rename = "allowReturnOutsideFunction")]
|
||||
pub allow_return_outside_function: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub auto_accessors: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub explicit_resource_management: bool,
|
||||
}
|
||||
|
||||
/// Syntactic context.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Context {
|
||||
/// `true` while backtracking
|
||||
ignore_error: bool,
|
||||
|
||||
/// Is in module code?
|
||||
module: bool,
|
||||
can_be_module: bool,
|
||||
strict: bool,
|
||||
|
||||
expr_ctx: ExpressionContext,
|
||||
|
||||
include_in_expr: bool,
|
||||
/// If true, await expression is parsed, and "await" is treated as a
|
||||
/// keyword.
|
||||
in_async: bool,
|
||||
/// If true, yield expression is parsed, and "yield" is treated as a
|
||||
/// keyword.
|
||||
in_generator: bool,
|
||||
|
||||
is_continue_allowed: bool,
|
||||
is_break_allowed: bool,
|
||||
|
||||
in_type: bool,
|
||||
/// Typescript extension.
|
||||
should_not_lex_lt_or_gt_as_type: bool,
|
||||
/// Typescript extension.
|
||||
in_declare: bool,
|
||||
|
||||
/// If true, `:` should not be treated as a type annotation.
|
||||
in_cond_expr: bool,
|
||||
will_expect_colon_for_cond: bool,
|
||||
|
||||
in_class: bool,
|
||||
|
||||
in_class_field: bool,
|
||||
|
||||
in_function: bool,
|
||||
|
||||
/// This indicates current scope or the scope out of arrow function is
|
||||
/// function declaration or function expression or not.
|
||||
inside_non_arrow_function_scope: bool,
|
||||
|
||||
in_parameters: bool,
|
||||
|
||||
has_super_class: bool,
|
||||
|
||||
in_property_name: bool,
|
||||
|
||||
in_forced_jsx_context: bool,
|
||||
|
||||
// If true, allow super.x and super[x]
|
||||
allow_direct_super: bool,
|
||||
|
||||
ignore_else_clause: bool,
|
||||
|
||||
disallow_conditional_types: bool,
|
||||
|
||||
allow_using_decl: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
struct ExpressionContext {
|
||||
// TODO:
|
||||
// - include_in
|
||||
for_loop_init: bool,
|
||||
for_await_loop_init: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn with_test_sess<F, Ret>(src: &str, f: F) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(&swc_common::errors::Handler, StringInput<'_>) -> Result<Ret, ()>,
|
||||
{
|
||||
use swc_common::FileName;
|
||||
|
||||
::testing::run_test(false, |cm, handler| {
|
||||
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
|
||||
|
||||
f(handler, (&*fm).into())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_file_parser<T>(
|
||||
fm: &SourceFile,
|
||||
syntax: Syntax,
|
||||
target: EsVersion,
|
||||
comments: Option<&dyn Comments>,
|
||||
recovered_errors: &mut Vec<Error>,
|
||||
op: impl for<'aa> FnOnce(&mut Parser<Lexer>) -> PResult<T>,
|
||||
) -> PResult<T> {
|
||||
let lexer = Lexer::new(syntax, target, SourceFileInput::from(fm), comments);
|
||||
let mut p = Parser::new_from(lexer);
|
||||
let ret = op(&mut p);
|
||||
|
||||
recovered_errors.append(&mut p.take_errors());
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
macro_rules! expose {
|
||||
(
|
||||
$name:ident,
|
||||
$T:ty,
|
||||
$($t:tt)*
|
||||
) => {
|
||||
/// Note: This is recommended way to parse a file.
|
||||
///
|
||||
/// This is an alias for [Parser], [Lexer] and [SourceFileInput], but
|
||||
/// instantiation of generics occur in `swc_ecma_parser` crate.
|
||||
pub fn $name(
|
||||
fm: &SourceFile,
|
||||
syntax: Syntax,
|
||||
target: EsVersion,
|
||||
comments: Option<&dyn Comments>,
|
||||
recovered_errors: &mut Vec<Error>,
|
||||
) -> PResult<$T> {
|
||||
with_file_parser(fm, syntax, target, comments, recovered_errors, $($t)*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
expose!(parse_file_as_expr, Box<Expr>, |p| {
|
||||
// This allow to parse `import.meta`
|
||||
p.input().ctx.can_be_module = true;
|
||||
p.parse_expr()
|
||||
});
|
||||
expose!(parse_file_as_module, Module, |p| { p.parse_module() });
|
||||
expose!(parse_file_as_script, Script, |p| { p.parse_script() });
|
||||
expose!(parse_file_as_program, Program, |p| { p.parse_program() });
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(any(target_arch = "wasm32", target_arch = "arm", not(feature = "stacker")))]
|
||||
fn maybe_grow<R, F: FnOnce() -> R>(_red_zone: usize, _stack_size: usize, callback: F) -> R {
|
||||
callback()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(all(
|
||||
not(any(target_arch = "wasm32", target_arch = "arm")),
|
||||
feature = "stacker"
|
||||
))]
|
||||
fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callback: F) -> R {
|
||||
stacker::maybe_grow(red_zone, stack_size, callback)
|
||||
}
|
||||
435
third-party/vendor/swc_ecma_parser/src/macros.rs
vendored
Normal file
435
third-party/vendor/swc_ecma_parser/src/macros.rs
vendored
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
#[allow(unused)]
|
||||
macro_rules! tok {
|
||||
('`') => {
|
||||
crate::token::Token::BackQuote
|
||||
};
|
||||
// (';') => { Token::Semi };
|
||||
('@') => {
|
||||
crate::token::Token::At
|
||||
};
|
||||
('#') => {
|
||||
crate::token::Token::Hash
|
||||
};
|
||||
|
||||
('&') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::BitAnd)
|
||||
};
|
||||
('|') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::BitOr)
|
||||
};
|
||||
('^') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::BitXor)
|
||||
};
|
||||
('+') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Add)
|
||||
};
|
||||
('-') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Sub)
|
||||
};
|
||||
("??") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::NullishCoalescing)
|
||||
};
|
||||
('~') => {
|
||||
crate::token::Token::Tilde
|
||||
};
|
||||
('!') => {
|
||||
crate::token::Token::Bang
|
||||
};
|
||||
("&&") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::LogicalAnd)
|
||||
};
|
||||
("||") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::LogicalOr)
|
||||
};
|
||||
("&&=") => {
|
||||
crate::token::Token::AssignOp(crate::token::AssignOpToken::AndAssign)
|
||||
};
|
||||
("||=") => {
|
||||
crate::token::Token::AssignOp(crate::token::AssignOpToken::OrAssign)
|
||||
};
|
||||
("??=") => {
|
||||
crate::token::Token::AssignOp(crate::token::AssignOpToken::NullishAssign)
|
||||
};
|
||||
|
||||
("==") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::EqEq)
|
||||
};
|
||||
("===") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::EqEqEq)
|
||||
};
|
||||
("!=") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::NotEq)
|
||||
};
|
||||
("!==") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::NotEqEq)
|
||||
};
|
||||
|
||||
(',') => {
|
||||
crate::token::Token::Comma
|
||||
};
|
||||
('?') => {
|
||||
crate::token::Token::QuestionMark
|
||||
};
|
||||
(':') => {
|
||||
crate::token::Token::Colon
|
||||
};
|
||||
('.') => {
|
||||
crate::token::Token::Dot
|
||||
};
|
||||
("=>") => {
|
||||
crate::token::Token::Arrow
|
||||
};
|
||||
("...") => {
|
||||
crate::token::Token::DotDotDot
|
||||
};
|
||||
("${") => {
|
||||
crate::token::Token::DollarLBrace
|
||||
};
|
||||
|
||||
('+') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Add)
|
||||
};
|
||||
('-') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Sub)
|
||||
};
|
||||
('*') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Mul)
|
||||
};
|
||||
('/') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Div)
|
||||
};
|
||||
("/=") => {
|
||||
crate::token::Token::AssignOp(crate::token::AssignOpToken::DivAssign)
|
||||
};
|
||||
('%') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Mod)
|
||||
};
|
||||
('~') => {
|
||||
crate::token::Token::Tilde
|
||||
};
|
||||
('<') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Lt)
|
||||
};
|
||||
('>') => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::Gt)
|
||||
};
|
||||
(">>") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::RShift)
|
||||
};
|
||||
(">=") => {
|
||||
crate::token::Token::BinOp(crate::token::BinOpToken::GtEq)
|
||||
};
|
||||
|
||||
("++") => {
|
||||
crate::token::Token::PlusPlus
|
||||
};
|
||||
("--") => {
|
||||
crate::token::Token::MinusMinus
|
||||
};
|
||||
|
||||
('=') => {
|
||||
crate::token::Token::AssignOp(crate::token::AssignOpToken::Assign)
|
||||
};
|
||||
|
||||
('(') => {
|
||||
crate::token::Token::LParen
|
||||
};
|
||||
(')') => {
|
||||
crate::token::Token::RParen
|
||||
};
|
||||
('{') => {
|
||||
crate::token::Token::LBrace
|
||||
};
|
||||
('}') => {
|
||||
crate::token::Token::RBrace
|
||||
};
|
||||
('[') => {
|
||||
crate::token::Token::LBracket
|
||||
};
|
||||
(']') => {
|
||||
crate::token::Token::RBracket
|
||||
};
|
||||
|
||||
("async") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("async")))
|
||||
};
|
||||
("as") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("as")))
|
||||
};
|
||||
("await") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Await))
|
||||
};
|
||||
("break") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Break))
|
||||
};
|
||||
("case") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Case))
|
||||
};
|
||||
("catch") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Catch))
|
||||
};
|
||||
("class") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Class))
|
||||
};
|
||||
("const") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Const))
|
||||
};
|
||||
("continue") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Continue))
|
||||
};
|
||||
("debugger") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Debugger))
|
||||
};
|
||||
("default") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Default_))
|
||||
};
|
||||
("delete") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Delete))
|
||||
};
|
||||
("do") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Do))
|
||||
};
|
||||
("else") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Else))
|
||||
};
|
||||
("export") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Export))
|
||||
};
|
||||
("extends") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Extends))
|
||||
};
|
||||
("false") => {
|
||||
crate::token::Token::Word(crate::token::Word::False)
|
||||
};
|
||||
("finally") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Finally))
|
||||
};
|
||||
("for") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::For))
|
||||
};
|
||||
("from") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("from")))
|
||||
};
|
||||
("function") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Function))
|
||||
};
|
||||
("if") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::If))
|
||||
};
|
||||
("in") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::In))
|
||||
};
|
||||
("instanceof") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(
|
||||
crate::token::Keyword::InstanceOf,
|
||||
))
|
||||
};
|
||||
("import") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Import))
|
||||
};
|
||||
("let") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Let))
|
||||
};
|
||||
("new") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::New))
|
||||
};
|
||||
("null") => {
|
||||
crate::token::Token::Word(crate::token::Word::Null)
|
||||
};
|
||||
("of") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("of")))
|
||||
};
|
||||
("return") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Return))
|
||||
};
|
||||
("super") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Super))
|
||||
};
|
||||
("static") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("static")))
|
||||
};
|
||||
("switch") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Switch))
|
||||
};
|
||||
("target") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("target")))
|
||||
};
|
||||
("this") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::This))
|
||||
};
|
||||
("throw") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Throw))
|
||||
};
|
||||
("true") => {
|
||||
crate::token::Token::Word(crate::token::Word::True)
|
||||
};
|
||||
("try") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Try))
|
||||
};
|
||||
("typeof") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::TypeOf))
|
||||
};
|
||||
("var") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Var))
|
||||
};
|
||||
("void") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Void))
|
||||
};
|
||||
("while") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::While))
|
||||
};
|
||||
("with") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::With))
|
||||
};
|
||||
("yield") => {
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Yield))
|
||||
};
|
||||
|
||||
("accessor") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("accessor")))
|
||||
};
|
||||
|
||||
// ----------
|
||||
// JSX
|
||||
// ----------
|
||||
(JSXTagStart) => {
|
||||
crate::token::Token::JSXTagStart
|
||||
};
|
||||
|
||||
(JSXTagEnd) => {
|
||||
crate::token::Token::JSXTagEnd
|
||||
};
|
||||
|
||||
// ----------
|
||||
// Typescript
|
||||
// ----------
|
||||
("asserts") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("asserts")))
|
||||
};
|
||||
("implements") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("implements")))
|
||||
};
|
||||
("is") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("is")))
|
||||
};
|
||||
("new") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("new")))
|
||||
};
|
||||
("keyof") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("keyof")))
|
||||
};
|
||||
("unique") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("unique")))
|
||||
};
|
||||
("object") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("object")))
|
||||
};
|
||||
("global") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("global")))
|
||||
};
|
||||
("require") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("require")))
|
||||
};
|
||||
("enum") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("enum")))
|
||||
};
|
||||
("readonly") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("readonly")))
|
||||
};
|
||||
("as") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("as")))
|
||||
};
|
||||
("satisfies") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("satisfies")))
|
||||
};
|
||||
("namespace") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("namespace")))
|
||||
};
|
||||
("abstract") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("abstract")))
|
||||
};
|
||||
("infer") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("infer")))
|
||||
};
|
||||
("any") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("any")))
|
||||
};
|
||||
("boolean") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("boolean")))
|
||||
};
|
||||
("bigint") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("bigint")))
|
||||
};
|
||||
("intrinsic") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("intrinsic")))
|
||||
};
|
||||
("never") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("never")))
|
||||
};
|
||||
("number") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("number")))
|
||||
};
|
||||
("string") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("string")))
|
||||
};
|
||||
("symbol") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("symbol")))
|
||||
};
|
||||
("unknown") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("unknown")))
|
||||
};
|
||||
("require") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("require")))
|
||||
};
|
||||
("interface") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("interface")))
|
||||
};
|
||||
("declare") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("declare")))
|
||||
};
|
||||
("override") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("override")))
|
||||
};
|
||||
("undefined") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("undefined")))
|
||||
};
|
||||
("meta") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("meta")))
|
||||
};
|
||||
("type") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("type")))
|
||||
};
|
||||
("assert") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("assert")))
|
||||
};
|
||||
("get") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("get")))
|
||||
};
|
||||
("set") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("set")))
|
||||
};
|
||||
("out") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("out")))
|
||||
};
|
||||
("public") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("public")))
|
||||
};
|
||||
("private") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("private")))
|
||||
};
|
||||
("protected") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("protected")))
|
||||
};
|
||||
("using") => {
|
||||
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("using")))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! token_including_semi {
|
||||
(';') => {
|
||||
Token::Semi
|
||||
};
|
||||
($t:tt) => {
|
||||
tok!($t)
|
||||
};
|
||||
}
|
||||
1792
third-party/vendor/swc_ecma_parser/src/parser/class_and_fn.rs
vendored
Normal file
1792
third-party/vendor/swc_ecma_parser/src/parser/class_and_fn.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
2153
third-party/vendor/swc_ecma_parser/src/parser/expr.rs
vendored
Normal file
2153
third-party/vendor/swc_ecma_parser/src/parser/expr.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
437
third-party/vendor/swc_ecma_parser/src/parser/expr/ops.rs
vendored
Normal file
437
third-party/vendor/swc_ecma_parser/src/parser/expr/ops.rs
vendored
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
//! Parser for unary operations and binary operations.
|
||||
use swc_common::Spanned;
|
||||
use tracing::trace;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
/// Name from spec: 'LogicalORExpression'
|
||||
pub(super) fn parse_bin_expr(&mut self) -> PResult<Box<Expr>> {
|
||||
trace_cur!(self, parse_bin_expr);
|
||||
|
||||
let ctx = self.ctx();
|
||||
|
||||
let left = match self.parse_unary_expr() {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
trace_cur!(self, parse_bin_expr__recovery_unary_err);
|
||||
|
||||
match cur!(self, true)? {
|
||||
&tok!("in") if ctx.include_in_expr => {
|
||||
self.emit_err(self.input.cur_span(), SyntaxError::TS1109);
|
||||
|
||||
Box::new(Expr::Invalid(Invalid { span: err.span() }))
|
||||
}
|
||||
&tok!("instanceof") | &Token::BinOp(..) => {
|
||||
self.emit_err(self.input.cur_span(), SyntaxError::TS1109);
|
||||
|
||||
Box::new(Expr::Invalid(Invalid { span: err.span() }))
|
||||
}
|
||||
_ => return Err(err),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return_if_arrow!(self, left);
|
||||
self.parse_bin_op_recursively(left, 0)
|
||||
}
|
||||
|
||||
/// Parse binary operators with the operator precedence parsing
|
||||
/// algorithm. `left` is the left-hand side of the operator.
|
||||
/// `minPrec` provides context that allows the function to stop and
|
||||
/// defer further parser to one of its callers when it encounters an
|
||||
/// operator that has a lower precedence than the set it is parsing.
|
||||
///
|
||||
/// `parseExprOp`
|
||||
pub(in crate::parser) fn parse_bin_op_recursively(
|
||||
&mut self,
|
||||
mut left: Box<Expr>,
|
||||
mut min_prec: u8,
|
||||
) -> PResult<Box<Expr>> {
|
||||
loop {
|
||||
let (next_left, next_prec) = self.parse_bin_op_recursively_inner(left, min_prec)?;
|
||||
|
||||
match &*next_left {
|
||||
Expr::Bin(BinExpr {
|
||||
span,
|
||||
left,
|
||||
op: op!("&&"),
|
||||
..
|
||||
})
|
||||
| Expr::Bin(BinExpr {
|
||||
span,
|
||||
left,
|
||||
op: op!("||"),
|
||||
..
|
||||
}) => {
|
||||
if let Expr::Bin(BinExpr { op: op!("??"), .. }) = &**left {
|
||||
self.emit_err(*span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
min_prec = match next_prec {
|
||||
Some(v) => v,
|
||||
None => return Ok(next_left),
|
||||
};
|
||||
|
||||
left = next_left;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `(left, Some(next_prec))` or `(expr, None)`.
|
||||
fn parse_bin_op_recursively_inner(
|
||||
&mut self,
|
||||
left: Box<Expr>,
|
||||
min_prec: u8,
|
||||
) -> PResult<(Box<Expr>, Option<u8>)> {
|
||||
const PREC_OF_IN: u8 = 7;
|
||||
|
||||
if self.input.syntax().typescript()
|
||||
&& PREC_OF_IN > min_prec
|
||||
&& !self.input.had_line_break_before_cur()
|
||||
&& is!(self, "as")
|
||||
{
|
||||
let start = left.span_lo();
|
||||
let expr = left;
|
||||
let node = if peeked_is!(self, "const") {
|
||||
bump!(self); // as
|
||||
let _ = cur!(self, false);
|
||||
bump!(self); // const
|
||||
Box::new(Expr::TsConstAssertion(TsConstAssertion {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
}))
|
||||
} else {
|
||||
let type_ann = self.next_then_parse_ts_type()?;
|
||||
Box::new(Expr::TsAs(TsAsExpr {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
type_ann,
|
||||
}))
|
||||
};
|
||||
|
||||
return self.parse_bin_op_recursively_inner(node, min_prec);
|
||||
}
|
||||
if self.input.syntax().typescript()
|
||||
&& !self.input.had_line_break_before_cur()
|
||||
&& is!(self, "satisfies")
|
||||
{
|
||||
let start = left.span_lo();
|
||||
let expr = left;
|
||||
let node = {
|
||||
let type_ann = self.next_then_parse_ts_type()?;
|
||||
Box::new(Expr::TsSatisfies(TsSatisfiesExpr {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
type_ann,
|
||||
}))
|
||||
};
|
||||
|
||||
return self.parse_bin_op_recursively_inner(node, min_prec);
|
||||
}
|
||||
|
||||
let ctx = self.ctx();
|
||||
// Return left on eof
|
||||
let word = match cur!(self, false) {
|
||||
Ok(cur) => cur,
|
||||
Err(..) => return Ok((left, None)),
|
||||
};
|
||||
let op = match *word {
|
||||
tok!("in") if ctx.include_in_expr => op!("in"),
|
||||
tok!("instanceof") => op!("instanceof"),
|
||||
Token::BinOp(op) => op.into(),
|
||||
_ => {
|
||||
return Ok((left, None));
|
||||
}
|
||||
};
|
||||
|
||||
if op.precedence() <= min_prec {
|
||||
if cfg!(feature = "debug") {
|
||||
trace!(
|
||||
"returning {:?} without parsing {:?} because min_prec={}, prec={}",
|
||||
left,
|
||||
op,
|
||||
min_prec,
|
||||
op.precedence()
|
||||
);
|
||||
}
|
||||
|
||||
return Ok((left, None));
|
||||
}
|
||||
bump!(self);
|
||||
if cfg!(feature = "debug") {
|
||||
trace!(
|
||||
"parsing binary op {:?} min_prec={}, prec={}",
|
||||
op,
|
||||
min_prec,
|
||||
op.precedence()
|
||||
);
|
||||
}
|
||||
match *left {
|
||||
// This is invalid syntax.
|
||||
Expr::Unary { .. } | Expr::Await(..) if op == op!("**") => {
|
||||
// Correct implementation would be returning Ok(left) and
|
||||
// returning "unexpected token '**'" on next.
|
||||
// But it's not useful error message.
|
||||
|
||||
syntax_error!(
|
||||
self,
|
||||
SyntaxError::UnaryInExp {
|
||||
// FIXME: Use display
|
||||
left: format!("{:?}", left),
|
||||
left_span: left.span(),
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let right = {
|
||||
let left_of_right = self.parse_unary_expr()?;
|
||||
self.parse_bin_op_recursively(
|
||||
left_of_right,
|
||||
if op == op!("**") {
|
||||
// exponential operator is right associative
|
||||
op.precedence() - 1
|
||||
} else {
|
||||
op.precedence()
|
||||
},
|
||||
)?
|
||||
};
|
||||
/* this check is for all ?? operators
|
||||
* a ?? b && c for this example
|
||||
* b && c => This is considered as a logical expression in the ast tree
|
||||
* a => Identifier
|
||||
* so for ?? operator we need to check in this case the right expression to
|
||||
* have parenthesis second case a && b ?? c
|
||||
* here a && b => This is considered as a logical expression in the ast tree
|
||||
* c => identifier
|
||||
* so now here for ?? operator we need to check the left expression to have
|
||||
* parenthesis if the parenthesis is missing we raise an error and
|
||||
* throw it
|
||||
*/
|
||||
if op == op!("??") {
|
||||
match *left {
|
||||
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||
self.emit_err(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match *right {
|
||||
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||
self.emit_err(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let node = Box::new(Expr::Bin(BinExpr {
|
||||
span: Span::new(left.span_lo(), right.span_hi(), Default::default()),
|
||||
op,
|
||||
left,
|
||||
right,
|
||||
}));
|
||||
|
||||
Ok((node, Some(min_prec)))
|
||||
}
|
||||
|
||||
/// Parse unary expression and update expression.
|
||||
///
|
||||
/// spec: 'UnaryExpression'
|
||||
pub(in crate::parser) fn parse_unary_expr(&mut self) -> PResult<Box<Expr>> {
|
||||
trace_cur!(self, parse_unary_expr);
|
||||
let start = cur_pos!(self);
|
||||
|
||||
if !self.input.syntax().jsx() && self.input.syntax().typescript() && eat!(self, '<') {
|
||||
if eat!(self, "const") {
|
||||
expect!(self, '>');
|
||||
let expr = self.parse_unary_expr()?;
|
||||
return Ok(Box::new(Expr::TsConstAssertion(TsConstAssertion {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
})));
|
||||
}
|
||||
|
||||
return self
|
||||
.parse_ts_type_assertion(start)
|
||||
.map(Expr::from)
|
||||
.map(Box::new);
|
||||
}
|
||||
|
||||
// Parse update expression
|
||||
if is!(self, "++") || is!(self, "--") {
|
||||
let op = if bump!(self) == tok!("++") {
|
||||
op!("++")
|
||||
} else {
|
||||
op!("--")
|
||||
};
|
||||
|
||||
let arg = self.parse_unary_expr()?;
|
||||
let span = Span::new(start, arg.span_hi(), Default::default());
|
||||
self.check_assign_target(&arg, false);
|
||||
|
||||
return Ok(Box::new(Expr::Update(UpdateExpr {
|
||||
span,
|
||||
prefix: true,
|
||||
op,
|
||||
arg,
|
||||
})));
|
||||
}
|
||||
|
||||
// Parse unary expression
|
||||
if is_one_of!(self, "delete", "void", "typeof", '+', '-', '~', '!') {
|
||||
let op = match bump!(self) {
|
||||
tok!("delete") => op!("delete"),
|
||||
tok!("void") => op!("void"),
|
||||
tok!("typeof") => op!("typeof"),
|
||||
tok!('+') => op!(unary, "+"),
|
||||
tok!('-') => op!(unary, "-"),
|
||||
tok!('~') => op!("~"),
|
||||
tok!('!') => op!("!"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let arg_start = cur_pos!(self) - BytePos(1);
|
||||
let arg = match self.parse_unary_expr() {
|
||||
Ok(expr) => expr,
|
||||
Err(err) => {
|
||||
self.emit_error(err);
|
||||
Box::new(Expr::Invalid(Invalid {
|
||||
span: Span::new(arg_start, arg_start, Default::default()),
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
if op == op!("delete") {
|
||||
if let Expr::Ident(ref i) = *arg {
|
||||
self.emit_strict_mode_err(i.span, SyntaxError::TS1102)
|
||||
}
|
||||
}
|
||||
|
||||
if self.input.syntax().typescript() && op == op!("delete") {
|
||||
match arg.unwrap_parens() {
|
||||
Expr::Member(..) => {}
|
||||
Expr::OptChain(OptChainExpr { base, .. })
|
||||
if matches!(&**base, OptChainBase::Member(..)) => {}
|
||||
|
||||
expr => {
|
||||
self.emit_err(expr.span(), SyntaxError::TS2703);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Box::new(Expr::Unary(UnaryExpr {
|
||||
span: Span::new(start, arg.span_hi(), Default::default()),
|
||||
op,
|
||||
arg,
|
||||
})));
|
||||
}
|
||||
|
||||
if is!(self, "await") {
|
||||
return self.parse_await_expr();
|
||||
}
|
||||
|
||||
// UpdateExpression
|
||||
let expr = self.parse_lhs_expr()?;
|
||||
return_if_arrow!(self, expr);
|
||||
|
||||
// Line terminator isn't allowed here.
|
||||
if self.input.had_line_break_before_cur() {
|
||||
return Ok(expr);
|
||||
}
|
||||
|
||||
if is_one_of!(self, "++", "--") {
|
||||
self.check_assign_target(&expr, false);
|
||||
|
||||
let op = if bump!(self) == tok!("++") {
|
||||
op!("++")
|
||||
} else {
|
||||
op!("--")
|
||||
};
|
||||
|
||||
return Ok(Box::new(Expr::Update(UpdateExpr {
|
||||
span: span!(self, expr.span_lo()),
|
||||
prefix: false,
|
||||
op,
|
||||
arg: expr,
|
||||
})));
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_await_expr(&mut self) -> PResult<Box<Expr>> {
|
||||
let start = cur_pos!(self);
|
||||
|
||||
assert_and_bump!(self, "await");
|
||||
|
||||
if is!(self, '*') {
|
||||
syntax_error!(self, SyntaxError::AwaitStar);
|
||||
}
|
||||
|
||||
let ctx = self.ctx();
|
||||
|
||||
let span = span!(self, start);
|
||||
|
||||
if is_one_of!(self, ')', ']', ';', ',') && !ctx.in_async {
|
||||
if ctx.module {
|
||||
self.emit_err(span, SyntaxError::InvalidIdentInAsync);
|
||||
}
|
||||
|
||||
return Ok(Box::new(Expr::Ident(Ident::new(js_word!("await"), span))));
|
||||
}
|
||||
|
||||
if ctx.in_function && !ctx.in_async {
|
||||
self.emit_err(self.input.cur_span(), SyntaxError::AwaitInFunction);
|
||||
}
|
||||
|
||||
if ctx.in_parameters && !ctx.in_function {
|
||||
self.emit_err(span, SyntaxError::AwaitParamInAsync);
|
||||
}
|
||||
|
||||
let arg = self.parse_unary_expr()?;
|
||||
Ok(Box::new(Expr::Await(AwaitExpr {
|
||||
span: span!(self, start),
|
||||
arg,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use swc_common::DUMMY_SP as span;
|
||||
use swc_ecma_visit::assert_eq_ignore_span;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn bin(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, Syntax::default(), |p| p.parse_bin_expr())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
assert_eq_ignore_span!(
|
||||
bin("5 + 4 * 7"),
|
||||
Box::new(Expr::Bin(BinExpr {
|
||||
span,
|
||||
op: op!(bin, "+"),
|
||||
left: bin("5"),
|
||||
right: bin("4 * 7"),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_prec() {
|
||||
assert_eq_ignore_span!(
|
||||
bin("5 + 4 + 7"),
|
||||
Box::new(Expr::Bin(BinExpr {
|
||||
span,
|
||||
op: op!(bin, "+"),
|
||||
left: bin("5 + 4"),
|
||||
right: bin("7"),
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
574
third-party/vendor/swc_ecma_parser/src/parser/expr/tests.rs
vendored
Normal file
574
third-party/vendor/swc_ecma_parser/src/parser/expr/tests.rs
vendored
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
extern crate test;
|
||||
|
||||
use std::hint::black_box;
|
||||
|
||||
use swc_common::{FileName, SourceMap, DUMMY_SP as span};
|
||||
use swc_ecma_visit::assert_eq_ignore_span;
|
||||
use test::Bencher;
|
||||
|
||||
use super::*;
|
||||
use crate::{parse_file_as_expr, EsConfig};
|
||||
|
||||
fn syntax() -> Syntax {
|
||||
Syntax::Es(EsConfig {
|
||||
allow_super_outside_method: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn lhs(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, syntax(), |p| p.parse_lhs_expr())
|
||||
}
|
||||
|
||||
fn new_expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, syntax(), |p| p.parse_new_expr())
|
||||
}
|
||||
|
||||
fn member_expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, syntax(), |p| p.parse_member_expr())
|
||||
}
|
||||
|
||||
fn expr(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, syntax(), |p| {
|
||||
p.parse_stmt(true).map(|stmt| match stmt {
|
||||
Stmt::Expr(expr) => expr.expr,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
})
|
||||
}
|
||||
fn regex_expr() -> Box<Expr> {
|
||||
Box::new(Expr::Assign(AssignExpr {
|
||||
span,
|
||||
left: PatOrExpr::Pat(Box::new(Pat::Ident(Ident::new("re".into(), span).into()))),
|
||||
op: AssignOp::Assign,
|
||||
right: Box::new(Expr::Lit(Lit::Regex(Regex {
|
||||
span,
|
||||
exp: "w+".into(),
|
||||
flags: "".into(),
|
||||
}))),
|
||||
}))
|
||||
}
|
||||
#[test]
|
||||
fn regex_single_line_comment() {
|
||||
assert_eq_ignore_span!(
|
||||
expr(
|
||||
r#"re = // ...
|
||||
/w+/"#
|
||||
),
|
||||
regex_expr()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regex_multi_line_comment() {
|
||||
assert_eq_ignore_span!(expr(r#"re = /* ... *//w+/"#), regex_expr())
|
||||
}
|
||||
#[test]
|
||||
fn regex_multi_line_comment_with_lines() {
|
||||
assert_eq_ignore_span!(
|
||||
expr(
|
||||
r#"re =
|
||||
/*
|
||||
...
|
||||
*/
|
||||
/w+/"#
|
||||
),
|
||||
regex_expr()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrow_assign() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("a = b => false"),
|
||||
Box::new(Expr::Assign(AssignExpr {
|
||||
span,
|
||||
left: PatOrExpr::Pat(Box::new(Ident::new("a".into(), span).into())),
|
||||
op: op!("="),
|
||||
right: expr("b => false"),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_call() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("async()"),
|
||||
Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Expr(expr("async")),
|
||||
args: vec![],
|
||||
type_args: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_arrow() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("async () => foo"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: true,
|
||||
is_generator: false,
|
||||
params: vec![],
|
||||
body: Box::new(BlockStmtOrExpr::Expr(expr("foo"))),
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_rest_pat() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("({ ...a34 }) => {}"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![Pat::Object(ObjectPat {
|
||||
span,
|
||||
optional: false,
|
||||
props: vec![ObjectPatProp::Rest(RestPat {
|
||||
span,
|
||||
dot3_token: span,
|
||||
arg: Box::new(Pat::Ident(Ident::new("a34".into(), span).into())),
|
||||
type_ann: None,
|
||||
})],
|
||||
type_ann: None
|
||||
})],
|
||||
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
|
||||
span,
|
||||
stmts: vec![]
|
||||
})),
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_spread() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("foo = {a, ...bar, b}"),
|
||||
Box::new(Expr::Assign(AssignExpr {
|
||||
span,
|
||||
left: PatOrExpr::Pat(Box::new(Pat::Ident(Ident::new("foo".into(), span).into()))),
|
||||
op: op!("="),
|
||||
right: Box::new(Expr::Object(ObjectLit {
|
||||
span,
|
||||
props: vec![
|
||||
PropOrSpread::Prop(Box::new(Ident::new("a".into(), span).into())),
|
||||
PropOrSpread::Spread(SpreadElement {
|
||||
dot3_token: span,
|
||||
expr: Box::new(Expr::Ident(Ident::new("bar".into(), span))),
|
||||
}),
|
||||
PropOrSpread::Prop(Box::new(Ident::new("b".into(), span).into())),
|
||||
]
|
||||
}))
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_expr_should_not_eat_too_much() {
|
||||
assert_eq_ignore_span!(
|
||||
new_expr("new Date().toString()"),
|
||||
Box::new(Expr::Member(MemberExpr {
|
||||
span,
|
||||
obj: member_expr("new Date()"),
|
||||
prop: MemberProp::Ident(Ident::new("toString".into(), span)),
|
||||
}))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn lhs_expr_as_new_expr_prod() {
|
||||
assert_eq_ignore_span!(
|
||||
lhs("new Date.toString()"),
|
||||
Box::new(Expr::New(NewExpr {
|
||||
span,
|
||||
callee: lhs("Date.toString"),
|
||||
args: Some(vec![]),
|
||||
type_args: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lhs_expr_as_call() {
|
||||
assert_eq_ignore_span!(
|
||||
lhs("new Date.toString()()"),
|
||||
Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Expr(lhs("new Date.toString()")),
|
||||
args: vec![],
|
||||
type_args: None,
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrow_fn_no_args() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("() => 1"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![],
|
||||
body: Box::new(BlockStmtOrExpr::Expr(expr("1"))),
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn arrow_fn() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("(a) => 1"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![Pat::Ident(Ident::new("a".into(), span).into())],
|
||||
body: Box::new(BlockStmtOrExpr::Expr(expr("1"))),
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn arrow_fn_rest() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("(...a) => 1"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![Pat::Rest(RestPat {
|
||||
span,
|
||||
dot3_token: span,
|
||||
arg: Box::new(Pat::Ident(Ident::new("a".into(), span).into())),
|
||||
type_ann: None
|
||||
})],
|
||||
body: Box::new(BlockStmtOrExpr::Expr(expr("1"))),
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn arrow_fn_no_paren() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("a => 1"),
|
||||
Box::new(Expr::Arrow(ArrowExpr {
|
||||
span,
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![Pat::Ident(Ident::new("a".into(), span).into())],
|
||||
body: Box::new(BlockStmtOrExpr::Expr(expr("1"))),
|
||||
type_params: None,
|
||||
return_type: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_no_paren() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("new a"),
|
||||
Box::new(Expr::New(NewExpr {
|
||||
span,
|
||||
callee: expr("a"),
|
||||
args: None,
|
||||
type_args: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_new_no_paren() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("new new a"),
|
||||
Box::new(Expr::New(NewExpr {
|
||||
span,
|
||||
callee: expr("new a"),
|
||||
args: None,
|
||||
type_args: None,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_lit() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("[a,,,,, ...d,, e]"),
|
||||
Box::new(Expr::Array(ArrayLit {
|
||||
span,
|
||||
elems: vec![
|
||||
Some(ExprOrSpread {
|
||||
spread: None,
|
||||
expr: Box::new(Expr::Ident(Ident::new("a".into(), span))),
|
||||
}),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(ExprOrSpread {
|
||||
spread: Some(span),
|
||||
expr: Box::new(Expr::Ident(Ident::new("d".into(), span))),
|
||||
}),
|
||||
None,
|
||||
Some(ExprOrSpread {
|
||||
spread: None,
|
||||
expr: Box::new(Expr::Ident(Ident::new("e".into(), span))),
|
||||
}),
|
||||
]
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_integer() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("1.7976931348623157e+308"),
|
||||
Box::new(Expr::Lit(Lit::Num(Number {
|
||||
span,
|
||||
value: 1.797_693_134_862_315_7e308,
|
||||
raw: Some("1.7976931348623157e+308".into()),
|
||||
})))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iife() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("(function(){})()"),
|
||||
Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Expr(expr("(function(){})")),
|
||||
args: vec![],
|
||||
type_args: Default::default(),
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_319_1() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("obj(({ async f() { await g(); } }));"),
|
||||
Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Expr(expr("obj")),
|
||||
args: vec![ExprOrSpread {
|
||||
spread: None,
|
||||
expr: expr("({ async f() { await g(); } })"),
|
||||
}],
|
||||
type_args: Default::default(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_328() {
|
||||
assert_eq_ignore_span!(
|
||||
test_parser("import('test')", Syntax::Es(Default::default()), |p| {
|
||||
p.parse_stmt(true)
|
||||
}),
|
||||
Stmt::Expr(ExprStmt {
|
||||
span,
|
||||
expr: Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Import(Import { span }),
|
||||
args: vec![ExprOrSpread {
|
||||
spread: None,
|
||||
expr: Box::new(Expr::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "test".into(),
|
||||
raw: Some("'test'".into()),
|
||||
}))),
|
||||
}],
|
||||
type_args: Default::default(),
|
||||
}))
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_337() {
|
||||
test_parser(
|
||||
"const foo = 'bar' in bas ? 'beep' : 'boop';",
|
||||
Default::default(),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_350() {
|
||||
assert_eq_ignore_span!(
|
||||
expr(
|
||||
r#""ok\
|
||||
ok\
|
||||
hehe.";"#,
|
||||
),
|
||||
Box::new(Expr::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "okokhehe.".into(),
|
||||
raw: Some("\"ok\\\nok\\\nhehe.\"".into()),
|
||||
})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_380() {
|
||||
expr(
|
||||
" import('../foo/bar')
|
||||
.then(bar => {
|
||||
// bar should be {default: DEFAULT_EXPORTED_THING_IN_BAR} or at least what it is supposed \
|
||||
to be
|
||||
})
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_675() {
|
||||
expr("fn = function () { Object.setPrototypeOf(this, new.target.prototype); }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn super_expr() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("super.foo();"),
|
||||
Box::new(Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: Callee::Expr(Box::new(Expr::SuperProp(SuperPropExpr {
|
||||
span,
|
||||
obj: Super { span },
|
||||
prop: SuperProp::Ident(Ident {
|
||||
span,
|
||||
sym: "foo".into(),
|
||||
optional: false
|
||||
})
|
||||
}))),
|
||||
args: Vec::new(),
|
||||
type_args: Default::default(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn super_expr_computed() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("super[a] ??= 123;"),
|
||||
Box::new(Expr::Assign(AssignExpr {
|
||||
span,
|
||||
op: AssignOp::NullishAssign,
|
||||
left: PatOrExpr::Expr(Box::new(Expr::SuperProp(SuperPropExpr {
|
||||
span,
|
||||
obj: Super { span },
|
||||
prop: SuperProp::Computed(ComputedPropName {
|
||||
span,
|
||||
expr: Box::new(Expr::Ident(Ident {
|
||||
span,
|
||||
sym: "a".into(),
|
||||
optional: false
|
||||
})),
|
||||
})
|
||||
}))),
|
||||
right: Box::new(Expr::Lit(Lit::Num(Number {
|
||||
span,
|
||||
value: 123f64,
|
||||
raw: Some("123".into()),
|
||||
})))
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3672_1() {
|
||||
test_parser(
|
||||
"report({
|
||||
fix: fixable ? null : (): RuleFix => {},
|
||||
});",
|
||||
Syntax::Typescript(Default::default()),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3672_2() {
|
||||
test_parser(
|
||||
"f(a ? (): void => { } : (): void => { })",
|
||||
Syntax::Typescript(Default::default()),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_5947() {
|
||||
test_parser(
|
||||
"[a as number, b as number, c as string] = [1, 2, '3']",
|
||||
Syntax::Typescript(Default::default()),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_6781() {
|
||||
let cm = SourceMap::default();
|
||||
let fm = cm.new_source_file(FileName::Anon, "import.meta.env".to_string());
|
||||
let mut errors = vec![];
|
||||
let expr = parse_file_as_expr(
|
||||
&fm,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
None,
|
||||
&mut errors,
|
||||
);
|
||||
assert!(expr.is_ok());
|
||||
assert!(errors.is_empty());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_new_expr_ts(b: &mut Bencher) {
|
||||
bench_parser(
|
||||
b,
|
||||
"new Foo()",
|
||||
Syntax::Typescript(Default::default()),
|
||||
|p| {
|
||||
black_box(p.parse_expr()?);
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_new_expr_es(b: &mut Bencher) {
|
||||
bench_parser(b, "new Foo()", Syntax::Es(Default::default()), |p| {
|
||||
black_box(p.parse_expr()?);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_member_expr_ts(b: &mut Bencher) {
|
||||
bench_parser(
|
||||
b,
|
||||
"a.b.c.d.e.f",
|
||||
Syntax::Typescript(Default::default()),
|
||||
|p| {
|
||||
black_box(p.parse_expr()?);
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_member_expr_es(b: &mut Bencher) {
|
||||
bench_parser(b, "a.b.c.d.e.f", Syntax::Es(Default::default()), |p| {
|
||||
black_box(p.parse_expr()?);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
47
third-party/vendor/swc_ecma_parser/src/parser/expr/verifier.rs
vendored
Normal file
47
third-party/vendor/swc_ecma_parser/src/parser/expr/verifier.rs
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#[cfg(feature = "verify")]
|
||||
use swc_common::{Span, Spanned};
|
||||
#[cfg(feature = "verify")]
|
||||
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
#[cfg(feature = "verify")]
|
||||
pub(in crate::parser) fn verify_expr(&mut self, expr: Box<Expr>) -> PResult<Box<Expr>> {
|
||||
let mut v = Verifier { errors: vec![] };
|
||||
|
||||
v.visit_expr(&expr);
|
||||
|
||||
for (span, error) in v.errors {
|
||||
self.emit_err(span, error);
|
||||
}
|
||||
|
||||
return Ok(expr);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "verify"))]
|
||||
pub(in crate::parser) fn verify_expr(&mut self, expr: Box<Expr>) -> PResult<Box<Expr>> {
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
pub(super) struct Verifier {
|
||||
pub errors: Vec<(Span, SyntaxError)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
impl Visit for Verifier {
|
||||
noop_visit_type!();
|
||||
|
||||
fn visit_assign_prop(&mut self, p: &AssignProp) {
|
||||
self.errors.push((p.span(), SyntaxError::AssignProperty));
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &Expr) {
|
||||
match *e {
|
||||
Expr::Fn(..) | Expr::Arrow(..) => {}
|
||||
_ => e.visit_children_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
173
third-party/vendor/swc_ecma_parser/src/parser/ident.rs
vendored
Normal file
173
third-party/vendor/swc_ecma_parser/src/parser/ident.rs
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//! 12.1 Identifiers
|
||||
use either::Either;
|
||||
use swc_atoms::js_word;
|
||||
|
||||
use super::*;
|
||||
use crate::token::Keyword;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
pub(super) fn parse_maybe_private_name(&mut self) -> PResult<Either<PrivateName, Ident>> {
|
||||
let is_private = is!(self, '#');
|
||||
|
||||
if is_private {
|
||||
self.parse_private_name().map(Either::Left)
|
||||
} else {
|
||||
self.parse_ident_name().map(Either::Right)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_private_name(&mut self) -> PResult<PrivateName> {
|
||||
let start = cur_pos!(self);
|
||||
assert_and_bump!(self, '#');
|
||||
|
||||
let hash_end = self.input.prev_span().hi;
|
||||
if self.input.cur_pos() - hash_end != BytePos(0) {
|
||||
syntax_error!(
|
||||
self,
|
||||
span!(self, start),
|
||||
SyntaxError::SpaceBetweenHashAndIdent
|
||||
);
|
||||
}
|
||||
|
||||
let id = self.parse_ident_name()?;
|
||||
Ok(PrivateName {
|
||||
span: span!(self, start),
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/// IdentifierReference
|
||||
pub(super) fn parse_ident_ref(&mut self) -> PResult<Ident> {
|
||||
let ctx = self.ctx();
|
||||
|
||||
self.parse_ident(!ctx.in_generator, !ctx.in_async)
|
||||
}
|
||||
|
||||
/// LabelIdentifier
|
||||
pub(super) fn parse_label_ident(&mut self) -> PResult<Ident> {
|
||||
let ctx = self.ctx();
|
||||
|
||||
self.parse_ident(!ctx.in_generator, !ctx.in_async)
|
||||
}
|
||||
|
||||
/// Use this when spec says "IdentifierName".
|
||||
/// This allows idents like `catch`.
|
||||
pub(super) fn parse_ident_name(&mut self) -> PResult<Ident> {
|
||||
let in_type = self.ctx().in_type;
|
||||
|
||||
let start = cur_pos!(self);
|
||||
|
||||
let w = match cur!(self, true) {
|
||||
Ok(&Word(..)) => match bump!(self) {
|
||||
Word(w) => w.into(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
||||
Ok(&Token::JSXName { .. }) if in_type => match bump!(self) {
|
||||
Token::JSXName { name } => name,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
||||
_ => syntax_error!(self, SyntaxError::ExpectedIdent),
|
||||
};
|
||||
|
||||
Ok(Ident::new(w, span!(self, start)))
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#prod-ModuleExportName
|
||||
pub(super) fn parse_module_export_name(&mut self) -> PResult<ModuleExportName> {
|
||||
let module_export_name = match cur!(self, false) {
|
||||
Ok(&Token::Str { .. }) => match self.parse_lit()? {
|
||||
Lit::Str(str_lit) => ModuleExportName::Str(str_lit),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Ok(&Word(..)) => ModuleExportName::Ident(self.parse_ident_name()?),
|
||||
_ => {
|
||||
unexpected!(self, "identifier or string");
|
||||
}
|
||||
};
|
||||
Ok(module_export_name)
|
||||
}
|
||||
|
||||
/// Identifier
|
||||
///
|
||||
/// In strict mode, "yield" is SyntaxError if matched.
|
||||
pub(super) fn parse_ident(&mut self, incl_yield: bool, incl_await: bool) -> PResult<Ident> {
|
||||
trace_cur!(self, parse_ident);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
|
||||
let word = self.parse_with(|p| {
|
||||
let w = match cur!(p, true) {
|
||||
Ok(&Word(..)) => match bump!(p) {
|
||||
Word(w) => w,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => syntax_error!(p, SyntaxError::ExpectedIdent),
|
||||
};
|
||||
|
||||
// Spec:
|
||||
// It is a Syntax Error if this phrase is contained in strict mode code and the
|
||||
// StringValue of IdentifierName is: "implements", "interface", "let",
|
||||
// "package", "private", "protected", "public", "static", or "yield".
|
||||
match w {
|
||||
Word::Ident(ref name @ js_word!("enum")) => {
|
||||
p.emit_err(
|
||||
p.input.prev_span(),
|
||||
SyntaxError::InvalidIdentInStrict(name.clone()),
|
||||
);
|
||||
}
|
||||
Word::Keyword(name @ Keyword::Yield) | Word::Keyword(name @ Keyword::Let) => {
|
||||
p.emit_strict_mode_err(
|
||||
p.input.prev_span(),
|
||||
SyntaxError::InvalidIdentInStrict(name.into_js_word()),
|
||||
);
|
||||
}
|
||||
|
||||
Word::Ident(ref name @ js_word!("static"))
|
||||
| Word::Ident(ref name @ js_word!("implements"))
|
||||
| Word::Ident(ref name @ js_word!("interface"))
|
||||
| Word::Ident(ref name @ js_word!("package"))
|
||||
| Word::Ident(ref name @ js_word!("private"))
|
||||
| Word::Ident(ref name @ js_word!("protected"))
|
||||
| Word::Ident(ref name @ js_word!("public")) => {
|
||||
p.emit_strict_mode_err(
|
||||
p.input.prev_span(),
|
||||
SyntaxError::InvalidIdentInStrict(name.clone()),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Spec:
|
||||
// It is a Syntax Error if StringValue of IdentifierName is the same String
|
||||
// value as the StringValue of any ReservedWord except for yield or await.
|
||||
match w {
|
||||
Word::Keyword(Keyword::Await) if p.ctx().in_declare => Ok(js_word!("await")),
|
||||
|
||||
// It is a Syntax Error if the goal symbol of the syntactic grammar is Module
|
||||
// and the StringValue of IdentifierName is "await".
|
||||
Word::Keyword(Keyword::Await) if p.ctx().module | p.ctx().in_async => {
|
||||
syntax_error!(p, p.input.prev_span(), SyntaxError::InvalidIdentInAsync)
|
||||
}
|
||||
Word::Keyword(Keyword::This) if p.input.syntax().typescript() => {
|
||||
Ok(js_word!("this"))
|
||||
}
|
||||
Word::Keyword(Keyword::Let) => Ok(js_word!("let")),
|
||||
Word::Ident(ident) => {
|
||||
if ident == js_word!("arguments") && p.ctx().in_class_field {
|
||||
p.emit_err(p.input.prev_span(), SyntaxError::ArgumentsInClassField)
|
||||
}
|
||||
Ok(ident)
|
||||
}
|
||||
Word::Keyword(Keyword::Yield) if incl_yield => Ok(js_word!("yield")),
|
||||
Word::Keyword(Keyword::Await) if incl_await => Ok(js_word!("await")),
|
||||
Word::Keyword(..) | Word::Null | Word::True | Word::False => {
|
||||
syntax_error!(p, p.input.prev_span(), SyntaxError::ExpectedIdent)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Ident::new(word, span!(self, start)))
|
||||
}
|
||||
}
|
||||
497
third-party/vendor/swc_ecma_parser/src/parser/input.rs
vendored
Normal file
497
third-party/vendor/swc_ecma_parser/src/parser/input.rs
vendored
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
use std::{cell::RefCell, mem, mem::take, rc::Rc};
|
||||
|
||||
use lexer::TokenContexts;
|
||||
use swc_common::{BytePos, Span};
|
||||
|
||||
use super::Parser;
|
||||
use crate::{
|
||||
error::Error,
|
||||
lexer::{self},
|
||||
token::*,
|
||||
Context, EsVersion, Syntax,
|
||||
};
|
||||
|
||||
/// Clone should be cheap if you are parsing typescript because typescript
|
||||
/// syntax requires backtracking.
|
||||
pub trait Tokens: Clone + Iterator<Item = TokenAndSpan> {
|
||||
fn set_ctx(&mut self, ctx: Context);
|
||||
fn ctx(&self) -> Context;
|
||||
fn syntax(&self) -> Syntax;
|
||||
fn target(&self) -> EsVersion;
|
||||
|
||||
fn start_pos(&self) -> BytePos {
|
||||
BytePos(0)
|
||||
}
|
||||
|
||||
fn set_expr_allowed(&mut self, allow: bool);
|
||||
fn set_next_regexp(&mut self, start: Option<BytePos>);
|
||||
|
||||
fn token_context(&self) -> &lexer::TokenContexts;
|
||||
fn token_context_mut(&mut self) -> &mut lexer::TokenContexts;
|
||||
fn set_token_context(&mut self, _c: lexer::TokenContexts);
|
||||
|
||||
/// Implementors should use Rc<RefCell<Vec<Error>>>.
|
||||
///
|
||||
/// It is required because parser should backtrack while parsing typescript
|
||||
/// code.
|
||||
fn add_error(&self, error: Error);
|
||||
|
||||
/// Add an error which is valid syntax in script mode.
|
||||
///
|
||||
/// This errors should be dropped if it's not a module.
|
||||
///
|
||||
/// Implementor should check for if [Context].module, and buffer errors if
|
||||
/// module is false. Also, implementors should move errors to the error
|
||||
/// buffer on set_ctx if the parser mode become module mode.
|
||||
fn add_module_mode_error(&self, error: Error);
|
||||
|
||||
fn take_errors(&mut self) -> Vec<Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TokensInput {
|
||||
iter: <Vec<TokenAndSpan> as IntoIterator>::IntoIter,
|
||||
ctx: Context,
|
||||
syntax: Syntax,
|
||||
start_pos: BytePos,
|
||||
target: EsVersion,
|
||||
token_ctx: TokenContexts,
|
||||
errors: Rc<RefCell<Vec<Error>>>,
|
||||
module_errors: Rc<RefCell<Vec<Error>>>,
|
||||
}
|
||||
|
||||
impl TokensInput {
|
||||
pub fn new(tokens: Vec<TokenAndSpan>, ctx: Context, syntax: Syntax, target: EsVersion) -> Self {
|
||||
let start_pos = tokens.first().map(|t| t.span.lo).unwrap_or(BytePos(0));
|
||||
|
||||
TokensInput {
|
||||
iter: tokens.into_iter(),
|
||||
ctx,
|
||||
syntax,
|
||||
start_pos,
|
||||
target,
|
||||
token_ctx: Default::default(),
|
||||
errors: Default::default(),
|
||||
module_errors: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for TokensInput {
|
||||
type Item = TokenAndSpan;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokens for TokensInput {
|
||||
fn set_ctx(&mut self, ctx: Context) {
|
||||
if ctx.module && !self.module_errors.borrow().is_empty() {
|
||||
let mut module_errors = self.module_errors.borrow_mut();
|
||||
self.errors.borrow_mut().append(&mut *module_errors);
|
||||
}
|
||||
self.ctx = ctx;
|
||||
}
|
||||
|
||||
fn ctx(&self) -> Context {
|
||||
self.ctx
|
||||
}
|
||||
|
||||
fn syntax(&self) -> Syntax {
|
||||
self.syntax
|
||||
}
|
||||
|
||||
fn target(&self) -> EsVersion {
|
||||
self.target
|
||||
}
|
||||
|
||||
fn start_pos(&self) -> BytePos {
|
||||
self.start_pos
|
||||
}
|
||||
|
||||
fn set_expr_allowed(&mut self, _: bool) {}
|
||||
|
||||
fn set_next_regexp(&mut self, _: Option<BytePos>) {}
|
||||
|
||||
fn token_context(&self) -> &TokenContexts {
|
||||
&self.token_ctx
|
||||
}
|
||||
|
||||
fn token_context_mut(&mut self) -> &mut TokenContexts {
|
||||
&mut self.token_ctx
|
||||
}
|
||||
|
||||
fn set_token_context(&mut self, c: TokenContexts) {
|
||||
self.token_ctx = c;
|
||||
}
|
||||
|
||||
fn add_error(&self, error: Error) {
|
||||
self.errors.borrow_mut().push(error);
|
||||
}
|
||||
|
||||
fn add_module_mode_error(&self, error: Error) {
|
||||
if self.ctx.module {
|
||||
self.add_error(error);
|
||||
return;
|
||||
}
|
||||
self.module_errors.borrow_mut().push(error);
|
||||
}
|
||||
|
||||
fn take_errors(&mut self) -> Vec<Error> {
|
||||
take(&mut self.errors.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: Lexer need access to parser's context to lex correctly.
|
||||
#[derive(Debug)]
|
||||
pub struct Capturing<I: Tokens> {
|
||||
inner: I,
|
||||
captured: Rc<RefCell<Vec<TokenAndSpan>>>,
|
||||
}
|
||||
|
||||
impl<I: Tokens> Clone for Capturing<I> {
|
||||
fn clone(&self) -> Self {
|
||||
Capturing {
|
||||
inner: self.inner.clone(),
|
||||
captured: self.captured.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Capturing<I> {
|
||||
pub fn new(input: I) -> Self {
|
||||
Capturing {
|
||||
inner: input,
|
||||
captured: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take captured tokens
|
||||
pub fn take(&mut self) -> Vec<TokenAndSpan> {
|
||||
mem::take(&mut *self.captured.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Iterator for Capturing<I> {
|
||||
type Item = TokenAndSpan;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.inner.next();
|
||||
|
||||
match next {
|
||||
Some(ts) => {
|
||||
let mut v = self.captured.borrow_mut();
|
||||
|
||||
// remove tokens that could change due to backtracing
|
||||
while let Some(last) = v.last() {
|
||||
if last.span.lo >= ts.span.lo {
|
||||
v.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v.push(ts.clone());
|
||||
|
||||
Some(ts)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Tokens for Capturing<I> {
|
||||
fn set_ctx(&mut self, ctx: Context) {
|
||||
self.inner.set_ctx(ctx)
|
||||
}
|
||||
|
||||
fn ctx(&self) -> Context {
|
||||
self.inner.ctx()
|
||||
}
|
||||
|
||||
fn syntax(&self) -> Syntax {
|
||||
self.inner.syntax()
|
||||
}
|
||||
|
||||
fn target(&self) -> EsVersion {
|
||||
self.inner.target()
|
||||
}
|
||||
|
||||
fn start_pos(&self) -> BytePos {
|
||||
self.inner.start_pos()
|
||||
}
|
||||
|
||||
fn set_expr_allowed(&mut self, allow: bool) {
|
||||
self.inner.set_expr_allowed(allow)
|
||||
}
|
||||
|
||||
fn set_next_regexp(&mut self, start: Option<BytePos>) {
|
||||
self.inner.set_next_regexp(start);
|
||||
}
|
||||
|
||||
fn token_context(&self) -> &TokenContexts {
|
||||
self.inner.token_context()
|
||||
}
|
||||
|
||||
fn token_context_mut(&mut self) -> &mut TokenContexts {
|
||||
self.inner.token_context_mut()
|
||||
}
|
||||
|
||||
fn set_token_context(&mut self, c: TokenContexts) {
|
||||
self.inner.set_token_context(c)
|
||||
}
|
||||
|
||||
fn add_error(&self, error: Error) {
|
||||
self.inner.add_error(error);
|
||||
}
|
||||
|
||||
fn add_module_mode_error(&self, error: Error) {
|
||||
self.inner.add_module_mode_error(error)
|
||||
}
|
||||
|
||||
fn take_errors(&mut self) -> Vec<Error> {
|
||||
self.inner.take_errors()
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is responsible for managing current token and peeked token.
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Buffer<I: Tokens> {
|
||||
iter: I,
|
||||
/// Span of the previous token.
|
||||
prev_span: Span,
|
||||
cur: Option<TokenAndSpan>,
|
||||
/// Peeked token
|
||||
next: Option<TokenAndSpan>,
|
||||
}
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
pub fn input(&mut self) -> &mut I {
|
||||
&mut self.input.iter
|
||||
}
|
||||
|
||||
pub(crate) fn input_ref(&self) -> &I {
|
||||
&self.input.iter
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Buffer<I> {
|
||||
pub fn new(lexer: I) -> Self {
|
||||
let start_pos = lexer.start_pos();
|
||||
Buffer {
|
||||
iter: lexer,
|
||||
cur: None,
|
||||
prev_span: Span::new(start_pos, start_pos, Default::default()),
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store(&mut self, token: Token) {
|
||||
debug_assert!(self.next.is_none());
|
||||
debug_assert!(self.cur.is_none());
|
||||
let span = self.prev_span;
|
||||
|
||||
self.cur = Some(TokenAndSpan {
|
||||
span,
|
||||
token,
|
||||
had_line_break: false,
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn bump_inner(&mut self) {
|
||||
let prev = self.cur.take();
|
||||
self.prev_span = match prev {
|
||||
Some(TokenAndSpan { span, .. }) => span,
|
||||
_ => self.prev_span,
|
||||
};
|
||||
|
||||
// If we have peeked a token, take it instead of calling lexer.next()
|
||||
self.cur = self.next.take().or_else(|| self.iter.next());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cur_debug(&self) -> Option<&Token> {
|
||||
self.cur.as_ref().map(|it| &it.token)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn dump_cur(&mut self) -> String {
|
||||
match self.cur() {
|
||||
Some(v) => format!("{:?}", v),
|
||||
None => "<eof>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns current token.
|
||||
pub fn bump(&mut self) -> Token {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn invalid_state() -> ! {
|
||||
unreachable!(
|
||||
"Current token is `None`. Parser should not call bump() without knowing current \
|
||||
token"
|
||||
)
|
||||
}
|
||||
|
||||
let prev = match self.cur.take() {
|
||||
Some(t) => t,
|
||||
None => invalid_state(),
|
||||
};
|
||||
self.prev_span = prev.span;
|
||||
|
||||
prev.token
|
||||
}
|
||||
|
||||
pub fn knows_cur(&self) -> bool {
|
||||
self.cur.is_some()
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> Option<&Token> {
|
||||
debug_assert!(
|
||||
self.cur.is_some(),
|
||||
"parser should not call peek() without knowing current token"
|
||||
);
|
||||
|
||||
if self.next.is_none() {
|
||||
self.next = self.iter.next();
|
||||
}
|
||||
|
||||
self.next.as_ref().map(|ts| &ts.token)
|
||||
}
|
||||
|
||||
/// Returns true on eof.
|
||||
pub fn had_line_break_before_cur(&mut self) -> bool {
|
||||
self.cur();
|
||||
|
||||
self.cur
|
||||
.as_ref()
|
||||
.map(|it| it.had_line_break)
|
||||
.unwrap_or_else(|| true)
|
||||
}
|
||||
|
||||
/// This returns true on eof.
|
||||
pub fn has_linebreak_between_cur_and_peeked(&mut self) -> bool {
|
||||
let _ = self.peek();
|
||||
self.next
|
||||
.as_ref()
|
||||
.map(|item| item.had_line_break)
|
||||
.unwrap_or({
|
||||
// return true on eof.
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
/// Get current token. Returns `None` only on eof.
|
||||
#[inline]
|
||||
pub fn cur(&mut self) -> Option<&Token> {
|
||||
if self.cur.is_none() {
|
||||
self.bump_inner();
|
||||
}
|
||||
|
||||
match &self.cur {
|
||||
Some(v) => Some(&v.token),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is(&mut self, expected: &Token) -> bool {
|
||||
match self.cur() {
|
||||
Some(t) => *expected == *t,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn eat(&mut self, expected: &Token) -> bool {
|
||||
let v = self.is(expected);
|
||||
if v {
|
||||
self.bump();
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
/// Returns start of current token.
|
||||
#[inline]
|
||||
pub fn cur_pos(&mut self) -> BytePos {
|
||||
let _ = self.cur();
|
||||
self.cur
|
||||
.as_ref()
|
||||
.map(|item| item.span.lo)
|
||||
.unwrap_or_else(|| {
|
||||
// eof
|
||||
self.last_pos()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cur_span(&self) -> Span {
|
||||
let data = self
|
||||
.cur
|
||||
.as_ref()
|
||||
.map(|item| item.span)
|
||||
.unwrap_or(self.prev_span);
|
||||
|
||||
Span::new(data.lo, data.hi, data.ctxt)
|
||||
}
|
||||
|
||||
/// Returns last byte position of previous token.
|
||||
#[inline]
|
||||
pub fn last_pos(&self) -> BytePos {
|
||||
self.prev_span.hi
|
||||
}
|
||||
|
||||
/// Returns span of the previous token.
|
||||
#[inline]
|
||||
pub fn prev_span(&self) -> Span {
|
||||
self.prev_span
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_ctx(&self) -> Context {
|
||||
self.iter.ctx()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_ctx(&mut self, ctx: Context) {
|
||||
self.iter.set_ctx(ctx);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn syntax(&self) -> Syntax {
|
||||
self.iter.syntax()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn target(&self) -> EsVersion {
|
||||
self.iter.target()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_expr_allowed(&mut self, allow: bool) {
|
||||
self.iter.set_expr_allowed(allow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_next_regexp(&mut self, start: Option<BytePos>) {
|
||||
self.iter.set_next_regexp(start);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn token_context(&self) -> &lexer::TokenContexts {
|
||||
self.iter.token_context()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn token_context_mut(&mut self) -> &mut lexer::TokenContexts {
|
||||
self.iter.token_context_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_token_context(&mut self, c: lexer::TokenContexts) {
|
||||
self.iter.set_token_context(c)
|
||||
}
|
||||
}
|
||||
478
third-party/vendor/swc_ecma_parser/src/parser/jsx.rs
vendored
Normal file
478
third-party/vendor/swc_ecma_parser/src/parser/jsx.rs
vendored
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
use either::Either;
|
||||
use swc_common::{Span, Spanned, SyntaxContext};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
/// Parse next token as JSX identifier
|
||||
pub(super) fn parse_jsx_ident(&mut self) -> PResult<Ident> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
trace_cur!(self, parse_jsx_ident);
|
||||
|
||||
let ctx = self.ctx();
|
||||
match *cur!(self, true)? {
|
||||
Token::JSXName { .. } => match bump!(self) {
|
||||
Token::JSXName { name } => {
|
||||
let span = self.input.prev_span();
|
||||
Ok(Ident::new(name, span))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ if ctx.in_forced_jsx_context => self.parse_ident_ref(),
|
||||
_ => unexpected!(self, "jsx identifier"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse namespaced identifier.
|
||||
pub(super) fn parse_jsx_namespaced_name(&mut self) -> PResult<JSXAttrName> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
trace_cur!(self, parse_jsx_namespaced_name);
|
||||
|
||||
let ns = self.parse_jsx_ident()?;
|
||||
if !eat!(self, ':') {
|
||||
return Ok(JSXAttrName::Ident(ns));
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_ident()?;
|
||||
Ok(JSXAttrName::JSXNamespacedName(JSXNamespacedName {
|
||||
ns,
|
||||
name,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses element name in any form - namespaced, member or single
|
||||
/// identifier.
|
||||
pub(super) fn parse_jsx_element_name(&mut self) -> PResult<JSXElementName> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
trace_cur!(self, parse_jsx_element_name);
|
||||
|
||||
let mut node = match self.parse_jsx_namespaced_name()? {
|
||||
JSXAttrName::Ident(i) => JSXElementName::Ident(i),
|
||||
JSXAttrName::JSXNamespacedName(i) => JSXElementName::JSXNamespacedName(i),
|
||||
};
|
||||
while eat!(self, '.') {
|
||||
let prop = self.parse_jsx_ident()?;
|
||||
let new_node = JSXElementName::JSXMemberExpr(JSXMemberExpr {
|
||||
obj: match node {
|
||||
JSXElementName::Ident(i) => JSXObject::Ident(i),
|
||||
JSXElementName::JSXMemberExpr(i) => JSXObject::JSXMemberExpr(Box::new(i)),
|
||||
_ => unimplemented!("JSXNamespacedName -> JSXObject"),
|
||||
},
|
||||
prop,
|
||||
});
|
||||
node = new_node;
|
||||
}
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
/// Parses any type of JSX attribute value.
|
||||
///
|
||||
/// TODO(kdy1): Change return type to JSXAttrValue
|
||||
pub(super) fn parse_jsx_attr_value(&mut self) -> PResult<JSXAttrValue> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
trace_cur!(self, parse_jsx_attr_value);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
|
||||
match *cur!(self, true)? {
|
||||
tok!('{') => {
|
||||
let node = self.parse_jsx_expr_container(start)?;
|
||||
|
||||
match node.expr {
|
||||
JSXExpr::JSXEmptyExpr(..) => {
|
||||
syntax_error!(self, span!(self, start), SyntaxError::EmptyJSXAttr)
|
||||
}
|
||||
JSXExpr::Expr(..) => Ok(node.into()),
|
||||
}
|
||||
}
|
||||
Token::Str { .. } => {
|
||||
let lit = self.parse_lit()?;
|
||||
Ok(JSXAttrValue::Lit(lit))
|
||||
}
|
||||
Token::JSXTagStart => {
|
||||
let expr = self.parse_jsx_element()?;
|
||||
match expr {
|
||||
Either::Left(n) => Ok(JSXAttrValue::JSXFragment(n)),
|
||||
Either::Right(n) => Ok(JSXAttrValue::JSXElement(Box::new(n))),
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let span = self.input.cur_span();
|
||||
syntax_error!(self, span, SyntaxError::InvalidJSXValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// JSXEmptyExpression is unique type since it doesn't actually parse
|
||||
/// anything, and so it should start at the end of last read token (left
|
||||
/// brace) and finish at the beginning of the next one (right brace).
|
||||
pub(super) fn parse_jsx_empty_expr(&mut self) -> PResult<JSXEmptyExpr> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!(self);
|
||||
|
||||
Ok(JSXEmptyExpr {
|
||||
span: Span::new(start, start, SyntaxContext::empty()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse JSX spread child
|
||||
pub(super) fn parse_jsx_spread_child(&mut self) -> PResult<JSXSpreadChild> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!(self);
|
||||
expect!(self, '{');
|
||||
expect!(self, "...");
|
||||
let expr = self.parse_expr()?;
|
||||
expect!(self, '}');
|
||||
|
||||
Ok(JSXSpreadChild {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses JSX expression enclosed into curly brackets.
|
||||
pub(super) fn parse_jsx_expr_container(&mut self, _: BytePos) -> PResult<JSXExprContainer> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let start = cur_pos!(self);
|
||||
bump!(self);
|
||||
let expr = if is!(self, '}') {
|
||||
self.parse_jsx_empty_expr().map(JSXExpr::JSXEmptyExpr)?
|
||||
} else {
|
||||
if is!(self, "...") {
|
||||
bump!(self);
|
||||
}
|
||||
self.parse_expr().map(JSXExpr::Expr)?
|
||||
};
|
||||
expect!(self, '}');
|
||||
Ok(JSXExprContainer {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses following JSX attribute name-value pair.
|
||||
pub(super) fn parse_jsx_attr(&mut self) -> PResult<JSXAttrOrSpread> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
let start = cur_pos!(self);
|
||||
|
||||
let _tracing = debug_tracing!(self, "parse_jsx_attr");
|
||||
|
||||
if eat!(self, '{') {
|
||||
let dot3_start = cur_pos!(self);
|
||||
expect!(self, "...");
|
||||
let dot3_token = span!(self, dot3_start);
|
||||
let expr = self.parse_assignment_expr()?;
|
||||
expect!(self, '}');
|
||||
return Ok(SpreadElement { dot3_token, expr }.into());
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_namespaced_name()?;
|
||||
let value = if eat!(self, '=') {
|
||||
let ctx = Context {
|
||||
in_cond_expr: false,
|
||||
will_expect_colon_for_cond: false,
|
||||
..self.ctx()
|
||||
};
|
||||
|
||||
self.with_ctx(ctx).parse_jsx_attr_value().map(Some)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(JSXAttr {
|
||||
span: span!(self, start),
|
||||
name,
|
||||
value,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Parses JSX opening tag starting after "<".
|
||||
pub(super) fn parse_jsx_opening_element_at(
|
||||
&mut self,
|
||||
start: BytePos,
|
||||
) -> PResult<Either<JSXOpeningFragment, JSXOpeningElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
if eat!(self, JSXTagEnd) {
|
||||
return Ok(Either::Left(JSXOpeningFragment {
|
||||
span: span!(self, start),
|
||||
}));
|
||||
}
|
||||
|
||||
let ctx = Context {
|
||||
should_not_lex_lt_or_gt_as_type: false,
|
||||
..self.ctx()
|
||||
};
|
||||
let name = self.with_ctx(ctx).parse_jsx_element_name()?;
|
||||
self.parse_jsx_opening_element_after_name(start, name)
|
||||
.map(Either::Right)
|
||||
}
|
||||
|
||||
/// `jsxParseOpeningElementAfterName`
|
||||
pub(super) fn parse_jsx_opening_element_after_name(
|
||||
&mut self,
|
||||
start: BytePos,
|
||||
name: JSXElementName,
|
||||
) -> PResult<JSXOpeningElement> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let type_args = if self.input.syntax().typescript() && is!(self, '<') {
|
||||
self.try_parse_ts(|p| p.parse_ts_type_args().map(Some))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut attrs = vec![];
|
||||
while cur!(self, false).is_ok() {
|
||||
trace_cur!(self, parse_jsx_opening__attrs_loop);
|
||||
|
||||
if is!(self, '/') || is!(self, JSXTagEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
let attr = self.parse_jsx_attr()?;
|
||||
attrs.push(attr);
|
||||
}
|
||||
let self_closing = eat!(self, '/');
|
||||
if !eat!(self, JSXTagEnd) & !(self.ctx().in_forced_jsx_context && eat!(self, '>')) {
|
||||
unexpected!(self, "> (jsx closing tag)");
|
||||
}
|
||||
Ok(JSXOpeningElement {
|
||||
span: span!(self, start),
|
||||
name,
|
||||
attrs,
|
||||
self_closing,
|
||||
type_args,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses JSX closing tag starting after "</".
|
||||
fn parse_jsx_closing_element_at(
|
||||
&mut self,
|
||||
start: BytePos,
|
||||
) -> PResult<Either<JSXClosingFragment, JSXClosingElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
if eat!(self, JSXTagEnd) {
|
||||
return Ok(Either::Left(JSXClosingFragment {
|
||||
span: span!(self, start),
|
||||
}));
|
||||
}
|
||||
|
||||
let name = self.parse_jsx_element_name()?;
|
||||
expect!(self, JSXTagEnd);
|
||||
Ok(Either::Right(JSXClosingElement {
|
||||
span: span!(self, start),
|
||||
name,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Parses entire JSX element, including it"s opening tag
|
||||
/// (starting after "<"), attributes, contents and closing tag.
|
||||
///
|
||||
/// babel: `jsxParseElementAt`
|
||||
pub(super) fn parse_jsx_element_at(
|
||||
&mut self,
|
||||
start_pos: BytePos,
|
||||
) -> PResult<Either<JSXFragment, JSXElement>> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
|
||||
let _ = cur!(self, true);
|
||||
let start = cur_pos!(self);
|
||||
let forced_jsx_context = match bump!(self) {
|
||||
tok!('<') => true,
|
||||
Token::JSXTagStart => false,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ctx = Context {
|
||||
in_forced_jsx_context: forced_jsx_context,
|
||||
should_not_lex_lt_or_gt_as_type: false,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx).parse_with(|p| {
|
||||
let _tracing = debug_tracing!(p, "parse_jsx_element");
|
||||
|
||||
let opening_element = p.parse_jsx_opening_element_at(start_pos)?;
|
||||
|
||||
trace_cur!(p, parse_jsx_element__after_opening_element);
|
||||
|
||||
let mut children = vec![];
|
||||
let mut closing_element = None;
|
||||
|
||||
let self_closing = match opening_element {
|
||||
Either::Right(ref el) => el.self_closing,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !self_closing {
|
||||
'contents: loop {
|
||||
match *cur!(p, true)? {
|
||||
Token::JSXTagStart => {
|
||||
let start = cur_pos!(p);
|
||||
|
||||
if peeked_is!(p, '/') {
|
||||
bump!(p); // JSXTagStart
|
||||
let _ = cur!(p, true);
|
||||
assert_and_bump!(p, '/');
|
||||
|
||||
closing_element =
|
||||
p.parse_jsx_closing_element_at(start).map(Some)?;
|
||||
break 'contents;
|
||||
}
|
||||
|
||||
children.push(p.parse_jsx_element_at(start).map(|e| match e {
|
||||
Either::Left(e) => JSXElementChild::from(e),
|
||||
Either::Right(e) => JSXElementChild::from(Box::new(e)),
|
||||
})?);
|
||||
}
|
||||
Token::JSXText { .. } => {
|
||||
children.push(p.parse_jsx_text().map(JSXElementChild::from)?)
|
||||
}
|
||||
tok!('{') => {
|
||||
let start = cur_pos!(p);
|
||||
if peeked_is!(p, "...") {
|
||||
children
|
||||
.push(p.parse_jsx_spread_child().map(JSXElementChild::from)?);
|
||||
} else {
|
||||
children.push(
|
||||
p.parse_jsx_expr_container(start)
|
||||
.map(JSXElementChild::from)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => unexpected!(p, "< (jsx tag start), jsx text or {"),
|
||||
}
|
||||
}
|
||||
}
|
||||
let span = span!(p, start);
|
||||
|
||||
Ok(match (opening_element, closing_element) {
|
||||
(Either::Left(..), Some(Either::Right(closing))) => {
|
||||
syntax_error!(p, closing.span(), SyntaxError::JSXExpectedClosingTagForLtGt);
|
||||
}
|
||||
(Either::Right(opening), Some(Either::Left(closing))) => {
|
||||
syntax_error!(
|
||||
p,
|
||||
closing.span(),
|
||||
SyntaxError::JSXExpectedClosingTag {
|
||||
tag: get_qualified_jsx_name(&opening.name)
|
||||
}
|
||||
);
|
||||
}
|
||||
(Either::Left(opening), Some(Either::Left(closing))) => Either::Left(JSXFragment {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing,
|
||||
}),
|
||||
(Either::Right(opening), None) => Either::Right(JSXElement {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing: None,
|
||||
}),
|
||||
(Either::Right(opening), Some(Either::Right(closing))) => {
|
||||
if get_qualified_jsx_name(&closing.name)
|
||||
!= get_qualified_jsx_name(&opening.name)
|
||||
{
|
||||
syntax_error!(
|
||||
p,
|
||||
closing.span(),
|
||||
SyntaxError::JSXExpectedClosingTag {
|
||||
tag: get_qualified_jsx_name(&opening.name)
|
||||
}
|
||||
);
|
||||
}
|
||||
Either::Right(JSXElement {
|
||||
span,
|
||||
opening,
|
||||
children,
|
||||
closing: Some(closing),
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses entire JSX element from current position.
|
||||
///
|
||||
/// babel: `jsxParseElement`
|
||||
pub(super) fn parse_jsx_element(&mut self) -> PResult<Either<JSXFragment, JSXElement>> {
|
||||
trace_cur!(self, parse_jsx_element);
|
||||
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
debug_assert!({ matches!(*cur!(self, true)?, Token::JSXTagStart | tok!('<')) });
|
||||
|
||||
let start_pos = cur_pos!(self);
|
||||
|
||||
self.parse_jsx_element_at(start_pos)
|
||||
}
|
||||
|
||||
pub(super) fn parse_jsx_text(&mut self) -> PResult<JSXText> {
|
||||
debug_assert!(self.input.syntax().jsx());
|
||||
debug_assert!(matches!(cur!(self, false), Ok(&Token::JSXText { .. })));
|
||||
let token = bump!(self);
|
||||
let span = self.input.prev_span();
|
||||
match token {
|
||||
Token::JSXText { raw } => Ok(JSXText {
|
||||
span,
|
||||
// TODO
|
||||
value: raw.clone(),
|
||||
raw,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait IsFragment {
|
||||
fn is_fragment(&self) -> bool;
|
||||
}
|
||||
|
||||
impl IsFragment for Either<JSXOpeningFragment, JSXOpeningElement> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
matches!(*self, Either::Left(..))
|
||||
}
|
||||
}
|
||||
|
||||
impl IsFragment for Either<JSXClosingFragment, JSXClosingElement> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
matches!(*self, Either::Left(..))
|
||||
}
|
||||
}
|
||||
impl<T: IsFragment> IsFragment for Option<T> {
|
||||
fn is_fragment(&self) -> bool {
|
||||
self.as_ref().map(|s| s.is_fragment()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_qualified_jsx_name(name: &JSXElementName) -> JsWord {
|
||||
fn get_qualified_obj_name(obj: &JSXObject) -> JsWord {
|
||||
match *obj {
|
||||
JSXObject::Ident(ref i) => i.sym.clone(),
|
||||
JSXObject::JSXMemberExpr(ref member) => format!(
|
||||
"{}.{}",
|
||||
get_qualified_obj_name(&member.obj),
|
||||
member.prop.sym
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
match *name {
|
||||
JSXElementName::Ident(ref i) => i.sym.clone(),
|
||||
JSXElementName::JSXNamespacedName(JSXNamespacedName { ref ns, ref name }) => {
|
||||
format!("{}:{}", ns.sym, name.sym).into()
|
||||
}
|
||||
JSXElementName::JSXMemberExpr(JSXMemberExpr { ref obj, ref prop }) => {
|
||||
format!("{}.{}", get_qualified_obj_name(obj), prop.sym).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
118
third-party/vendor/swc_ecma_parser/src/parser/jsx/tests.rs
vendored
Normal file
118
third-party/vendor/swc_ecma_parser/src/parser/jsx/tests.rs
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
use swc_common::DUMMY_SP as span;
|
||||
use swc_ecma_visit::assert_eq_ignore_span;
|
||||
|
||||
use super::*;
|
||||
use crate::parser::test_parser;
|
||||
|
||||
fn jsx(src: &'static str) -> Box<Expr> {
|
||||
test_parser(
|
||||
src,
|
||||
crate::Syntax::Es(crate::EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_expr(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn self_closing_01() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx("<a />"),
|
||||
Box::new(Expr::JSXElement(Box::new(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
self_closing: true,
|
||||
attrs: vec![],
|
||||
type_args: None,
|
||||
},
|
||||
children: vec![],
|
||||
closing: None,
|
||||
})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normal_01() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx("<a>foo</a>"),
|
||||
Box::new(Expr::JSXElement(Box::new(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
self_closing: false,
|
||||
attrs: vec![],
|
||||
type_args: None,
|
||||
},
|
||||
children: vec![JSXElementChild::JSXText(JSXText {
|
||||
span,
|
||||
raw: "foo".into(),
|
||||
value: "foo".into(),
|
||||
})],
|
||||
closing: Some(JSXClosingElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("a".into(), span)),
|
||||
})
|
||||
})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_in_attr() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx(r#"<div id="w < w" />;"#),
|
||||
Box::new(Expr::JSXElement(Box::new(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
attrs: vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
|
||||
span,
|
||||
name: JSXAttrName::Ident(Ident::new("id".into(), span)),
|
||||
value: Some(JSXAttrValue::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "w < w".into(),
|
||||
raw: Some("\"w < w\"".into()),
|
||||
}))),
|
||||
})],
|
||||
name: JSXElementName::Ident(Ident::new("div".into(), span)),
|
||||
self_closing: true,
|
||||
type_args: None,
|
||||
},
|
||||
children: vec![],
|
||||
closing: None
|
||||
})))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_584() {
|
||||
assert_eq_ignore_span!(
|
||||
jsx(r#"<test other={4} />;"#),
|
||||
Box::new(Expr::JSXElement(Box::new(JSXElement {
|
||||
span,
|
||||
opening: JSXOpeningElement {
|
||||
span,
|
||||
name: JSXElementName::Ident(Ident::new("test".into(), span)),
|
||||
attrs: vec![JSXAttrOrSpread::JSXAttr(JSXAttr {
|
||||
span,
|
||||
name: JSXAttrName::Ident(Ident::new("other".into(), span)),
|
||||
value: Some(JSXAttrValue::JSXExprContainer(JSXExprContainer {
|
||||
span,
|
||||
expr: JSXExpr::Expr(Box::new(Expr::Lit(Lit::Num(Number {
|
||||
span,
|
||||
value: 4.0,
|
||||
raw: Some("4".into())
|
||||
}))))
|
||||
})),
|
||||
})],
|
||||
self_closing: true,
|
||||
type_args: None,
|
||||
},
|
||||
children: vec![],
|
||||
closing: None
|
||||
})))
|
||||
);
|
||||
}
|
||||
394
third-party/vendor/swc_ecma_parser/src/parser/macros.rs
vendored
Normal file
394
third-party/vendor/swc_ecma_parser/src/parser/macros.rs
vendored
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
macro_rules! unexpected {
|
||||
($p:expr, $expected:literal) => {{
|
||||
let got = $p.input.dump_cur();
|
||||
syntax_error!(
|
||||
$p,
|
||||
$p.input.cur_span(),
|
||||
SyntaxError::Unexpected {
|
||||
got,
|
||||
expected: $expected
|
||||
}
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
/// This handles automatic semicolon insertion.
|
||||
///
|
||||
/// Returns bool.
|
||||
macro_rules! is {
|
||||
($p:expr, BindingIdent) => {{
|
||||
let ctx = $p.ctx();
|
||||
match cur!($p, false) {
|
||||
Ok(&Word(ref w)) => !ctx.is_reserved_word(&w.cow()),
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, IdentRef) => {{
|
||||
let ctx = $p.ctx();
|
||||
match cur!($p, false) {
|
||||
Ok(&Word(ref w)) => !ctx.is_reserved_word(&w.cow()),
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,IdentName) => {{
|
||||
match cur!($p, false) {
|
||||
Ok(&Word(..)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,Str) => {{
|
||||
match cur!($p, false) {
|
||||
Ok(&Token::Str { .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,Num) => {{
|
||||
match cur!($p, false) {
|
||||
Ok(&Token::Num { .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,Regex) => {{
|
||||
match cur!($p, false) {
|
||||
Ok(&Token::Regex { .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,BigInt) => {{
|
||||
match cur!($p, false) {
|
||||
Ok(&Token::BigInt { .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,';') => {{
|
||||
match $p.input.cur() {
|
||||
Some(&Token::Semi) | None | Some(&tok!('}')) => true,
|
||||
_ => $p.input.had_line_break_before_cur(),
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, $t:tt) => {
|
||||
is_exact!($p, $t)
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! peeked_is {
|
||||
($p:expr, BindingIdent) => {{
|
||||
let ctx = $p.ctx();
|
||||
match peek!($p) {
|
||||
Ok(&Word(ref w)) => !ctx.is_reserved_word(&w.cow()),
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, IdentRef) => {{
|
||||
let ctx = $p.ctx();
|
||||
match peek!($p) {
|
||||
Ok(&Word(ref w)) => !ctx.is_reserved_word(&w.cow()),
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr,IdentName) => {{
|
||||
match peek!($p) {
|
||||
Ok(&Word(..)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, JSXName) => {{
|
||||
match peek!($p) {
|
||||
Ok(&Token::JSXName { .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, ';') => {{
|
||||
compile_error!("peeked_is!(self, ';') is invalid");
|
||||
}};
|
||||
|
||||
($p:expr, $t:tt) => {
|
||||
match peek!($p).ok() {
|
||||
Some(&token_including_semi!($t)) => true,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true on eof.
|
||||
macro_rules! eof {
|
||||
($p:expr) => {
|
||||
cur!($p, false).is_err()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_one_of {
|
||||
($p:expr, $($t:tt),+) => {{
|
||||
false
|
||||
$(
|
||||
|| is!($p, $t)
|
||||
)*
|
||||
}};
|
||||
}
|
||||
|
||||
// This will panic if current != token
|
||||
macro_rules! assert_and_bump {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: &Token = &tok!($t);
|
||||
if cfg!(debug_assertions) && !is!($p, $t) {
|
||||
unreachable!(
|
||||
"assertion failed: expected {:?}, got {:?}",
|
||||
TOKEN,
|
||||
$p.input.cur()
|
||||
);
|
||||
}
|
||||
let _ = cur!($p, true)?;
|
||||
bump!($p);
|
||||
}};
|
||||
}
|
||||
|
||||
/// This handles automatic semicolon insertion.
|
||||
///
|
||||
/// Returns bool if token is static, and Option<Token>
|
||||
/// if token has data like string.
|
||||
macro_rules! eat {
|
||||
($p:expr, ';') => {{
|
||||
if cfg!(feature = "debug") {
|
||||
tracing::trace!("eat(';'): cur={:?}", cur!($p, false));
|
||||
}
|
||||
match $p.input.cur() {
|
||||
Some(&Token::Semi) => {
|
||||
$p.input.bump();
|
||||
true
|
||||
}
|
||||
None | Some(&tok!('}')) => true,
|
||||
_ => $p.input.had_line_break_before_cur(),
|
||||
}
|
||||
}};
|
||||
|
||||
($p:expr, $t:tt) => {{
|
||||
if is!($p, $t) {
|
||||
bump!($p);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! eat_exact {
|
||||
($p:expr, $t:tt) => {{
|
||||
if is_exact!($p, $t) {
|
||||
bump!($p);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! is_exact {
|
||||
($p:expr, $t:tt) => {{
|
||||
match $p.input.cur() {
|
||||
Some(&token_including_semi!($t)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// This handles automatic semicolon insertion.
|
||||
macro_rules! expect {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: &Token = &token_including_semi!($t);
|
||||
if !eat!($p, $t) {
|
||||
let cur = $p.input.dump_cur();
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN, cur))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! expect_exact {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: &Token = &token_including_semi!($t);
|
||||
if !eat_exact!($p, $t) {
|
||||
let cur = $p.input.dump_cur();
|
||||
syntax_error!($p, $p.input.cur_span(), SyntaxError::Expected(TOKEN, cur))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! store {
|
||||
($p:expr, $t:tt) => {{
|
||||
const TOKEN: Token = token_including_semi!($t);
|
||||
|
||||
$p.input.store(TOKEN);
|
||||
}};
|
||||
}
|
||||
|
||||
/// cur!($parser, required:bool)
|
||||
macro_rules! cur {
|
||||
($p:expr, $required:expr) => {{
|
||||
let pos = $p.input.last_pos();
|
||||
let last = Span::new(pos, pos, Default::default());
|
||||
let is_err_token = match $p.input.cur() {
|
||||
Some(&$crate::token::Token::Error(..)) => true,
|
||||
_ => false,
|
||||
};
|
||||
if is_err_token {
|
||||
match $p.input.bump() {
|
||||
$crate::token::Token::Error(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
match $p.input.cur() {
|
||||
Some(c) => Ok(c),
|
||||
None => {
|
||||
if $required {
|
||||
let err = crate::error::Error::new(last, crate::error::SyntaxError::Eof);
|
||||
return Err(err);
|
||||
}
|
||||
Err(crate::error::Error::new(
|
||||
last,
|
||||
crate::error::SyntaxError::Eof,
|
||||
))
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! peek {
|
||||
($p:expr) => {{
|
||||
debug_assert!(
|
||||
$p.input.knows_cur(),
|
||||
"parser should not call peek() without knowing current token.
|
||||
Current token is {:?}",
|
||||
cur!($p, false),
|
||||
);
|
||||
|
||||
let pos = cur_pos!($p);
|
||||
let last = Span::new(pos, pos, Default::default());
|
||||
match $p.input.peek() {
|
||||
Some(c) => Ok(c),
|
||||
None => {
|
||||
let err = crate::error::Error::new(last, crate::error::SyntaxError::Eof);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! bump {
|
||||
($p:expr) => {{
|
||||
debug_assert!(
|
||||
$p.input.knows_cur(),
|
||||
"parser should not call bump() without knowing current token"
|
||||
);
|
||||
$p.input.bump()
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! cur_pos {
|
||||
($p:expr) => {{
|
||||
$p.input.cur_pos()
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! last_pos {
|
||||
($p:expr) => {
|
||||
$p.input.prev_span().hi
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! return_if_arrow {
|
||||
($p:expr, $expr:expr) => {{
|
||||
// FIXME:
|
||||
//
|
||||
//
|
||||
|
||||
// let is_cur = match $p.state.potential_arrow_start {
|
||||
// Some(start) => $expr.span.lo() == start,
|
||||
// None => false
|
||||
// };
|
||||
// if is_cur {
|
||||
if let Expr::Arrow { .. } = *$expr {
|
||||
return Ok($expr);
|
||||
}
|
||||
// }
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! trace_cur {
|
||||
($p:expr, $name:ident) => {{
|
||||
if cfg!(feature = "debug") {
|
||||
tracing::debug!("{}: {:?}", stringify!($name), $p.input.cur());
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// This macro requires macro named 'last_pos' to be in scope.
|
||||
macro_rules! span {
|
||||
($p:expr, $start:expr) => {{
|
||||
let start: ::swc_common::BytePos = $start;
|
||||
let end: ::swc_common::BytePos = last_pos!($p);
|
||||
if cfg!(debug_assertions) && start > end {
|
||||
unreachable!(
|
||||
"assertion failed: (span.start <= span.end).
|
||||
start = {}, end = {}",
|
||||
start.0, end.0
|
||||
)
|
||||
}
|
||||
::swc_common::Span::new(start, end, ::swc_common::SyntaxContext::empty())
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! make_error {
|
||||
($p:expr, $span:expr, $err:expr) => {{
|
||||
crate::error::Error::new($span, $err)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! syntax_error {
|
||||
($p:expr, $err:expr) => {
|
||||
syntax_error!($p, $p.input.cur_span(), $err)
|
||||
};
|
||||
|
||||
($p:expr, $span:expr, $err:expr) => {{
|
||||
let err = make_error!($p, $span, $err);
|
||||
|
||||
if cfg!(feature = "debug") {
|
||||
tracing::error!(
|
||||
"Syntax error called from {}:{}:{}\nCurrent token = {:?}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
$p.input.cur()
|
||||
);
|
||||
}
|
||||
return Err(err.into());
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! debug_tracing {
|
||||
($p:expr, $name:tt) => {{
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
tracing::span!(
|
||||
tracing::Level::ERROR,
|
||||
$name,
|
||||
cur = tracing::field::debug(&$p.input.cur())
|
||||
)
|
||||
.entered()
|
||||
}
|
||||
}};
|
||||
}
|
||||
308
third-party/vendor/swc_ecma_parser/src/parser/mod.rs
vendored
Normal file
308
third-party/vendor/swc_ecma_parser/src/parser/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#![allow(clippy::let_unit_value)]
|
||||
#![deny(non_snake_case)]
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use swc_atoms::{Atom, JsWord};
|
||||
use swc_common::{collections::AHashMap, comments::Comments, input::StringInput, BytePos, Span};
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
pub use self::input::{Capturing, Tokens, TokensInput};
|
||||
use self::{input::Buffer, util::ParseObject};
|
||||
use crate::{
|
||||
error::SyntaxError,
|
||||
lexer::Lexer,
|
||||
token::{Token, Word},
|
||||
Context, EsVersion, Syntax, TsConfig,
|
||||
};
|
||||
#[cfg(test)]
|
||||
extern crate test;
|
||||
#[cfg(test)]
|
||||
use test::Bencher;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod class_and_fn;
|
||||
mod expr;
|
||||
mod ident;
|
||||
pub mod input;
|
||||
mod jsx;
|
||||
mod object;
|
||||
mod pat;
|
||||
mod stmt;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod typescript;
|
||||
mod util;
|
||||
|
||||
/// When error occurs, error is emitted and parser returns Err(()).
|
||||
pub type PResult<T> = Result<T, Error>;
|
||||
|
||||
/// EcmaScript parser.
|
||||
#[derive(Clone)]
|
||||
pub struct Parser<I: Tokens> {
|
||||
state: State,
|
||||
input: Buffer<I>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct State {
|
||||
labels: Vec<JsWord>,
|
||||
/// Start position of an assignment expression.
|
||||
potential_arrow_start: Option<BytePos>,
|
||||
|
||||
found_module_item: bool,
|
||||
/// Start position of an AST node and the span of its trailing comma.
|
||||
trailing_commas: AHashMap<BytePos, Span>,
|
||||
}
|
||||
|
||||
impl<'a> Parser<Lexer<'a>> {
|
||||
pub fn new(syntax: Syntax, input: StringInput<'a>, comments: Option<&'a dyn Comments>) -> Self {
|
||||
Self::new_from(Lexer::new(syntax, Default::default(), input, comments))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
pub fn new_from(mut input: I) -> Self {
|
||||
let in_declare = matches!(
|
||||
input.syntax(),
|
||||
Syntax::Typescript(TsConfig { dts: true, .. })
|
||||
);
|
||||
let ctx = Context {
|
||||
in_declare,
|
||||
..input.ctx()
|
||||
};
|
||||
input.set_ctx(ctx);
|
||||
|
||||
Parser {
|
||||
state: Default::default(),
|
||||
input: Buffer::new(input),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_errors(&mut self) -> Vec<Error> {
|
||||
self.input().take_errors()
|
||||
}
|
||||
|
||||
pub fn parse_script(&mut self) -> PResult<Script> {
|
||||
trace_cur!(self, parse_script);
|
||||
|
||||
let ctx = Context {
|
||||
module: false,
|
||||
..self.ctx()
|
||||
};
|
||||
self.set_ctx(ctx);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
|
||||
let shebang = self.parse_shebang()?;
|
||||
|
||||
self.parse_block_body(true, true, None).map(|body| Script {
|
||||
span: span!(self, start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_typescript_module(&mut self) -> PResult<Module> {
|
||||
trace_cur!(self, parse_typescript_module);
|
||||
|
||||
debug_assert!(self.syntax().typescript());
|
||||
|
||||
//TODO: parse() -> PResult<Program>
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
strict: false,
|
||||
..self.ctx()
|
||||
};
|
||||
// Module code is always in strict mode
|
||||
self.set_ctx(ctx);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
let shebang = self.parse_shebang()?;
|
||||
|
||||
self.parse_block_body(true, true, None).map(|body| Module {
|
||||
span: span!(self, start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns [Module] if it's a module and returns [Script] if it's not a
|
||||
/// module.
|
||||
///
|
||||
/// Note: This is not perfect yet. It means, some strict mode violations may
|
||||
/// not be reported even if the method returns [Module].
|
||||
pub fn parse_program(&mut self) -> PResult<Program> {
|
||||
let start = cur_pos!(self);
|
||||
let shebang = self.parse_shebang()?;
|
||||
let ctx = Context {
|
||||
can_be_module: true,
|
||||
..self.ctx()
|
||||
};
|
||||
|
||||
let body: Vec<ModuleItem> = self.with_ctx(ctx).parse_block_body(true, true, None)?;
|
||||
let has_module_item = self.state.found_module_item
|
||||
|| body
|
||||
.iter()
|
||||
.any(|item| matches!(item, ModuleItem::ModuleDecl(..)));
|
||||
if has_module_item && !self.ctx().module {
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
can_be_module: true,
|
||||
strict: true,
|
||||
..self.ctx()
|
||||
};
|
||||
// Emit buffered strict mode / module code violations
|
||||
self.input.set_ctx(ctx);
|
||||
}
|
||||
|
||||
Ok(if has_module_item {
|
||||
Program::Module(Module {
|
||||
span: span!(self, start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
} else {
|
||||
let body = body
|
||||
.into_iter()
|
||||
.map(|item| match item {
|
||||
ModuleItem::ModuleDecl(_) => unreachable!("Module is handled above"),
|
||||
ModuleItem::Stmt(stmt) => stmt,
|
||||
})
|
||||
.collect();
|
||||
Program::Script(Script {
|
||||
span: span!(self, start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_module(&mut self) -> PResult<Module> {
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
can_be_module: true,
|
||||
strict: true,
|
||||
..self.ctx()
|
||||
};
|
||||
// Module code is always in strict mode
|
||||
self.set_ctx(ctx);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
let shebang = self.parse_shebang()?;
|
||||
|
||||
self.parse_block_body(true, true, None).map(|body| Module {
|
||||
span: span!(self, start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_shebang(&mut self) -> PResult<Option<Atom>> {
|
||||
match cur!(self, false) {
|
||||
Ok(&Token::Shebang(..)) => match bump!(self) {
|
||||
Token::Shebang(v) => Ok(Some(v)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn ctx(&self) -> Context {
|
||||
self.input.get_ctx()
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn emit_err(&self, span: Span, error: SyntaxError) {
|
||||
if self.ctx().ignore_error || !self.syntax().early_errors() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.emit_error(Error::new(span, error))
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn emit_error(&self, error: Error) {
|
||||
if self.ctx().ignore_error || !self.syntax().early_errors() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.input_ref().add_error(error);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn emit_strict_mode_err(&self, span: Span, error: SyntaxError) {
|
||||
if self.ctx().ignore_error {
|
||||
return;
|
||||
}
|
||||
let error = Error::new(span, error);
|
||||
self.input_ref().add_module_mode_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_parser<F, Ret>(s: &'static str, syntax: Syntax, f: F) -> Ret
|
||||
where
|
||||
F: FnOnce(&mut Parser<Lexer>) -> Result<Ret, Error>,
|
||||
{
|
||||
crate::with_test_sess(s, |handler, input| {
|
||||
let lexer = Lexer::new(syntax, EsVersion::Es2019, input, None);
|
||||
let mut p = Parser::new_from(lexer);
|
||||
let ret = f(&mut p);
|
||||
let mut error = false;
|
||||
|
||||
for err in p.take_errors() {
|
||||
error = true;
|
||||
err.into_diagnostic(handler).emit();
|
||||
}
|
||||
|
||||
let res = ret.map_err(|err| err.into_diagnostic(handler).emit())?;
|
||||
|
||||
if error {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
})
|
||||
.unwrap_or_else(|output| panic!("test_parser(): failed to parse \n{}\n{}", s, output))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_parser_comment<F, Ret>(c: &dyn Comments, s: &'static str, syntax: Syntax, f: F) -> Ret
|
||||
where
|
||||
F: FnOnce(&mut Parser<Lexer>) -> Result<Ret, Error>,
|
||||
{
|
||||
crate::with_test_sess(s, |handler, input| {
|
||||
let lexer = Lexer::new(syntax, EsVersion::Es2019, input, Some(&c));
|
||||
let mut p = Parser::new_from(lexer);
|
||||
let ret = f(&mut p);
|
||||
|
||||
for err in p.take_errors() {
|
||||
err.into_diagnostic(handler).emit();
|
||||
}
|
||||
|
||||
ret.map_err(|err| err.into_diagnostic(handler).emit())
|
||||
})
|
||||
.unwrap_or_else(|output| panic!("test_parser(): failed to parse \n{}\n{}", s, output))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn bench_parser<F>(b: &mut Bencher, s: &'static str, syntax: Syntax, mut f: F)
|
||||
where
|
||||
F: for<'a> FnMut(&'a mut Parser<Lexer<'a>>) -> PResult<()>,
|
||||
{
|
||||
b.bytes = s.len() as u64;
|
||||
|
||||
let _ = crate::with_test_sess(s, |handler, input| {
|
||||
b.iter(|| {
|
||||
let lexer = Lexer::new(syntax, Default::default(), input.clone(), None);
|
||||
let _ =
|
||||
f(&mut Parser::new_from(lexer)).map_err(|err| err.into_diagnostic(handler).emit());
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
490
third-party/vendor/swc_ecma_parser/src/parser/object.rs
vendored
Normal file
490
third-party/vendor/swc_ecma_parser/src/parser/object.rs
vendored
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
//! Parser for object literal.
|
||||
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::Spanned;
|
||||
|
||||
use super::*;
|
||||
use crate::parser::class_and_fn::is_not_this;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
/// Parse a object literal or object pattern.
|
||||
pub(super) fn parse_object<T>(&mut self) -> PResult<T>
|
||||
where
|
||||
Self: ParseObject<T>,
|
||||
{
|
||||
let ctx = Context {
|
||||
will_expect_colon_for_cond: false,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx).parse_with(|p| {
|
||||
trace_cur!(p, parse_object);
|
||||
|
||||
let start = cur_pos!(p);
|
||||
let mut trailing_comma = None;
|
||||
assert_and_bump!(p, '{');
|
||||
|
||||
let mut props = vec![];
|
||||
|
||||
while !eat!(p, '}') {
|
||||
props.push(p.parse_object_prop()?);
|
||||
|
||||
if !is!(p, '}') {
|
||||
expect!(p, ',');
|
||||
if is!(p, '}') {
|
||||
trailing_comma = Some(p.input.prev_span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.make_object(span!(p, start), props, trailing_comma)
|
||||
})
|
||||
}
|
||||
|
||||
/// spec: 'PropertyName'
|
||||
pub(super) fn parse_prop_name(&mut self) -> PResult<PropName> {
|
||||
trace_cur!(self, parse_prop_name);
|
||||
|
||||
let ctx = self.ctx();
|
||||
self.with_ctx(Context {
|
||||
in_property_name: true,
|
||||
..ctx
|
||||
})
|
||||
.parse_with(|p| {
|
||||
let start = cur_pos!(p);
|
||||
|
||||
let v = match *cur!(p, true)? {
|
||||
Token::Str { .. } => match bump!(p) {
|
||||
Token::Str { value, raw } => PropName::Str(Str {
|
||||
span: span!(p, start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Token::Num { .. } => match bump!(p) {
|
||||
Token::Num { value, raw } => PropName::Num(Number {
|
||||
span: span!(p, start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Token::BigInt { .. } => match bump!(p) {
|
||||
Token::BigInt { value, raw } => PropName::BigInt(BigInt {
|
||||
span: span!(p, start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Word(..) => match bump!(p) {
|
||||
Word(w) => PropName::Ident(Ident::new(w.into(), span!(p, start))),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
tok!('[') => {
|
||||
bump!(p);
|
||||
let inner_start = cur_pos!(p);
|
||||
|
||||
let mut expr = p.include_in_expr(true).parse_assignment_expr()?;
|
||||
|
||||
if p.syntax().typescript() && is!(p, ',') {
|
||||
let mut exprs = vec![expr];
|
||||
|
||||
while eat!(p, ',') {
|
||||
exprs.push(p.include_in_expr(true).parse_assignment_expr()?);
|
||||
}
|
||||
|
||||
p.emit_err(span!(p, inner_start), SyntaxError::TS1171);
|
||||
|
||||
expr = Box::new(
|
||||
SeqExpr {
|
||||
span: span!(p, inner_start),
|
||||
exprs,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
expect!(p, ']');
|
||||
|
||||
PropName::Computed(ComputedPropName {
|
||||
span: span!(p, start),
|
||||
expr,
|
||||
})
|
||||
}
|
||||
_ => unexpected!(
|
||||
p,
|
||||
"identifier, string literal, numeric literal or [ for the computed key"
|
||||
),
|
||||
};
|
||||
|
||||
Ok(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> ParseObject<Box<Expr>> for Parser<I> {
|
||||
type Prop = PropOrSpread;
|
||||
|
||||
fn make_object(
|
||||
&mut self,
|
||||
span: Span,
|
||||
props: Vec<Self::Prop>,
|
||||
trailing_comma: Option<Span>,
|
||||
) -> PResult<Box<Expr>> {
|
||||
if let Some(trailing_comma) = trailing_comma {
|
||||
self.state.trailing_commas.insert(span.lo, trailing_comma);
|
||||
}
|
||||
Ok(Box::new(Expr::Object(ObjectLit { span, props })))
|
||||
}
|
||||
|
||||
/// spec: 'PropertyDefinition'
|
||||
fn parse_object_prop(&mut self) -> PResult<Self::Prop> {
|
||||
trace_cur!(self, parse_object_prop);
|
||||
|
||||
let start = cur_pos!(self);
|
||||
// Parse as 'MethodDefinition'
|
||||
|
||||
if eat!(self, "...") {
|
||||
// spread element
|
||||
let dot3_token = span!(self, start);
|
||||
|
||||
let expr = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
|
||||
return Ok(PropOrSpread::Spread(SpreadElement { dot3_token, expr }));
|
||||
}
|
||||
|
||||
if eat!(self, '*') {
|
||||
let name = self.parse_prop_name()?;
|
||||
return self
|
||||
.with_ctx(Context {
|
||||
allow_direct_super: true,
|
||||
in_class_field: false,
|
||||
..self.ctx()
|
||||
})
|
||||
.parse_fn_args_body(
|
||||
// no decorator in an object literal
|
||||
vec![],
|
||||
start,
|
||||
|p| p.parse_unique_formal_params(),
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.map(|function| {
|
||||
PropOrSpread::Prop(Box::new(Prop::Method(MethodProp {
|
||||
key: name,
|
||||
function,
|
||||
})))
|
||||
});
|
||||
}
|
||||
|
||||
let has_modifiers = self.eat_any_ts_modifier()?;
|
||||
let modifiers_span = self.input.prev_span();
|
||||
|
||||
let key = self.parse_prop_name()?;
|
||||
|
||||
if self.input.syntax().typescript()
|
||||
&& !is_one_of!(self, '(', '[', ':', ',', '?', '=', '*', IdentName, Str, Num)
|
||||
&& !(self.input.syntax().typescript() && is!(self, '<'))
|
||||
&& !(is!(self, '}') && matches!(key, PropName::Ident(..)))
|
||||
{
|
||||
trace_cur!(self, parse_object_prop_error);
|
||||
|
||||
self.emit_err(self.input.cur_span(), SyntaxError::TS1005);
|
||||
return Ok(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key,
|
||||
value: Box::new(Expr::Invalid(Invalid {
|
||||
span: span!(self, start),
|
||||
})),
|
||||
}))));
|
||||
}
|
||||
//
|
||||
// {[computed()]: a,}
|
||||
// { 'a': a, }
|
||||
// { 0: 1, }
|
||||
// { a: expr, }
|
||||
if eat!(self, ':') {
|
||||
let value = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
return Ok(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
|
||||
key,
|
||||
value,
|
||||
}))));
|
||||
}
|
||||
|
||||
// Handle `a(){}` (and async(){} / get(){} / set(){})
|
||||
if (self.input.syntax().typescript() && is!(self, '<')) || is!(self, '(') {
|
||||
return self
|
||||
.with_ctx(Context {
|
||||
allow_direct_super: true,
|
||||
in_class_field: false,
|
||||
..self.ctx()
|
||||
})
|
||||
.parse_fn_args_body(
|
||||
// no decorator in an object literal
|
||||
vec![],
|
||||
start,
|
||||
|p| p.parse_unique_formal_params(),
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.map(|function| Box::new(Prop::Method(MethodProp { key, function })))
|
||||
.map(PropOrSpread::Prop);
|
||||
}
|
||||
|
||||
let ident = match key {
|
||||
PropName::Ident(ident) => ident,
|
||||
// TODO
|
||||
_ => unexpected!(self, "identifier"),
|
||||
};
|
||||
|
||||
if eat!(self, '?') {
|
||||
self.emit_err(self.input.prev_span(), SyntaxError::TS1162);
|
||||
}
|
||||
|
||||
// `ident` from parse_prop_name is parsed as 'IdentifierName'
|
||||
// It means we should check for invalid expressions like { for, }
|
||||
if is_one_of!(self, '=', ',', '}') {
|
||||
let is_reserved_word = { self.ctx().is_reserved_word(&ident.sym) };
|
||||
if is_reserved_word {
|
||||
self.emit_err(ident.span, SyntaxError::ReservedWordInObjShorthandOrPat);
|
||||
}
|
||||
|
||||
if eat!(self, '=') {
|
||||
let value = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
return Ok(PropOrSpread::Prop(Box::new(Prop::Assign(AssignProp {
|
||||
key: ident,
|
||||
value,
|
||||
}))));
|
||||
}
|
||||
|
||||
return Ok(PropOrSpread::Prop(Box::new(Prop::from(ident))));
|
||||
}
|
||||
|
||||
// get a(){}
|
||||
// set a(v){}
|
||||
// async a(){}
|
||||
|
||||
match ident.sym {
|
||||
js_word!("get") | js_word!("set") | js_word!("async") => {
|
||||
trace_cur!(self, parse_object_prop__after_accessor);
|
||||
|
||||
if has_modifiers {
|
||||
self.emit_err(modifiers_span, SyntaxError::TS1042);
|
||||
}
|
||||
|
||||
let is_generator = ident.sym == js_word!("async") && eat!(self, '*');
|
||||
let key = self.parse_prop_name()?;
|
||||
let key_span = key.span();
|
||||
self.with_ctx(Context {
|
||||
allow_direct_super: true,
|
||||
in_class_field: false,
|
||||
..self.ctx()
|
||||
})
|
||||
.parse_with(|parser| {
|
||||
match ident.sym {
|
||||
js_word!("get") => parser
|
||||
.parse_fn_args_body(
|
||||
// no decorator in an object literal
|
||||
vec![],
|
||||
start,
|
||||
|p| {
|
||||
let params = p.parse_formal_params()?;
|
||||
|
||||
if params.iter().filter(|p| is_not_this(p)).count() != 0 {
|
||||
p.emit_err(key_span, SyntaxError::GetterParam);
|
||||
}
|
||||
|
||||
Ok(params)
|
||||
},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.map(|v| *v)
|
||||
.map(
|
||||
|Function {
|
||||
body, return_type, ..
|
||||
}| {
|
||||
if parser.input.syntax().typescript()
|
||||
&& parser.input.target() == EsVersion::Es3
|
||||
{
|
||||
parser.emit_err(key_span, SyntaxError::TS1056);
|
||||
}
|
||||
|
||||
PropOrSpread::Prop(Box::new(Prop::Getter(GetterProp {
|
||||
span: span!(parser, start),
|
||||
key,
|
||||
type_ann: return_type,
|
||||
body,
|
||||
})))
|
||||
},
|
||||
),
|
||||
js_word!("set") => {
|
||||
parser
|
||||
.parse_fn_args_body(
|
||||
// no decorator in an object literal
|
||||
vec![],
|
||||
start,
|
||||
|p| {
|
||||
let params = p.parse_formal_params()?;
|
||||
|
||||
if params.iter().filter(|p| is_not_this(p)).count() != 1 {
|
||||
p.emit_err(key_span, SyntaxError::SetterParam);
|
||||
}
|
||||
|
||||
if !params.is_empty() {
|
||||
if let Pat::Rest(..) = params[0].pat {
|
||||
p.emit_err(
|
||||
params[0].span(),
|
||||
SyntaxError::RestPatInSetter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if p.input.syntax().typescript()
|
||||
&& p.input.target() == EsVersion::Es3
|
||||
{
|
||||
p.emit_err(key_span, SyntaxError::TS1056);
|
||||
}
|
||||
|
||||
Ok(params)
|
||||
},
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.map(|v| *v)
|
||||
.map(|Function { params, body, .. }| {
|
||||
// debug_assert_eq!(params.len(), 1);
|
||||
PropOrSpread::Prop(Box::new(Prop::Setter(SetterProp {
|
||||
span: span!(parser, start),
|
||||
key,
|
||||
body,
|
||||
param: Box::new(
|
||||
params.into_iter().map(|p| p.pat).next().unwrap_or(
|
||||
Pat::Invalid(Invalid { span: key_span }),
|
||||
),
|
||||
),
|
||||
})))
|
||||
})
|
||||
}
|
||||
js_word!("async") => parser
|
||||
.parse_fn_args_body(
|
||||
// no decorator in an object literal
|
||||
vec![],
|
||||
start,
|
||||
|p| p.parse_unique_formal_params(),
|
||||
true,
|
||||
is_generator,
|
||||
)
|
||||
.map(|function| {
|
||||
PropOrSpread::Prop(Box::new(Prop::Method(MethodProp {
|
||||
key,
|
||||
function,
|
||||
})))
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
if self.input.syntax().typescript() {
|
||||
unexpected!(
|
||||
self,
|
||||
"... , *, (, [, :, , ?, =, an identifier, public, protected, private, \
|
||||
readonly, <."
|
||||
)
|
||||
} else {
|
||||
unexpected!(self, "... , *, (, [, :, , ?, = or an identifier")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> ParseObject<Pat> for Parser<I> {
|
||||
type Prop = ObjectPatProp;
|
||||
|
||||
fn make_object(
|
||||
&mut self,
|
||||
span: Span,
|
||||
props: Vec<Self::Prop>,
|
||||
trailing_comma: Option<Span>,
|
||||
) -> PResult<Pat> {
|
||||
let len = props.len();
|
||||
for (i, p) in props.iter().enumerate() {
|
||||
if i == len - 1 {
|
||||
if let ObjectPatProp::Rest(ref rest) = p {
|
||||
match *rest.arg {
|
||||
Pat::Ident(..) => {
|
||||
if let Some(trailing_comma) = trailing_comma {
|
||||
self.emit_err(trailing_comma, SyntaxError::CommaAfterRestElement);
|
||||
}
|
||||
}
|
||||
_ => syntax_error!(self, p.span(), SyntaxError::DotsWithoutIdentifier),
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if let ObjectPatProp::Rest(..) = p {
|
||||
self.emit_err(p.span(), SyntaxError::NonLastRestParam)
|
||||
}
|
||||
}
|
||||
|
||||
let optional = (self.input.syntax().dts() || self.ctx().in_declare) && eat!(self, '?');
|
||||
|
||||
Ok(Pat::Object(ObjectPat {
|
||||
span,
|
||||
props,
|
||||
optional,
|
||||
type_ann: None,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Production 'BindingProperty'
|
||||
fn parse_object_prop(&mut self) -> PResult<Self::Prop> {
|
||||
let start = cur_pos!(self);
|
||||
|
||||
if eat!(self, "...") {
|
||||
// spread element
|
||||
let dot3_token = span!(self, start);
|
||||
|
||||
let arg = Box::new(self.parse_binding_pat_or_ident()?);
|
||||
|
||||
return Ok(ObjectPatProp::Rest(RestPat {
|
||||
span: span!(self, start),
|
||||
dot3_token,
|
||||
arg,
|
||||
type_ann: None,
|
||||
}));
|
||||
}
|
||||
|
||||
let key = self.parse_prop_name()?;
|
||||
if eat!(self, ':') {
|
||||
let value = Box::new(self.parse_binding_element()?);
|
||||
|
||||
return Ok(ObjectPatProp::KeyValue(KeyValuePatProp { key, value }));
|
||||
}
|
||||
let key = match key {
|
||||
PropName::Ident(ident) => ident,
|
||||
_ => unexpected!(self, "an identifier"),
|
||||
};
|
||||
|
||||
let value = if eat!(self, '=') {
|
||||
self.include_in_expr(true)
|
||||
.parse_assignment_expr()
|
||||
.map(Some)?
|
||||
} else {
|
||||
if self.ctx().is_reserved_word(&key.sym) {
|
||||
self.emit_err(key.span, SyntaxError::ReservedWordInObjShorthandOrPat);
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ObjectPatProp::Assign(AssignPatProp {
|
||||
span: span!(self, start),
|
||||
key,
|
||||
value,
|
||||
}))
|
||||
}
|
||||
}
|
||||
1224
third-party/vendor/swc_ecma_parser/src/parser/pat.rs
vendored
Normal file
1224
third-party/vendor/swc_ecma_parser/src/parser/pat.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
2567
third-party/vendor/swc_ecma_parser/src/parser/stmt.rs
vendored
Normal file
2567
third-party/vendor/swc_ecma_parser/src/parser/stmt.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
884
third-party/vendor/swc_ecma_parser/src/parser/stmt/module_item.rs
vendored
Normal file
884
third-party/vendor/swc_ecma_parser/src/parser/stmt/module_item.rs
vendored
Normal file
|
|
@ -0,0 +1,884 @@
|
|||
use super::*;
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
fn parse_import(&mut self) -> PResult<ModuleItem> {
|
||||
let start = cur_pos!(self);
|
||||
|
||||
if peeked_is!(self, '.') {
|
||||
let expr = self.parse_expr()?;
|
||||
|
||||
eat!(self, ';');
|
||||
|
||||
return Ok(Stmt::Expr(ExprStmt {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
})
|
||||
.into());
|
||||
}
|
||||
|
||||
if peeked_is!(self, '(') {
|
||||
let expr = self.parse_expr()?;
|
||||
|
||||
eat!(self, ';');
|
||||
|
||||
return Ok(Stmt::Expr(ExprStmt {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
})
|
||||
.into());
|
||||
}
|
||||
|
||||
// It's now import statement
|
||||
|
||||
if !self.ctx().module {
|
||||
// Switch to module mode
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
strict: true,
|
||||
..self.ctx()
|
||||
};
|
||||
self.set_ctx(ctx);
|
||||
}
|
||||
|
||||
expect!(self, "import");
|
||||
|
||||
if self.input.syntax().typescript() && is!(self, IdentRef) && peeked_is!(self, '=') {
|
||||
return self
|
||||
.parse_ts_import_equals_decl(
|
||||
start, /* is_export */ false, /* is_type_only */ false,
|
||||
)
|
||||
.map(ModuleDecl::from)
|
||||
.map(ModuleItem::from);
|
||||
}
|
||||
|
||||
// Handle import 'mod.js'
|
||||
let str_start = cur_pos!(self);
|
||||
if let Ok(&Token::Str { .. }) = cur!(self, false) {
|
||||
let src = match bump!(self) {
|
||||
Token::Str { value, raw, .. } => Box::new(Str {
|
||||
span: span!(self, str_start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let _ = cur!(self, false);
|
||||
let with = if self.input.syntax().import_attributes()
|
||||
&& !self.input.had_line_break_before_cur()
|
||||
&& (eat!(self, "assert") || eat!(self, "with"))
|
||||
{
|
||||
match *self.parse_object::<Box<Expr>>()? {
|
||||
Expr::Object(v) => Some(Box::new(v)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
expect!(self, ';');
|
||||
return Ok(ModuleDecl::Import(ImportDecl {
|
||||
span: span!(self, start),
|
||||
src,
|
||||
specifiers: vec![],
|
||||
type_only: false,
|
||||
with,
|
||||
}))
|
||||
.map(ModuleItem::from);
|
||||
}
|
||||
|
||||
let type_only = self.input.syntax().typescript()
|
||||
&& is!(self, "type")
|
||||
&& (peeked_is!(self, '{') || !peeked_is!(self, "from") && !peeked_is!(self, ','));
|
||||
|
||||
if type_only {
|
||||
assert_and_bump!(self, "type");
|
||||
|
||||
if is!(self, IdentRef) && peeked_is!(self, '=') {
|
||||
return self
|
||||
.parse_ts_import_equals_decl(
|
||||
start, /* is_export */ false, /* is_type_only */ true,
|
||||
)
|
||||
.map(ModuleDecl::from)
|
||||
.map(ModuleItem::from);
|
||||
}
|
||||
}
|
||||
|
||||
let mut specifiers = vec![];
|
||||
|
||||
if is!(self, BindingIdent) {
|
||||
let local = self.parse_imported_default_binding()?;
|
||||
//TODO: Better error reporting
|
||||
if !is!(self, "from") {
|
||||
expect!(self, ',');
|
||||
}
|
||||
specifiers.push(ImportSpecifier::Default(ImportDefaultSpecifier {
|
||||
span: local.span,
|
||||
local,
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
let import_spec_start = cur_pos!(self);
|
||||
if eat!(self, '*') {
|
||||
expect!(self, "as");
|
||||
let local = self.parse_imported_binding()?;
|
||||
specifiers.push(ImportSpecifier::Namespace(ImportStarAsSpecifier {
|
||||
span: span!(self, import_spec_start),
|
||||
local,
|
||||
}));
|
||||
} else if eat!(self, '{') {
|
||||
while !eof!(self) && !is!(self, '}') {
|
||||
specifiers.push(self.parse_import_specifier(type_only)?);
|
||||
|
||||
if is!(self, '}') {
|
||||
break;
|
||||
} else {
|
||||
expect!(self, ',');
|
||||
}
|
||||
}
|
||||
expect!(self, '}');
|
||||
}
|
||||
}
|
||||
|
||||
let src = {
|
||||
expect!(self, "from");
|
||||
let str_start = cur_pos!(self);
|
||||
|
||||
match *cur!(self, true)? {
|
||||
Token::Str { .. } => match bump!(self) {
|
||||
Token::Str { value, raw, .. } => Box::new(Str {
|
||||
span: span!(self, str_start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unexpected!(self, "a string literal"),
|
||||
}
|
||||
};
|
||||
|
||||
let _ = cur!(self, false);
|
||||
let with = if self.input.syntax().import_attributes()
|
||||
&& !self.input.had_line_break_before_cur()
|
||||
&& (eat!(self, "assert") || eat!(self, "with"))
|
||||
{
|
||||
match *self.parse_object::<Box<Expr>>()? {
|
||||
Expr::Object(v) => Some(Box::new(v)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
expect!(self, ';');
|
||||
|
||||
Ok(ModuleDecl::Import(ImportDecl {
|
||||
span: span!(self, start),
|
||||
specifiers,
|
||||
src,
|
||||
type_only,
|
||||
with,
|
||||
}))
|
||||
.map(ModuleItem::from)
|
||||
}
|
||||
|
||||
/// Parse `foo`, `foo2 as bar` in `import { foo, foo2 as bar }`
|
||||
fn parse_import_specifier(&mut self, type_only: bool) -> PResult<ImportSpecifier> {
|
||||
let start = cur_pos!(self);
|
||||
match self.parse_module_export_name()? {
|
||||
ModuleExportName::Ident(mut orig_name) => {
|
||||
let mut is_type_only = false;
|
||||
// Handle:
|
||||
// `import { type xx } from 'mod'`
|
||||
// `import { type xx as yy } from 'mod'`
|
||||
// `import { type as } from 'mod'`
|
||||
// `import { type as as } from 'mod'`
|
||||
// `import { type as as as } from 'mod'`
|
||||
if self.syntax().typescript()
|
||||
&& orig_name.sym == js_word!("type")
|
||||
&& is!(self, IdentName)
|
||||
{
|
||||
let possibly_orig_name = self.parse_ident_name()?;
|
||||
if possibly_orig_name.sym == js_word!("as") {
|
||||
// `import { type as } from 'mod'`
|
||||
if !is!(self, IdentName) {
|
||||
if self.ctx().is_reserved_word(&possibly_orig_name.sym) {
|
||||
syntax_error!(
|
||||
self,
|
||||
possibly_orig_name.span,
|
||||
SyntaxError::ReservedWordInImport
|
||||
)
|
||||
}
|
||||
|
||||
if type_only {
|
||||
self.emit_err(orig_name.span, SyntaxError::TS2206);
|
||||
}
|
||||
|
||||
return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: span!(self, start),
|
||||
local: possibly_orig_name,
|
||||
imported: None,
|
||||
is_type_only: true,
|
||||
}));
|
||||
}
|
||||
|
||||
let maybe_as = self.parse_binding_ident()?.id;
|
||||
if maybe_as.sym == js_word!("as") {
|
||||
if is!(self, IdentName) {
|
||||
// `import { type as as as } from 'mod'`
|
||||
// `import { type as as foo } from 'mod'`
|
||||
let local = self.parse_binding_ident()?.id;
|
||||
|
||||
if type_only {
|
||||
self.emit_err(orig_name.span, SyntaxError::TS2206);
|
||||
}
|
||||
|
||||
return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: Span::new(start, orig_name.span.hi(), Default::default()),
|
||||
local,
|
||||
imported: Some(ModuleExportName::Ident(possibly_orig_name)),
|
||||
is_type_only: true,
|
||||
}));
|
||||
} else {
|
||||
// `import { type as as } from 'mod'`
|
||||
return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: Span::new(start, maybe_as.span.hi(), Default::default()),
|
||||
local: maybe_as,
|
||||
imported: Some(ModuleExportName::Ident(orig_name)),
|
||||
is_type_only: false,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// `import { type as xxx } from 'mod'`
|
||||
return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: Span::new(start, orig_name.span.hi(), Default::default()),
|
||||
local: maybe_as,
|
||||
imported: Some(ModuleExportName::Ident(orig_name)),
|
||||
is_type_only: false,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// `import { type xx } from 'mod'`
|
||||
// `import { type xx as yy } from 'mod'`
|
||||
if type_only {
|
||||
self.emit_err(orig_name.span, SyntaxError::TS2206);
|
||||
}
|
||||
|
||||
orig_name = possibly_orig_name;
|
||||
is_type_only = true;
|
||||
}
|
||||
}
|
||||
|
||||
if eat!(self, "as") {
|
||||
let local = self.parse_binding_ident()?.id;
|
||||
return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: Span::new(start, local.span.hi(), Default::default()),
|
||||
local,
|
||||
imported: Some(ModuleExportName::Ident(orig_name)),
|
||||
is_type_only,
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle difference between
|
||||
//
|
||||
// 'ImportedBinding'
|
||||
// 'IdentifierName' as 'ImportedBinding'
|
||||
if self.ctx().is_reserved_word(&orig_name.sym) {
|
||||
syntax_error!(self, orig_name.span, SyntaxError::ReservedWordInImport)
|
||||
}
|
||||
|
||||
let local = orig_name;
|
||||
Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: span!(self, start),
|
||||
local,
|
||||
imported: None,
|
||||
is_type_only,
|
||||
}))
|
||||
}
|
||||
ModuleExportName::Str(orig_str) => {
|
||||
if eat!(self, "as") {
|
||||
let local = self.parse_binding_ident()?.id;
|
||||
Ok(ImportSpecifier::Named(ImportNamedSpecifier {
|
||||
span: Span::new(start, local.span.hi(), Default::default()),
|
||||
local,
|
||||
imported: Some(ModuleExportName::Str(orig_str)),
|
||||
is_type_only: false,
|
||||
}))
|
||||
} else {
|
||||
syntax_error!(
|
||||
self,
|
||||
orig_str.span,
|
||||
SyntaxError::ImportBindingIsString(orig_str.value)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_imported_default_binding(&mut self) -> PResult<Ident> {
|
||||
self.parse_imported_binding()
|
||||
}
|
||||
|
||||
fn parse_imported_binding(&mut self) -> PResult<Ident> {
|
||||
let ctx = Context {
|
||||
in_async: false,
|
||||
in_generator: false,
|
||||
..self.ctx()
|
||||
};
|
||||
Ok(self.with_ctx(ctx).parse_binding_ident()?.id)
|
||||
}
|
||||
|
||||
fn parse_export(&mut self, mut decorators: Vec<Decorator>) -> PResult<ModuleDecl> {
|
||||
if !self.ctx().module {
|
||||
// Switch to module mode
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
strict: true,
|
||||
..self.ctx()
|
||||
};
|
||||
self.set_ctx(ctx);
|
||||
}
|
||||
|
||||
let start = cur_pos!(self);
|
||||
assert_and_bump!(self, "export");
|
||||
let _ = cur!(self, true);
|
||||
let after_export_start = cur_pos!(self);
|
||||
|
||||
// "export declare" is equivalent to just "export".
|
||||
let declare = self.input.syntax().typescript() && eat!(self, "declare");
|
||||
|
||||
if declare {
|
||||
// TODO: Remove
|
||||
if let Some(decl) = self.try_parse_ts_declare(after_export_start, decorators.clone())? {
|
||||
return Ok(ModuleDecl::ExportDecl(ExportDecl {
|
||||
span: span!(self, start),
|
||||
decl,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if self.input.syntax().typescript() && is!(self, IdentName) {
|
||||
let sym = match *cur!(self, true)? {
|
||||
Token::Word(ref w) => w.clone().into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// TODO: remove clone
|
||||
if let Some(decl) = self.try_parse_ts_export_decl(decorators.clone(), sym) {
|
||||
return Ok(ModuleDecl::ExportDecl(ExportDecl {
|
||||
span: span!(self, start),
|
||||
decl,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if self.input.syntax().typescript() {
|
||||
if eat!(self, "import") {
|
||||
let is_type_only = is!(self, "type") && peeked_is!(self, IdentRef);
|
||||
|
||||
if is_type_only {
|
||||
assert_and_bump!(self, "type");
|
||||
}
|
||||
|
||||
// export import A = B
|
||||
return self
|
||||
.parse_ts_import_equals_decl(start, /* is_export */ true, is_type_only)
|
||||
.map(From::from);
|
||||
}
|
||||
|
||||
if eat!(self, '=') {
|
||||
// `export = x;`
|
||||
let expr = self.parse_expr()?;
|
||||
expect!(self, ';');
|
||||
return Ok(TsExportAssignment {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
if eat!(self, "as") {
|
||||
// `export as namespace A;`
|
||||
// See `parseNamespaceExportDeclaration` in TypeScript's own parser
|
||||
expect!(self, "namespace");
|
||||
let id = self.parse_ident(false, false)?;
|
||||
expect!(self, ';');
|
||||
return Ok(TsNamespaceExportDecl {
|
||||
span: span!(self, start),
|
||||
id,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
let ns_export_specifier_start = cur_pos!(self);
|
||||
|
||||
let type_only = self.input.syntax().typescript() && eat!(self, "type");
|
||||
|
||||
// Some("default") if default is exported from 'src'
|
||||
let mut export_default = None;
|
||||
|
||||
if !type_only && eat!(self, "default") {
|
||||
if is!(self, '@') {
|
||||
let start = cur_pos!(self);
|
||||
let after_decorators = self.parse_decorators(false)?;
|
||||
|
||||
if !decorators.is_empty() {
|
||||
syntax_error!(self, span!(self, start), SyntaxError::TS8038);
|
||||
}
|
||||
|
||||
decorators = after_decorators;
|
||||
}
|
||||
|
||||
if self.input.syntax().typescript() {
|
||||
if is!(self, "abstract")
|
||||
&& peeked_is!(self, "class")
|
||||
&& !self.input.has_linebreak_between_cur_and_peeked()
|
||||
{
|
||||
let class_start = cur_pos!(self);
|
||||
assert_and_bump!(self, "abstract");
|
||||
let _ = cur!(self, true);
|
||||
|
||||
return self
|
||||
.parse_default_class(start, class_start, decorators, true)
|
||||
.map(ModuleDecl::ExportDefaultDecl);
|
||||
}
|
||||
if is!(self, "abstract") && peeked_is!(self, "interface") {
|
||||
self.emit_err(self.input.cur_span(), SyntaxError::TS1242);
|
||||
assert_and_bump!(self, "abstract");
|
||||
}
|
||||
|
||||
if is!(self, "interface") {
|
||||
let interface_start = cur_pos!(self);
|
||||
assert_and_bump!(self, "interface");
|
||||
let decl = self
|
||||
.parse_ts_interface_decl(interface_start)
|
||||
.map(DefaultDecl::from)?;
|
||||
return Ok(ExportDefaultDecl {
|
||||
span: span!(self, start),
|
||||
decl,
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
if is!(self, "class") {
|
||||
let class_start = cur_pos!(self);
|
||||
let decl = self.parse_default_class(start, class_start, decorators, false)?;
|
||||
return Ok(ModuleDecl::ExportDefaultDecl(decl));
|
||||
} else if is!(self, "async")
|
||||
&& peeked_is!(self, "function")
|
||||
&& !self.input.has_linebreak_between_cur_and_peeked()
|
||||
{
|
||||
let decl = self.parse_default_async_fn(start, decorators)?;
|
||||
return Ok(ModuleDecl::ExportDefaultDecl(decl));
|
||||
} else if is!(self, "function") {
|
||||
let decl = self.parse_default_fn(start, decorators)?;
|
||||
return Ok(ModuleDecl::ExportDefaultDecl(decl));
|
||||
} else if self.input.syntax().export_default_from()
|
||||
&& (is!(self, "from")
|
||||
|| (is!(self, ',') && (peeked_is!(self, '{') || peeked_is!(self, '*'))))
|
||||
{
|
||||
export_default = Some(Ident::new("default".into(), self.input.prev_span()))
|
||||
} else {
|
||||
let expr = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
expect!(self, ';');
|
||||
return Ok(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
|
||||
span: span!(self, start),
|
||||
expr,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if is!(self, '@') {
|
||||
let start = cur_pos!(self);
|
||||
let after_decorators = self.parse_decorators(false)?;
|
||||
|
||||
if !decorators.is_empty() {
|
||||
syntax_error!(self, span!(self, start), SyntaxError::TS8038);
|
||||
}
|
||||
|
||||
decorators = after_decorators;
|
||||
}
|
||||
|
||||
let decl = if !type_only && is!(self, "class") {
|
||||
let class_start = cur_pos!(self);
|
||||
self.parse_class_decl(start, class_start, decorators, false)?
|
||||
} else if !type_only
|
||||
&& is!(self, "async")
|
||||
&& peeked_is!(self, "function")
|
||||
&& !self.input.has_linebreak_between_cur_and_peeked()
|
||||
{
|
||||
self.parse_async_fn_decl(decorators)?
|
||||
} else if !type_only && is!(self, "function") {
|
||||
self.parse_fn_decl(decorators)?
|
||||
} else if !type_only
|
||||
&& self.input.syntax().typescript()
|
||||
&& is!(self, "const")
|
||||
&& peeked_is!(self, "enum")
|
||||
{
|
||||
let enum_start = cur_pos!(self);
|
||||
assert_and_bump!(self, "const");
|
||||
let _ = cur!(self, true);
|
||||
assert_and_bump!(self, "enum");
|
||||
return self
|
||||
.parse_ts_enum_decl(enum_start, /* is_const */ true)
|
||||
.map(Decl::from)
|
||||
.map(|decl| {
|
||||
ModuleDecl::ExportDecl(ExportDecl {
|
||||
span: span!(self, start),
|
||||
decl,
|
||||
})
|
||||
});
|
||||
} else if !type_only
|
||||
&& (is!(self, "var")
|
||||
|| is!(self, "const")
|
||||
|| (is!(self, "let"))
|
||||
&& peek!(self)
|
||||
.map(|t| {
|
||||
// module code is always in strict mode.
|
||||
t.follows_keyword_let(true)
|
||||
})
|
||||
.unwrap_or(false))
|
||||
{
|
||||
self.parse_var_stmt(false).map(Decl::Var)?
|
||||
} else {
|
||||
// ```javascript
|
||||
// export foo, * as bar, { baz } from "mod"; // *
|
||||
// export * as bar, { baz } from "mod"; // *
|
||||
// export foo, { baz } from "mod"; // *
|
||||
// export foo, * as bar from "mod"; // *
|
||||
// export foo from "mod"; // *
|
||||
// export * as bar from "mod"; //
|
||||
// export { baz } from "mod"; //
|
||||
// export { baz } ; //
|
||||
// export * from "mod"; //
|
||||
// ```
|
||||
|
||||
// export default
|
||||
// export foo
|
||||
let default = match export_default {
|
||||
Some(default) => Some(default),
|
||||
None => {
|
||||
if self.input.syntax().export_default_from() && is!(self, IdentName) {
|
||||
Some(self.parse_ident(false, false)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if default.is_none() && is!(self, '*') && !peeked_is!(self, "as") {
|
||||
assert_and_bump!(self, '*');
|
||||
|
||||
// improve error message for `export * from foo`
|
||||
let (src, with) = self.parse_from_clause_and_semi()?;
|
||||
return Ok(ModuleDecl::ExportAll(ExportAll {
|
||||
span: span!(self, start),
|
||||
src,
|
||||
type_only,
|
||||
with,
|
||||
}));
|
||||
}
|
||||
|
||||
let mut specifiers = vec![];
|
||||
|
||||
let mut has_default = false;
|
||||
let mut has_ns = false;
|
||||
|
||||
if let Some(default) = default {
|
||||
has_default = true;
|
||||
|
||||
specifiers.push(ExportSpecifier::Default(ExportDefaultSpecifier {
|
||||
exported: default,
|
||||
}))
|
||||
}
|
||||
|
||||
// export foo, * as bar
|
||||
// ^
|
||||
if !specifiers.is_empty() && is!(self, ',') && peeked_is!(self, '*') {
|
||||
assert_and_bump!(self, ',');
|
||||
|
||||
has_ns = true;
|
||||
}
|
||||
// export * as bar
|
||||
// ^
|
||||
else if specifiers.is_empty() && is!(self, '*') {
|
||||
has_ns = true;
|
||||
}
|
||||
|
||||
if has_ns {
|
||||
assert_and_bump!(self, '*');
|
||||
expect!(self, "as");
|
||||
let name = self.parse_module_export_name()?;
|
||||
specifiers.push(ExportSpecifier::Namespace(ExportNamespaceSpecifier {
|
||||
span: span!(self, ns_export_specifier_start),
|
||||
name,
|
||||
}));
|
||||
}
|
||||
|
||||
if has_default || has_ns {
|
||||
if is!(self, "from") {
|
||||
let (src, with) = self.parse_from_clause_and_semi()?;
|
||||
return Ok(ModuleDecl::ExportNamed(NamedExport {
|
||||
span: span!(self, start),
|
||||
specifiers,
|
||||
src: Some(src),
|
||||
type_only,
|
||||
with,
|
||||
}));
|
||||
} else if !self.input.syntax().export_default_from() {
|
||||
// emit error
|
||||
expect!(self, "from");
|
||||
}
|
||||
|
||||
expect!(self, ',');
|
||||
}
|
||||
|
||||
expect!(self, '{');
|
||||
|
||||
let mut string_export_binding_span = None;
|
||||
while !eof!(self) && !is!(self, '}') {
|
||||
let specifier = self.parse_named_export_specifier(type_only)?;
|
||||
if let ModuleExportName::Str(str_export) = &specifier.orig {
|
||||
string_export_binding_span = Some(str_export.span);
|
||||
}
|
||||
specifiers.push(ExportSpecifier::Named(specifier));
|
||||
|
||||
if is!(self, '}') {
|
||||
break;
|
||||
} else {
|
||||
expect!(self, ',');
|
||||
}
|
||||
}
|
||||
expect!(self, '}');
|
||||
|
||||
let opt = if is!(self, "from") {
|
||||
Some(self.parse_from_clause_and_semi()?)
|
||||
} else {
|
||||
eat!(self, ';');
|
||||
if has_default || has_ns {
|
||||
syntax_error!(
|
||||
self,
|
||||
span!(self, start),
|
||||
SyntaxError::ExportDefaultWithOutFrom
|
||||
);
|
||||
}
|
||||
if let Some(span) = string_export_binding_span {
|
||||
syntax_error!(self, span, SyntaxError::ExportBindingIsString);
|
||||
}
|
||||
None
|
||||
};
|
||||
let (src, with) = match opt {
|
||||
Some(v) => (Some(v.0), v.1),
|
||||
None => (None, None),
|
||||
};
|
||||
return Ok(ModuleDecl::ExportNamed(NamedExport {
|
||||
span: span!(self, start),
|
||||
specifiers,
|
||||
src,
|
||||
type_only,
|
||||
with,
|
||||
}));
|
||||
};
|
||||
|
||||
Ok(ModuleDecl::ExportDecl(ExportDecl {
|
||||
span: span!(self, start),
|
||||
decl,
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_named_export_specifier(&mut self, type_only: bool) -> PResult<ExportNamedSpecifier> {
|
||||
let start = cur_pos!(self);
|
||||
|
||||
let mut is_type_only = false;
|
||||
|
||||
let orig = match self.parse_module_export_name()? {
|
||||
ModuleExportName::Ident(orig_ident) => {
|
||||
// Handle:
|
||||
// `export { type xx }`
|
||||
// `export { type xx as yy }`
|
||||
// `export { type as }`
|
||||
// `export { type as as }`
|
||||
// `export { type as as as }`
|
||||
if self.syntax().typescript()
|
||||
&& orig_ident.sym == js_word!("type")
|
||||
&& is!(self, IdentName)
|
||||
{
|
||||
let possibly_orig = self.parse_ident_name()?;
|
||||
if possibly_orig.sym == js_word!("as") {
|
||||
// `export { type as }`
|
||||
if !is!(self, IdentName) {
|
||||
if type_only {
|
||||
self.emit_err(orig_ident.span, SyntaxError::TS2207);
|
||||
}
|
||||
|
||||
return Ok(ExportNamedSpecifier {
|
||||
span: span!(self, start),
|
||||
orig: ModuleExportName::Ident(possibly_orig),
|
||||
exported: None,
|
||||
is_type_only: true,
|
||||
});
|
||||
}
|
||||
|
||||
let maybe_as = self.parse_ident_name()?;
|
||||
if maybe_as.sym == js_word!("as") {
|
||||
if is!(self, IdentName) {
|
||||
// `export { type as as as }`
|
||||
// `export { type as as foo }`
|
||||
let exported = self.parse_ident_name()?;
|
||||
|
||||
if type_only {
|
||||
self.emit_err(orig_ident.span, SyntaxError::TS2207);
|
||||
}
|
||||
|
||||
return Ok(ExportNamedSpecifier {
|
||||
span: Span::new(
|
||||
start,
|
||||
orig_ident.span.hi(),
|
||||
Default::default(),
|
||||
),
|
||||
orig: ModuleExportName::Ident(possibly_orig),
|
||||
exported: Some(ModuleExportName::Ident(exported)),
|
||||
is_type_only: true,
|
||||
});
|
||||
} else {
|
||||
// `export { type as as }`
|
||||
return Ok(ExportNamedSpecifier {
|
||||
span: Span::new(
|
||||
start,
|
||||
orig_ident.span.hi(),
|
||||
Default::default(),
|
||||
),
|
||||
orig: ModuleExportName::Ident(orig_ident),
|
||||
exported: Some(ModuleExportName::Ident(maybe_as)),
|
||||
is_type_only: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// `export { type as xxx }`
|
||||
return Ok(ExportNamedSpecifier {
|
||||
span: Span::new(start, orig_ident.span.hi(), Default::default()),
|
||||
orig: ModuleExportName::Ident(orig_ident),
|
||||
exported: Some(ModuleExportName::Ident(maybe_as)),
|
||||
is_type_only: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// `export { type xx }`
|
||||
// `export { type xx as yy }`
|
||||
if type_only {
|
||||
self.emit_err(orig_ident.span, SyntaxError::TS2207);
|
||||
}
|
||||
|
||||
is_type_only = true;
|
||||
ModuleExportName::Ident(possibly_orig)
|
||||
}
|
||||
} else {
|
||||
ModuleExportName::Ident(orig_ident)
|
||||
}
|
||||
}
|
||||
module_export_name => module_export_name,
|
||||
};
|
||||
|
||||
let exported = if eat!(self, "as") {
|
||||
Some(self.parse_module_export_name()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ExportNamedSpecifier {
|
||||
span: span!(self, start),
|
||||
orig,
|
||||
exported,
|
||||
is_type_only,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses `from 'foo.js' with {};` or `from 'foo.js' assert {};`
|
||||
fn parse_from_clause_and_semi(&mut self) -> PResult<(Box<Str>, Option<Box<ObjectLit>>)> {
|
||||
expect!(self, "from");
|
||||
|
||||
let str_start = cur_pos!(self);
|
||||
let src = match *cur!(self, true)? {
|
||||
Token::Str { .. } => match bump!(self) {
|
||||
Token::Str { value, raw, .. } => Box::new(Str {
|
||||
span: span!(self, str_start),
|
||||
value,
|
||||
raw: Some(raw),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unexpected!(self, "a string literal"),
|
||||
};
|
||||
let _ = cur!(self, false);
|
||||
let with = if self.input.syntax().import_attributes()
|
||||
&& !self.input.had_line_break_before_cur()
|
||||
&& (eat!(self, "assert") || eat!(self, "with"))
|
||||
{
|
||||
match *self.parse_object::<Box<Expr>>()? {
|
||||
Expr::Object(v) => Some(Box::new(v)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
expect!(self, ';');
|
||||
Ok((src, with))
|
||||
}
|
||||
}
|
||||
|
||||
impl IsDirective for ModuleItem {
|
||||
fn as_ref(&self) -> Option<&Stmt> {
|
||||
match *self {
|
||||
ModuleItem::Stmt(ref s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Tokens> StmtLikeParser<'a, ModuleItem> for Parser<I> {
|
||||
fn handle_import_export(
|
||||
&mut self,
|
||||
top_level: bool,
|
||||
decorators: Vec<Decorator>,
|
||||
) -> PResult<ModuleItem> {
|
||||
if !top_level {
|
||||
syntax_error!(self, SyntaxError::NonTopLevelImportExport);
|
||||
}
|
||||
|
||||
let decl = if is!(self, "import") {
|
||||
self.parse_import()?
|
||||
} else if is!(self, "export") {
|
||||
self.parse_export(decorators).map(ModuleItem::from)?
|
||||
} else {
|
||||
unreachable!(
|
||||
"handle_import_export should not be called if current token isn't import nor \
|
||||
export"
|
||||
)
|
||||
};
|
||||
|
||||
Ok(decl)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{EsConfig, Syntax};
|
||||
|
||||
#[test]
|
||||
fn test_legacy_decorator() {
|
||||
crate::test_parser(
|
||||
"@foo
|
||||
export default class Foo {
|
||||
bar() {
|
||||
class Baz {}
|
||||
}
|
||||
}",
|
||||
Syntax::Es(EsConfig {
|
||||
decorators: true,
|
||||
decorators_before_export: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
}
|
||||
}
|
||||
350
third-party/vendor/swc_ecma_parser/src/parser/tests.rs
vendored
Normal file
350
third-party/vendor/swc_ecma_parser/src/parser/tests.rs
vendored
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
use swc_common::{comments::SingleThreadedComments, BytePos};
|
||||
|
||||
use super::*;
|
||||
use crate::{test_parser, EsConfig, TsConfig};
|
||||
|
||||
fn program(src: &'static str) -> Program {
|
||||
test_parser(src, Default::default(), |p| p.parse_program())
|
||||
}
|
||||
|
||||
/// Assert that Parser.parse_program returns [Program::Module].
|
||||
fn module(src: &'static str) -> Module {
|
||||
program(src).expect_module()
|
||||
}
|
||||
|
||||
/// Assert that Parser.parse_program returns [Program::Script].
|
||||
fn script(src: &'static str) -> Script {
|
||||
program(src).expect_script()
|
||||
}
|
||||
|
||||
/// Assert that Parser.parse_program returns [Program::Module] and has errors.
|
||||
#[track_caller]
|
||||
fn assert_module_error(src: &'static str) -> Module {
|
||||
test_parser(src, Default::default(), |p| {
|
||||
let program = p.parse_program()?;
|
||||
|
||||
let errors = p.take_errors();
|
||||
assert_ne!(errors, vec![]);
|
||||
|
||||
let module = program.expect_module();
|
||||
|
||||
Ok(module)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_program_module_01() {
|
||||
module("import 'foo';");
|
||||
module("export const a = 1;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_program_script_01() {
|
||||
script("let a = 5;");
|
||||
script("function foo() {}");
|
||||
script("const a = 00176;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_program_module_02() {
|
||||
module(
|
||||
"
|
||||
function foo() {}
|
||||
export default foo;
|
||||
",
|
||||
);
|
||||
module(
|
||||
"
|
||||
export function foo() {}
|
||||
export default foo;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_program_module_error_01() {
|
||||
assert_module_error(
|
||||
"
|
||||
const a = 01234;
|
||||
export default a;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_1813() {
|
||||
test_parser(
|
||||
"\\u{cccccccccsccccccQcXt[uc(~).const[uctor().const[uctor())tbr())",
|
||||
Default::default(),
|
||||
|p| {
|
||||
p.parse_program().expect_err("should fail");
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_module_export_named_span() {
|
||||
let m = module("export function foo() {}");
|
||||
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { span, .. })) = &m.body[0] {
|
||||
assert_eq!(span.lo, BytePos(1));
|
||||
} else {
|
||||
panic!("expected ExportDecl");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_module_export_default_fn_span() {
|
||||
let m = module("export default function foo() {}");
|
||||
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
|
||||
span, ..
|
||||
})) = &m.body[0]
|
||||
{
|
||||
assert_eq!(span.lo, BytePos(1));
|
||||
assert_eq!(span.hi, BytePos(33));
|
||||
} else {
|
||||
panic!("expected ExportDefaultDecl");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_module_export_default_async_fn_span() {
|
||||
let m = module("export default async function foo() {}");
|
||||
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
|
||||
span, ..
|
||||
})) = &m.body[0]
|
||||
{
|
||||
assert_eq!(span.lo, BytePos(1));
|
||||
assert_eq!(span.hi, BytePos(39));
|
||||
} else {
|
||||
panic!("expected ExportDefaultDecl");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_module_export_default_class_span() {
|
||||
let m = module("export default class Foo {}");
|
||||
if let ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
|
||||
span, ..
|
||||
})) = &m.body[0]
|
||||
{
|
||||
assert_eq!(span.lo, BytePos(1));
|
||||
assert_eq!(span.hi, BytePos(28));
|
||||
} else {
|
||||
panic!("expected ExportDefaultDecl");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_1878() {
|
||||
// file with only comments should have the comments
|
||||
// in the leading map instead of the trailing
|
||||
{
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "
|
||||
// test
|
||||
";
|
||||
let _ = super::test_parser_comment(&c, s, Syntax::Typescript(Default::default()), |p| {
|
||||
p.parse_typescript_module()
|
||||
});
|
||||
|
||||
let (leading, trailing) = c.take_all();
|
||||
assert!(trailing.borrow().is_empty());
|
||||
assert_eq!(leading.borrow().len(), 1);
|
||||
assert!(leading.borrow().get(&BytePos(1)).is_some());
|
||||
}
|
||||
|
||||
// file with shebang and comments should still work with the comments trailing
|
||||
// the shebang
|
||||
{
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "#!/foo/bar
|
||||
// test
|
||||
";
|
||||
let _ = super::test_parser_comment(&c, s, Syntax::Typescript(Default::default()), |p| {
|
||||
p.parse_typescript_module()
|
||||
});
|
||||
|
||||
let (leading, trailing) = c.take_all();
|
||||
assert!(leading.borrow().is_empty());
|
||||
assert_eq!(trailing.borrow().len(), 1);
|
||||
assert!(trailing.borrow().get(&BytePos(11)).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2264_1() {
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "
|
||||
const t = <Switch>
|
||||
// 1
|
||||
/* 2 */
|
||||
</Switch>
|
||||
";
|
||||
let _ = super::test_parser_comment(
|
||||
&c,
|
||||
s,
|
||||
Syntax::Typescript(TsConfig {
|
||||
tsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_typescript_module(),
|
||||
);
|
||||
|
||||
let (_leading, trailing) = c.take_all();
|
||||
// assert!(leading.borrow().is_empty());
|
||||
assert!(trailing.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2264_2() {
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "
|
||||
const t = <Switch>
|
||||
// 1
|
||||
/* 2 */
|
||||
</Switch>
|
||||
";
|
||||
let _ = super::test_parser_comment(
|
||||
&c,
|
||||
s,
|
||||
Syntax::Es(EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_module(),
|
||||
);
|
||||
|
||||
let (leading, trailing) = c.take_all();
|
||||
assert!(leading.borrow().is_empty());
|
||||
assert!(trailing.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2264_3() {
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "const foo = <h1>/* no */{/* 1 */ bar /* 2 */}/* no */</h1>;";
|
||||
let _ = super::test_parser_comment(
|
||||
&c,
|
||||
s,
|
||||
Syntax::Typescript(TsConfig {
|
||||
tsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_typescript_module(),
|
||||
);
|
||||
|
||||
let (leading, trailing) = c.take_all();
|
||||
assert!(leading.borrow().is_empty());
|
||||
assert_eq!(trailing.borrow().len(), 2);
|
||||
assert_eq!(trailing.borrow().get(&BytePos(26)).unwrap().len(), 1);
|
||||
assert_eq!(trailing.borrow().get(&BytePos(37)).unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2339_1() {
|
||||
let c = SingleThreadedComments::default();
|
||||
let s = "
|
||||
const t = <T>() => {
|
||||
// 1
|
||||
/* 2 */
|
||||
test;
|
||||
};
|
||||
";
|
||||
let _ = super::test_parser_comment(
|
||||
&c,
|
||||
s,
|
||||
Syntax::Typescript(TsConfig {
|
||||
tsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
|p| p.parse_typescript_module(),
|
||||
);
|
||||
|
||||
let (leading, trailing) = c.take_all();
|
||||
assert_eq!(leading.borrow().len(), 1);
|
||||
assert_eq!(leading.borrow().get(&BytePos(80)).unwrap().len(), 2);
|
||||
assert!(trailing.borrow().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2853_1() {
|
||||
test_parser("const a = \"\\0a\";", Default::default(), |p| {
|
||||
let program = p.parse_program()?;
|
||||
|
||||
let errors = p.take_errors();
|
||||
assert_eq!(errors, vec![]);
|
||||
assert_eq!(errors, vec![]);
|
||||
|
||||
Ok(program)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2853_2() {
|
||||
test_parser("const a = \"\u{0000}a\";", Default::default(), |p| {
|
||||
let program = p.parse_program()?;
|
||||
|
||||
let errors = p.take_errors();
|
||||
assert_eq!(errors, vec![]);
|
||||
|
||||
Ok(program)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn illegal_language_mode_directive1() {
|
||||
test_parser(
|
||||
r#"function f(a = 0) { "use strict"; }"#,
|
||||
Default::default(),
|
||||
|p| {
|
||||
let program = p.parse_program()?;
|
||||
|
||||
let errors = p.take_errors();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![Error::new(
|
||||
Span {
|
||||
lo: BytePos(21),
|
||||
hi: BytePos(34),
|
||||
ctxt: swc_common::SyntaxContext::empty()
|
||||
},
|
||||
crate::parser::SyntaxError::IllegalLanguageModeDirective
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(program)
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn illegal_language_mode_directive2() {
|
||||
test_parser(
|
||||
r#"let f = (a = 0) => { "use strict"; }"#,
|
||||
Default::default(),
|
||||
|p| {
|
||||
let program = p.parse_program()?;
|
||||
|
||||
let errors = p.take_errors();
|
||||
assert_eq!(
|
||||
errors,
|
||||
vec![Error::new(
|
||||
Span {
|
||||
lo: BytePos(22),
|
||||
hi: BytePos(35),
|
||||
ctxt: swc_common::SyntaxContext::empty()
|
||||
},
|
||||
crate::parser::SyntaxError::IllegalLanguageModeDirective
|
||||
)]
|
||||
);
|
||||
|
||||
Ok(program)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_non_strict_for_loop() {
|
||||
script("for (var v1 = 1 in v3) {}");
|
||||
}
|
||||
2969
third-party/vendor/swc_ecma_parser/src/parser/typescript.rs
vendored
Normal file
2969
third-party/vendor/swc_ecma_parser/src/parser/typescript.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
338
third-party/vendor/swc_ecma_parser/src/parser/util.rs
vendored
Normal file
338
third-party/vendor/swc_ecma_parser/src/parser/util.rs
vendored
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
use swc_atoms::js_word;
|
||||
|
||||
use super::*;
|
||||
use crate::token::Keyword;
|
||||
|
||||
impl Context {
|
||||
pub(crate) fn is_reserved(self, word: &Word) -> bool {
|
||||
match *word {
|
||||
Word::Keyword(Keyword::Let) => self.strict,
|
||||
Word::Keyword(Keyword::Await) => self.in_async || self.strict,
|
||||
Word::Keyword(Keyword::Yield) => self.in_generator || self.strict,
|
||||
|
||||
Word::Null
|
||||
| Word::True
|
||||
| Word::False
|
||||
| Word::Keyword(Keyword::Break)
|
||||
| Word::Keyword(Keyword::Case)
|
||||
| Word::Keyword(Keyword::Catch)
|
||||
| Word::Keyword(Keyword::Continue)
|
||||
| Word::Keyword(Keyword::Debugger)
|
||||
| Word::Keyword(Keyword::Default_)
|
||||
| Word::Keyword(Keyword::Do)
|
||||
| Word::Keyword(Keyword::Export)
|
||||
| Word::Keyword(Keyword::Else)
|
||||
| Word::Keyword(Keyword::Finally)
|
||||
| Word::Keyword(Keyword::For)
|
||||
| Word::Keyword(Keyword::Function)
|
||||
| Word::Keyword(Keyword::If)
|
||||
| Word::Keyword(Keyword::Return)
|
||||
| Word::Keyword(Keyword::Switch)
|
||||
| Word::Keyword(Keyword::Throw)
|
||||
| Word::Keyword(Keyword::Try)
|
||||
| Word::Keyword(Keyword::Var)
|
||||
| Word::Keyword(Keyword::Const)
|
||||
| Word::Keyword(Keyword::While)
|
||||
| Word::Keyword(Keyword::With)
|
||||
| Word::Keyword(Keyword::New)
|
||||
| Word::Keyword(Keyword::This)
|
||||
| Word::Keyword(Keyword::Super)
|
||||
| Word::Keyword(Keyword::Class)
|
||||
| Word::Keyword(Keyword::Extends)
|
||||
| Word::Keyword(Keyword::Import)
|
||||
| Word::Keyword(Keyword::In)
|
||||
| Word::Keyword(Keyword::InstanceOf)
|
||||
| Word::Keyword(Keyword::TypeOf)
|
||||
| Word::Keyword(Keyword::Void)
|
||||
| Word::Keyword(Keyword::Delete) => true,
|
||||
|
||||
// Future reserved word
|
||||
Word::Ident(js_word!("enum")) => true,
|
||||
|
||||
Word::Ident(js_word!("implements"))
|
||||
| Word::Ident(js_word!("package"))
|
||||
| Word::Ident(js_word!("protected"))
|
||||
| Word::Ident(js_word!("interface"))
|
||||
| Word::Ident(js_word!("private"))
|
||||
| Word::Ident(js_word!("public"))
|
||||
if self.strict =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reserved_word(self, word: &JsWord) -> bool {
|
||||
match *word {
|
||||
js_word!("let") => self.strict,
|
||||
// SyntaxError in the module only, not in the strict.
|
||||
// ```JavaScript
|
||||
// function foo() {
|
||||
// "use strict";
|
||||
// let await = 1;
|
||||
// }
|
||||
// ```
|
||||
js_word!("await") => self.in_async || self.module,
|
||||
js_word!("yield") => self.in_generator || self.strict,
|
||||
|
||||
js_word!("null")
|
||||
| js_word!("true")
|
||||
| js_word!("false")
|
||||
| js_word!("break")
|
||||
| js_word!("case")
|
||||
| js_word!("catch")
|
||||
| js_word!("continue")
|
||||
| js_word!("debugger")
|
||||
| js_word!("default")
|
||||
| js_word!("do")
|
||||
| js_word!("export")
|
||||
| js_word!("else")
|
||||
| js_word!("finally")
|
||||
| js_word!("for")
|
||||
| js_word!("function")
|
||||
| js_word!("if")
|
||||
| js_word!("return")
|
||||
| js_word!("switch")
|
||||
| js_word!("throw")
|
||||
| js_word!("try")
|
||||
| js_word!("var")
|
||||
| js_word!("const")
|
||||
| js_word!("while")
|
||||
| js_word!("with")
|
||||
| js_word!("new")
|
||||
| js_word!("this")
|
||||
| js_word!("super")
|
||||
| js_word!("class")
|
||||
| js_word!("extends")
|
||||
| js_word!("import")
|
||||
| js_word!("in")
|
||||
| js_word!("instanceof")
|
||||
| js_word!("typeof")
|
||||
| js_word!("void")
|
||||
| js_word!("delete") => true,
|
||||
|
||||
// Future reserved word
|
||||
js_word!("enum") => true,
|
||||
|
||||
js_word!("implements")
|
||||
| js_word!("package")
|
||||
| js_word!("protected")
|
||||
| js_word!("interface")
|
||||
| js_word!("private")
|
||||
| js_word!("public")
|
||||
if self.strict =>
|
||||
{
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Tokens> Parser<I> {
|
||||
/// Original context is restored when returned guard is dropped.
|
||||
pub(super) fn with_ctx(&mut self, ctx: Context) -> WithCtx<I> {
|
||||
let orig_ctx = self.ctx();
|
||||
self.set_ctx(ctx);
|
||||
WithCtx {
|
||||
orig_ctx,
|
||||
inner: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Original state is restored when returned guard is dropped.
|
||||
pub(super) fn with_state(&mut self, state: State) -> WithState<I> {
|
||||
let orig_state = std::mem::replace(&mut self.state, state);
|
||||
WithState {
|
||||
orig_state,
|
||||
inner: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_ctx(&mut self, ctx: Context) {
|
||||
self.input.set_ctx(ctx);
|
||||
}
|
||||
|
||||
pub(super) fn strict_mode(&mut self) -> WithCtx<I> {
|
||||
let ctx = Context {
|
||||
strict: true,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx)
|
||||
}
|
||||
|
||||
/// Original context is restored when returned guard is dropped.
|
||||
pub(super) fn in_type(&mut self) -> WithCtx<I> {
|
||||
let ctx = Context {
|
||||
in_type: true,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx)
|
||||
}
|
||||
|
||||
/// Original context is restored when returned guard is dropped.
|
||||
pub(super) fn include_in_expr(&mut self, include_in_expr: bool) -> WithCtx<I> {
|
||||
let ctx = Context {
|
||||
include_in_expr,
|
||||
..self.ctx()
|
||||
};
|
||||
self.with_ctx(ctx)
|
||||
}
|
||||
|
||||
/// Parse with given closure
|
||||
#[inline(always)]
|
||||
pub(super) fn parse_with<F, Ret>(&mut self, f: F) -> PResult<Ret>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> PResult<Ret>,
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
pub(super) fn syntax(&self) -> Syntax {
|
||||
self.input.syntax()
|
||||
}
|
||||
}
|
||||
pub trait ParseObject<Obj> {
|
||||
type Prop;
|
||||
fn make_object(
|
||||
&mut self,
|
||||
span: Span,
|
||||
props: Vec<Self::Prop>,
|
||||
trailing_comma: Option<Span>,
|
||||
) -> PResult<Obj>;
|
||||
fn parse_object_prop(&mut self) -> PResult<Self::Prop>;
|
||||
}
|
||||
|
||||
pub struct WithState<'w, I: 'w + Tokens> {
|
||||
inner: &'w mut Parser<I>,
|
||||
orig_state: State,
|
||||
}
|
||||
impl<'w, I: Tokens> Deref for WithState<'w, I> {
|
||||
type Target = Parser<I>;
|
||||
|
||||
fn deref(&self) -> &Parser<I> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
impl<'w, I: Tokens> DerefMut for WithState<'w, I> {
|
||||
fn deref_mut(&mut self) -> &mut Parser<I> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
impl<'w, I: Tokens> Drop for WithState<'w, I> {
|
||||
fn drop(&mut self) {
|
||||
std::mem::swap(&mut self.inner.state, &mut self.orig_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WithCtx<'w, I: 'w + Tokens> {
|
||||
inner: &'w mut Parser<I>,
|
||||
orig_ctx: Context,
|
||||
}
|
||||
impl<'w, I: Tokens> Deref for WithCtx<'w, I> {
|
||||
type Target = Parser<I>;
|
||||
|
||||
fn deref(&self) -> &Parser<I> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
impl<'w, I: Tokens> DerefMut for WithCtx<'w, I> {
|
||||
fn deref_mut(&mut self) -> &mut Parser<I> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, I: Tokens> Drop for WithCtx<'w, I> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.set_ctx(self.orig_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) trait ExprExt {
|
||||
fn as_expr(&self) -> &Expr;
|
||||
|
||||
/// "IsValidSimpleAssignmentTarget" from spec.
|
||||
fn is_valid_simple_assignment_target(&self, strict: bool) -> bool {
|
||||
match self.as_expr() {
|
||||
Expr::Ident(ident) => {
|
||||
if strict && ident.is_reserved_in_strict_bind() {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
Expr::This(..)
|
||||
| Expr::Lit(..)
|
||||
| Expr::Array(..)
|
||||
| Expr::Object(..)
|
||||
| Expr::Fn(..)
|
||||
| Expr::Class(..)
|
||||
| Expr::Tpl(..)
|
||||
| Expr::TaggedTpl(..) => false,
|
||||
Expr::Paren(ParenExpr { expr, .. }) => expr.is_valid_simple_assignment_target(strict),
|
||||
|
||||
Expr::Member(MemberExpr { obj, .. }) => match obj.as_ref() {
|
||||
Expr::Member(..) => obj.is_valid_simple_assignment_target(strict),
|
||||
Expr::OptChain(..) => false,
|
||||
_ => true,
|
||||
},
|
||||
|
||||
Expr::SuperProp(..) => true,
|
||||
|
||||
Expr::New(..) | Expr::Call(..) => false,
|
||||
// TODO: Spec only mentions `new.target`
|
||||
Expr::MetaProp(..) => false,
|
||||
|
||||
Expr::Update(..) => false,
|
||||
|
||||
Expr::Unary(..) | Expr::Await(..) => false,
|
||||
|
||||
Expr::Bin(..) => false,
|
||||
|
||||
Expr::Cond(..) => false,
|
||||
|
||||
Expr::Yield(..) | Expr::Arrow(..) | Expr::Assign(..) => false,
|
||||
|
||||
Expr::Seq(..) => false,
|
||||
|
||||
Expr::OptChain(..) => false,
|
||||
|
||||
// MemberExpression is valid assignment target
|
||||
Expr::PrivateName(..) => false,
|
||||
|
||||
// jsx
|
||||
Expr::JSXMember(..)
|
||||
| Expr::JSXNamespacedName(..)
|
||||
| Expr::JSXEmpty(..)
|
||||
| Expr::JSXElement(..)
|
||||
| Expr::JSXFragment(..) => false,
|
||||
|
||||
// typescript
|
||||
Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
|
||||
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
||||
| Expr::TsAs(TsAsExpr { ref expr, .. })
|
||||
| Expr::TsInstantiation(TsInstantiation { ref expr, .. })
|
||||
| Expr::TsSatisfies(TsSatisfiesExpr { ref expr, .. }) => {
|
||||
expr.is_valid_simple_assignment_target(strict)
|
||||
}
|
||||
|
||||
Expr::TsConstAssertion(..) => false,
|
||||
|
||||
Expr::Invalid(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExprExt for Box<Expr> {
|
||||
fn as_expr(&self) -> &Expr {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ExprExt for Expr {
|
||||
fn as_expr(&self) -> &Expr {
|
||||
self
|
||||
}
|
||||
}
|
||||
674
third-party/vendor/swc_ecma_parser/src/token.rs
vendored
Normal file
674
third-party/vendor/swc_ecma_parser/src/token.rs
vendored
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
//! Ported from [babel/babylon][]
|
||||
//!
|
||||
//! [babel/babylon]:https://github.com/babel/babel/blob/2d378d076eb0c5fe63234a8b509886005c01d7ee/packages/babylon/src/tokenizer/types.js
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
};
|
||||
|
||||
use num_bigint::BigInt as BigIntValue;
|
||||
use swc_atoms::{js_word, Atom, JsWord};
|
||||
use swc_common::{Span, Spanned};
|
||||
pub(crate) use swc_ecma_ast::AssignOp as AssignOpToken;
|
||||
use swc_ecma_ast::BinaryOp;
|
||||
|
||||
pub(crate) use self::{AssignOpToken::*, BinOpToken::*, Keyword::*, Token::*};
|
||||
use crate::{error::Error, lexer::LexResult};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Token {
|
||||
/// Identifier, "null", "true", "false".
|
||||
///
|
||||
/// Contains `null` and ``
|
||||
Word(Word),
|
||||
|
||||
/// '=>'
|
||||
Arrow,
|
||||
|
||||
/// '#'
|
||||
Hash,
|
||||
|
||||
/// '@'
|
||||
At,
|
||||
/// '.'
|
||||
Dot,
|
||||
|
||||
/// '...'
|
||||
DotDotDot,
|
||||
/// '!'
|
||||
Bang,
|
||||
|
||||
/// '('
|
||||
LParen,
|
||||
/// ')'
|
||||
RParen,
|
||||
/// `[`
|
||||
LBracket,
|
||||
/// ']'
|
||||
RBracket,
|
||||
/// '{'
|
||||
LBrace,
|
||||
/// '}'
|
||||
RBrace,
|
||||
|
||||
/// ';'
|
||||
Semi,
|
||||
/// ','
|
||||
Comma,
|
||||
|
||||
/// '`'
|
||||
BackQuote,
|
||||
Template {
|
||||
raw: Atom,
|
||||
cooked: LexResult<Atom>,
|
||||
},
|
||||
/// ':'
|
||||
Colon,
|
||||
///
|
||||
BinOp(BinOpToken),
|
||||
///
|
||||
AssignOp(AssignOpToken),
|
||||
|
||||
/// '${'
|
||||
DollarLBrace,
|
||||
|
||||
/// '?'
|
||||
QuestionMark,
|
||||
|
||||
/// `++`
|
||||
PlusPlus,
|
||||
/// `--`
|
||||
MinusMinus,
|
||||
|
||||
/// `~`
|
||||
Tilde,
|
||||
|
||||
/// String literal. Span of this token contains quote.
|
||||
Str {
|
||||
value: JsWord,
|
||||
raw: Atom,
|
||||
},
|
||||
|
||||
/// Regexp literal.
|
||||
Regex(Atom, Atom),
|
||||
|
||||
/// TODO: Make Num as enum and separate decimal, binary, ..etc
|
||||
Num {
|
||||
value: f64,
|
||||
raw: Atom,
|
||||
},
|
||||
|
||||
BigInt {
|
||||
value: Box<BigIntValue>,
|
||||
raw: Atom,
|
||||
},
|
||||
|
||||
JSXName {
|
||||
name: JsWord,
|
||||
},
|
||||
JSXText {
|
||||
raw: Atom,
|
||||
},
|
||||
JSXTagStart,
|
||||
JSXTagEnd,
|
||||
|
||||
Shebang(Atom),
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
impl Token {
|
||||
pub(crate) const fn before_expr(&self) -> bool {
|
||||
match self {
|
||||
Self::Word(w) => w.before_expr(),
|
||||
Self::BinOp(w) => w.before_expr(),
|
||||
Self::Arrow
|
||||
| Self::DotDotDot
|
||||
| Self::Bang
|
||||
| Self::LParen
|
||||
| Self::LBrace
|
||||
| Self::LBracket
|
||||
| Self::Semi
|
||||
| Self::Comma
|
||||
| Self::Colon
|
||||
| Self::AssignOp(..)
|
||||
| Self::DollarLBrace
|
||||
| Self::QuestionMark
|
||||
| Self::PlusPlus
|
||||
| Self::MinusMinus
|
||||
| Self::Tilde
|
||||
| Self::JSXText { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn starts_expr(&self) -> bool {
|
||||
match self {
|
||||
Self::Word(w) => w.starts_expr(),
|
||||
Self::BinOp(w) => w.starts_expr(),
|
||||
Self::Bang
|
||||
| Self::LParen
|
||||
| Self::LBrace
|
||||
| Self::LBracket
|
||||
| Self::BackQuote
|
||||
| Self::DollarLBrace
|
||||
| Self::PlusPlus
|
||||
| Self::MinusMinus
|
||||
| Self::Tilde
|
||||
| Self::Str { .. }
|
||||
| Self::Regex(..)
|
||||
| Self::Num { .. }
|
||||
| Self::BigInt { .. }
|
||||
| Self::JSXTagStart => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum BinOpToken {
|
||||
/// `==`
|
||||
EqEq,
|
||||
/// `!=`
|
||||
NotEq,
|
||||
/// `===`
|
||||
EqEqEq,
|
||||
/// `!==`
|
||||
NotEqEq,
|
||||
/// `<`
|
||||
Lt,
|
||||
/// `<=`
|
||||
LtEq,
|
||||
/// `>`
|
||||
Gt,
|
||||
/// `>=`
|
||||
GtEq,
|
||||
/// `<<`
|
||||
LShift,
|
||||
/// `>>`
|
||||
RShift,
|
||||
/// `>>>`
|
||||
ZeroFillRShift,
|
||||
|
||||
/// `+`
|
||||
Add,
|
||||
/// `-`
|
||||
Sub,
|
||||
/// `*`
|
||||
Mul,
|
||||
/// `/`
|
||||
Div,
|
||||
/// `%`
|
||||
Mod,
|
||||
|
||||
/// `|`
|
||||
BitOr,
|
||||
/// `^`
|
||||
BitXor,
|
||||
/// `&`
|
||||
BitAnd,
|
||||
|
||||
// /// `in`
|
||||
// #[kind(precedence = "7")]
|
||||
// In,
|
||||
// /// `instanceof`
|
||||
// #[kind(precedence = "7")]
|
||||
// InstanceOf,
|
||||
/// `**`
|
||||
Exp,
|
||||
|
||||
/// `||`
|
||||
LogicalOr,
|
||||
/// `&&`
|
||||
LogicalAnd,
|
||||
|
||||
/// `??`
|
||||
NullishCoalescing,
|
||||
}
|
||||
|
||||
impl BinOpToken {
|
||||
pub(crate) const fn starts_expr(&self) -> bool {
|
||||
matches!(self, Self::Add | Self::Sub)
|
||||
}
|
||||
|
||||
pub(crate) const fn before_expr(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TokenAndSpan {
|
||||
pub token: Token,
|
||||
/// Had a line break before this token?
|
||||
pub had_line_break: bool,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl Spanned for TokenAndSpan {
|
||||
#[inline(always)]
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Word {
|
||||
Keyword(Keyword),
|
||||
|
||||
Null,
|
||||
True,
|
||||
False,
|
||||
|
||||
Ident(JsWord),
|
||||
}
|
||||
|
||||
impl Word {
|
||||
pub(crate) const fn before_expr(&self) -> bool {
|
||||
match self {
|
||||
Word::Keyword(k) => k.before_expr(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn starts_expr(&self) -> bool {
|
||||
match self {
|
||||
Word::Keyword(k) => k.starts_expr(),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsWord> for Word {
|
||||
fn from(i: JsWord) -> Self {
|
||||
match i {
|
||||
js_word!("null") => Word::Null,
|
||||
js_word!("true") => Word::True,
|
||||
js_word!("false") => Word::False,
|
||||
js_word!("await") => Await.into(),
|
||||
js_word!("break") => Break.into(),
|
||||
js_word!("case") => Case.into(),
|
||||
js_word!("catch") => Catch.into(),
|
||||
js_word!("continue") => Continue.into(),
|
||||
js_word!("debugger") => Debugger.into(),
|
||||
js_word!("default") => Default_.into(),
|
||||
js_word!("do") => Do.into(),
|
||||
js_word!("export") => Export.into(),
|
||||
js_word!("else") => Else.into(),
|
||||
js_word!("finally") => Finally.into(),
|
||||
js_word!("for") => For.into(),
|
||||
js_word!("function") => Function.into(),
|
||||
js_word!("if") => If.into(),
|
||||
js_word!("return") => Return.into(),
|
||||
js_word!("switch") => Switch.into(),
|
||||
js_word!("throw") => Throw.into(),
|
||||
js_word!("try") => Try.into(),
|
||||
js_word!("var") => Var.into(),
|
||||
js_word!("let") => Let.into(),
|
||||
js_word!("const") => Const.into(),
|
||||
js_word!("while") => While.into(),
|
||||
js_word!("with") => With.into(),
|
||||
js_word!("new") => New.into(),
|
||||
js_word!("this") => This.into(),
|
||||
js_word!("super") => Super.into(),
|
||||
js_word!("class") => Class.into(),
|
||||
js_word!("extends") => Extends.into(),
|
||||
js_word!("import") => Import.into(),
|
||||
js_word!("yield") => Yield.into(),
|
||||
js_word!("in") => In.into(),
|
||||
js_word!("instanceof") => InstanceOf.into(),
|
||||
js_word!("typeof") => TypeOf.into(),
|
||||
js_word!("void") => Void.into(),
|
||||
js_word!("delete") => Delete.into(),
|
||||
_ => Word::Ident(i),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Keyword> for Word {
|
||||
fn from(kwd: Keyword) -> Self {
|
||||
Word::Keyword(kwd)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Word> for JsWord {
|
||||
fn from(w: Word) -> Self {
|
||||
match w {
|
||||
Word::Keyword(k) => match k {
|
||||
Await => js_word!("await"),
|
||||
Break => js_word!("break"),
|
||||
Case => js_word!("case"),
|
||||
Catch => js_word!("catch"),
|
||||
Continue => js_word!("continue"),
|
||||
Debugger => js_word!("debugger"),
|
||||
Default_ => js_word!("default"),
|
||||
Do => js_word!("do"),
|
||||
Else => js_word!("else"),
|
||||
|
||||
Finally => js_word!("finally"),
|
||||
For => js_word!("for"),
|
||||
|
||||
Function => js_word!("function"),
|
||||
|
||||
If => js_word!("if"),
|
||||
|
||||
Return => js_word!("return"),
|
||||
|
||||
Switch => js_word!("switch"),
|
||||
|
||||
Throw => js_word!("throw"),
|
||||
|
||||
Try => js_word!("try"),
|
||||
Var => js_word!("var"),
|
||||
Let => js_word!("let"),
|
||||
Const => js_word!("const"),
|
||||
While => js_word!("while"),
|
||||
With => js_word!("with"),
|
||||
|
||||
New => js_word!("new"),
|
||||
This => js_word!("this"),
|
||||
Super => js_word!("super"),
|
||||
|
||||
Class => js_word!("class"),
|
||||
|
||||
Extends => js_word!("extends"),
|
||||
|
||||
Export => js_word!("export"),
|
||||
Import => js_word!("import"),
|
||||
|
||||
Yield => js_word!("yield"),
|
||||
|
||||
In => js_word!("in"),
|
||||
InstanceOf => js_word!("instanceof"),
|
||||
|
||||
TypeOf => js_word!("typeof"),
|
||||
|
||||
Void => js_word!("void"),
|
||||
|
||||
Delete => js_word!("delete"),
|
||||
},
|
||||
|
||||
Word::Null => js_word!("null"),
|
||||
Word::True => js_word!("true"),
|
||||
Word::False => js_word!("false"),
|
||||
|
||||
Word::Ident(w) => w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Word {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Word::Ident(ref s) => Display::fmt(s, f),
|
||||
_ => {
|
||||
let s: JsWord = self.clone().into();
|
||||
Display::fmt(&s, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Keywords
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Keyword {
|
||||
/// Spec says this might be identifier.
|
||||
Await,
|
||||
Break,
|
||||
Case,
|
||||
Catch,
|
||||
Continue,
|
||||
Debugger,
|
||||
Default_,
|
||||
Do,
|
||||
Else,
|
||||
|
||||
Finally,
|
||||
For,
|
||||
|
||||
Function,
|
||||
|
||||
If,
|
||||
|
||||
Return,
|
||||
|
||||
Switch,
|
||||
|
||||
Throw,
|
||||
|
||||
Try,
|
||||
Var,
|
||||
Let,
|
||||
Const,
|
||||
While,
|
||||
With,
|
||||
|
||||
New,
|
||||
This,
|
||||
Super,
|
||||
|
||||
Class,
|
||||
|
||||
Extends,
|
||||
|
||||
Export,
|
||||
Import,
|
||||
|
||||
/// Spec says this might be identifier.
|
||||
Yield,
|
||||
|
||||
In,
|
||||
InstanceOf,
|
||||
TypeOf,
|
||||
Void,
|
||||
Delete,
|
||||
}
|
||||
|
||||
impl Keyword {
|
||||
pub(crate) const fn before_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Await
|
||||
| Self::Case
|
||||
| Self::Default_
|
||||
| Self::Do
|
||||
| Self::Else
|
||||
| Self::Return
|
||||
| Self::Throw
|
||||
| Self::New
|
||||
| Self::Extends
|
||||
| Self::Yield
|
||||
| Self::In
|
||||
| Self::InstanceOf
|
||||
| Self::TypeOf
|
||||
| Self::Void
|
||||
| Self::Delete
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) const fn starts_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Await
|
||||
| Self::Function
|
||||
| Self::Throw
|
||||
| Self::New
|
||||
| Self::This
|
||||
| Self::Super
|
||||
| Self::Class
|
||||
| Self::Import
|
||||
| Self::Yield
|
||||
| Self::TypeOf
|
||||
| Self::Void
|
||||
| Self::Delete
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) const fn into_js_word(self) -> JsWord {
|
||||
match self {
|
||||
Await => js_word!("await"),
|
||||
Break => js_word!("break"),
|
||||
Case => js_word!("case"),
|
||||
Catch => js_word!("catch"),
|
||||
Continue => js_word!("continue"),
|
||||
Debugger => js_word!("debugger"),
|
||||
Default_ => js_word!("default"),
|
||||
Do => js_word!("do"),
|
||||
Else => js_word!("else"),
|
||||
|
||||
Finally => js_word!("finally"),
|
||||
For => js_word!("for"),
|
||||
|
||||
Function => js_word!("function"),
|
||||
|
||||
If => js_word!("if"),
|
||||
|
||||
Return => js_word!("return"),
|
||||
|
||||
Switch => js_word!("switch"),
|
||||
|
||||
Throw => js_word!("throw"),
|
||||
|
||||
Try => js_word!("try"),
|
||||
Var => js_word!("var"),
|
||||
Let => js_word!("let"),
|
||||
Const => js_word!("const"),
|
||||
While => js_word!("while"),
|
||||
With => js_word!("with"),
|
||||
|
||||
New => js_word!("new"),
|
||||
This => js_word!("this"),
|
||||
Super => js_word!("super"),
|
||||
|
||||
Class => js_word!("class"),
|
||||
|
||||
Extends => js_word!("extends"),
|
||||
|
||||
Export => js_word!("export"),
|
||||
Import => js_word!("import"),
|
||||
|
||||
Yield => js_word!("yield"),
|
||||
|
||||
In => js_word!("in"),
|
||||
InstanceOf => js_word!("instanceof"),
|
||||
|
||||
TypeOf => js_word!("typeof"),
|
||||
|
||||
Void => js_word!("void"),
|
||||
|
||||
Delete => js_word!("delete"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Keyword {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "keyword '{}'", self.into_js_word())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BinOpToken> for BinaryOp {
|
||||
fn from(t: BinOpToken) -> Self {
|
||||
use self::BinaryOp::*;
|
||||
match t {
|
||||
BinOpToken::EqEq => EqEq,
|
||||
BinOpToken::NotEq => NotEq,
|
||||
BinOpToken::EqEqEq => EqEqEq,
|
||||
BinOpToken::NotEqEq => NotEqEq,
|
||||
BinOpToken::Lt => Lt,
|
||||
BinOpToken::LtEq => LtEq,
|
||||
BinOpToken::Gt => Gt,
|
||||
BinOpToken::GtEq => GtEq,
|
||||
BinOpToken::LShift => LShift,
|
||||
BinOpToken::RShift => RShift,
|
||||
BinOpToken::ZeroFillRShift => ZeroFillRShift,
|
||||
BinOpToken::Add => Add,
|
||||
BinOpToken::Sub => Sub,
|
||||
BinOpToken::Mul => Mul,
|
||||
BinOpToken::Div => Div,
|
||||
BinOpToken::Mod => Mod,
|
||||
BinOpToken::BitOr => BitOr,
|
||||
BinOpToken::BitXor => BitXor,
|
||||
BinOpToken::BitAnd => BitAnd,
|
||||
BinOpToken::LogicalOr => LogicalOr,
|
||||
BinOpToken::LogicalAnd => LogicalAnd,
|
||||
BinOpToken::Exp => Exp,
|
||||
BinOpToken::NullishCoalescing => NullishCoalescing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// Returns true if `self` can follow keyword let.
|
||||
///
|
||||
/// e.g. `let a = xx;`, `let {a:{}} = 1`
|
||||
pub(crate) fn follows_keyword_let(&self, _strict: bool) -> bool {
|
||||
matches!(
|
||||
*self,
|
||||
crate::token::Token::Word(crate::token::Word::Keyword(crate::token::Keyword::Let))
|
||||
| tok!('{')
|
||||
| tok!('[')
|
||||
| Word(Word::Ident(..))
|
||||
| tok!("yield")
|
||||
| tok!("await")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Word {
|
||||
pub(crate) fn cow(&self) -> Cow<JsWord> {
|
||||
match *self {
|
||||
Word::Keyword(k) => Cow::Owned(k.into_js_word()),
|
||||
Word::Ident(ref w) => Cow::Borrowed(w),
|
||||
Word::False => Cow::Owned(js_word!("false")),
|
||||
Word::True => Cow::Owned(js_word!("true")),
|
||||
Word::Null => Cow::Owned(js_word!("null")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Token {
|
||||
/// This method is called only in the case of parsing failure.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Token::Word(w) => write!(f, "{:?}", w)?,
|
||||
Arrow => write!(f, "=>")?,
|
||||
Hash => write!(f, "#")?,
|
||||
At => write!(f, "@")?,
|
||||
Dot => write!(f, ".")?,
|
||||
DotDotDot => write!(f, "...")?,
|
||||
Bang => write!(f, "!")?,
|
||||
LParen => write!(f, "(")?,
|
||||
RParen => write!(f, ")")?,
|
||||
LBracket => write!(f, "[")?,
|
||||
RBracket => write!(f, "]")?,
|
||||
LBrace => write!(f, "{{")?,
|
||||
RBrace => write!(f, "}}")?,
|
||||
Semi => write!(f, ";")?,
|
||||
Comma => write!(f, ",")?,
|
||||
BackQuote => write!(f, "`")?,
|
||||
Template { raw, .. } => write!(f, "template token ({})", raw)?,
|
||||
Colon => write!(f, ":")?,
|
||||
BinOp(op) => write!(f, "{}", BinaryOp::from(*op).as_str())?,
|
||||
AssignOp(op) => write!(f, "{}", op.as_str())?,
|
||||
DollarLBrace => write!(f, "${{")?,
|
||||
QuestionMark => write!(f, "?")?,
|
||||
PlusPlus => write!(f, "++")?,
|
||||
MinusMinus => write!(f, "--")?,
|
||||
Tilde => write!(f, "~")?,
|
||||
Str { value, raw } => write!(f, "string literal ({}, {})", value, raw)?,
|
||||
Regex(exp, flags) => write!(f, "regexp literal ({}, {})", exp, flags)?,
|
||||
Num { value, raw, .. } => write!(f, "numeric literal ({}, {})", value, raw)?,
|
||||
BigInt { value, raw } => write!(f, "bigint literal ({}, {})", value, raw)?,
|
||||
JSXName { name } => write!(f, "jsx name ({})", name)?,
|
||||
JSXText { raw } => write!(f, "jsx text ({})", raw)?,
|
||||
JSXTagStart => write!(f, "< (jsx tag start)")?,
|
||||
JSXTagEnd => write!(f, "> (jsx tag end)")?,
|
||||
Shebang(_) => write!(f, "#!")?,
|
||||
Token::Error(e) => write!(f, "<lexing error: {:?}>", e)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue