use syntax::stmt::FOLLOWS_LET;
use super::decl::{class_decl, function_decl};
use super::expr::{assign_expr, expr, identifier_name, literal, object_expr, primary_expr};
use super::pat::binding_identifier;
use super::stmt::{block_items, semi, var_decl};
use super::typescript::*;
use crate::{SyntaxKind::*, *};
#[macro_export]
macro_rules! at_ident_name {
($p:expr) => {
($p.at_ts(token_set![T![ident], T![await], T![yield]]) || $p.cur().is_keyword())
};
($p:expr, $offset:expr) => {
(token_set![T![ident], T![await], T![yield]].contains($p.nth($offset)) || $p.nth($offset).is_keyword())
}
}
pub fn parse(p: &mut Parser) -> CompletedMarker {
let m = p.start();
p.eat(T![shebang]);
block_items(p, true, true, false, None);
m.complete(
p,
if p.syntax.file_kind == FileKind::Script {
SyntaxKind::SCRIPT
} else {
SyntaxKind::MODULE
},
)
}
fn named_list(p: &mut Parser) -> Marker {
let m = p.start();
p.expect(T!['{']);
let mut first = true;
while !p.at(EOF) && !p.at(T!['}']) {
if first {
first = false;
} else if p.at(T![,]) && p.nth_at(1, T!['}']) {
p.bump_any();
break;
} else {
p.expect(T![,]);
}
specifier(p);
}
p.expect(T!['}']);
m
}
fn specifier(p: &mut Parser) -> CompletedMarker {
let m = p.start();
identifier_name(p);
if p.cur_src() == "as" {
p.bump_remap(T![as]);
identifier_name(p);
}
m.complete(p, SPECIFIER)
}
fn named_export_specifier(p: &mut Parser) -> CompletedMarker {
let m = p.start();
identifier_name(p);
if p.cur_src() == "as" {
p.bump_remap(T![as]);
identifier_name(p);
}
m.complete(p, SPECIFIER)
}
pub fn import_decl(p: &mut Parser) -> CompletedMarker {
assert_eq!(p.cur(), T![import]);
let m = p.start();
let start = p.cur_tok().range.start;
if p.nth_at(1, T![.]) || p.nth_at(1, T!['(']) {
primary_expr(p).expect(
"returned value from primary_expr should not be None because
the current token is guaranteed to be `IMPORT_KW`",
);
semi(p, start..p.cur_tok().range.start);
return m.complete(p, EXPR_STMT);
}
let p = &mut *p.with_state(ParserState {
is_module: true,
strict: Some(StrictMode::Module),
..p.state.clone()
});
p.expect(T![import]);
if p.at_ts(token_set![T![ident], T![async], T![yield]]) && p.nth_at(1, T![=]) {
let mut complete = ts_import_equals_decl(p, m);
complete.err_if_not_ts(
p,
"import equals declarations can only be used in TypeScript files",
);
return complete;
}
if p.at(STRING) {
let inner = p.start();
p.bump_any();
inner.complete(p, IMPORT_STRING_SPECIFIER);
semi(p, start..p.cur_tok().range.start);
return m.complete(p, IMPORT_DECL);
}
let ty_only = p.cur_src() == "type"
&& (p.nth_at(1, T!['{']) || p.nth_src(1) != "from" && !p.nth_at(1, T![,]));
if ty_only {
if !p.typescript() {
let err = p
.err_builder("type imports can only be used in TypeScript files")
.primary(p.cur_tok().range, "");
p.error(err);
let m = p.start();
p.bump_any();
m.complete(p, ERROR);
} else {
p.bump_remap(T![type]);
}
}
if p.at_ts(token_set![T![async], T![yield], T![ident]]) {
imported_binding(p);
if p.cur_src() != "from" {
p.expect(T![,]);
}
}
if p.at(T![*]) {
let m = p.start();
p.bump_any();
if p.cur_src() != "as" {
let err = p
.err_builder("expected `as` for a namespace specifier, but found none")
.primary(p.cur_tok().range, "");
p.error(err);
} else {
p.bump_remap(T![as]);
}
imported_binding(p);
m.complete(p, WILDCARD_IMPORT);
} else if p.at(T!['{']) {
named_list(p).complete(p, NAMED_IMPORTS);
}
if p.cur_src() != "from" {
let err = p
.err_builder("expected a `from` clause for an import, but found none")
.primary(p.cur_tok().range, "");
p.error(err);
} else {
p.bump_remap(T![from]);
}
if !p.at(STRING) {
let err = p
.err_builder(
"expected a source for a `from` clause in an import statement, but found none",
)
.primary(p.cur_tok().range, "");
p.error(err);
} else {
literal(p);
}
if p.cur_src() == "assert" {
p.bump_remap(T![assert]);
if !p.at(T!['{']) {
let err = p
.err_builder("assert clauses in import declarations require an object expression")
.primary(p.cur_tok().range, "");
p.error(err);
} else {
object_expr(p);
}
}
semi(p, start..p.cur_tok().range.start);
m.complete(p, IMPORT_DECL)
}
fn imported_binding(p: &mut Parser) {
let p = &mut *p.with_state(ParserState {
in_async: false,
in_generator: false,
..p.state.clone()
});
binding_identifier(p);
}
pub fn export_decl(p: &mut Parser) -> CompletedMarker {
let start = p.cur_tok().range.start;
let m = p.start();
p.expect(T![export]);
let declare = p.typescript() && p.cur_src() == "declare";
if declare {
if let Some(mut res) = try_parse_ts(p, |p| ts_declare(p)) {
res.err_if_not_ts(
p,
"TypeScript declarations can only be used in TypeScript files",
);
return m.complete(p, EXPORT_DECL);
}
}
let offset = declare as usize;
if p.typescript() && at_ident_name!(p, offset) {
if let Some(mut res) = try_parse_ts(p, |p| ts_decl(p)) {
res.err_if_not_ts(
p,
"TypeScript declarations can only be used in TypeScript files",
);
return m.complete(p, EXPORT_DECL);
}
}
macro_rules! err_if_declare {
($p:expr, $declare:expr, $msg:literal) => {
if $declare {
let range = $p.cur_tok().range;
$p.bump_remap(T![declare]);
let err = $p.err_builder($msg).primary(range, "");
$p.error(err);
}
};
}
match p.nth(offset) {
T![import] => {
err_if_declare!(
p,
declare,
"`declare` modifiers cannot be applied to import declarations"
);
p.bump_any();
let mut complete = ts_import_equals_decl(p, m);
complete.err_if_not_ts(
p,
"import equals declarations can only be used in TypeScript files",
);
return complete;
}
T![=] => {
err_if_declare!(
p,
declare,
"`declare` modifiers cannot be applied to export equals declarations"
);
p.bump_any();
expr(p);
semi(p, start..p.cur_tok().range.start);
let mut complete = m.complete(p, TS_EXPORT_ASSIGNMENT);
complete.err_if_not_ts(
p,
"export equals declarations can only be used in TypeScript files",
);
return complete;
}
_ if p.nth_src(offset) == "as" => {
err_if_declare!(
p,
declare,
"`declare` modifiers cannot be applied to export as namespace declarations"
);
p.bump_remap(T![as]);
if p.cur_src() != "namespace" {
let err = p
.err_builder("expected `namespace`, but found none")
.primary(p.cur_tok().range, "");
p.error(err);
} else {
p.bump_remap(T![namespace]);
}
identifier_name(p);
semi(p, start..p.cur_tok().range.start);
let mut complete = m.complete(p, TS_NAMESPACE_EXPORT_DECL);
complete.err_if_not_ts(
p,
"export as namespace declarations can only be used in TypeScript files",
);
return complete;
}
_ => {}
}
if declare {
if !p.typescript() {
let m = p.start();
let err = p
.err_builder("declare modifiers can only be used in TypeScript files")
.primary(p.cur_tok().range, "");
p.error(err);
p.bump_any();
m.complete(p, ERROR);
} else {
p.bump_remap(T![declare]);
}
}
let only_ty = p.typescript()
&& (p.cur_src() == "type" && {
p.bump_remap(T![type]);
true
});
let mut exports_ns = false;
let mut has_star = false;
if p.eat(T![*]) {
has_star = true;
if p.cur_src() == "from" {
from_clause_and_semi(p, start);
return m.complete(p, EXPORT_WILDCARD);
}
if p.cur_src() == "as" {
p.bump_remap(T![as]);
identifier_name(p);
exports_ns = true;
}
}
let mut export_default = false;
if !only_ty && !exports_ns && p.eat(T![default]) {
if p.cur_src() == "abstract" && p.nth_at(1, T![class]) {
p.state.decorators_were_valid = true;
let inner = p.start();
if !p.typescript() {
let err = p
.err_builder("`abstract` modifiers can only be used in TypeScript files")
.primary(p.cur_tok().range, "");
p.error(err);
let m = p.start();
p.bump_any();
m.complete(p, ERROR);
} else {
p.bump_remap(T![abstract]);
}
let decl = class_decl(
&mut *p.with_state(ParserState {
in_default: true,
..p.state.clone()
}),
false,
);
decl.undo_completion(p).abandon(p);
inner.complete(p, CLASS_DECL);
return m.complete(p, EXPORT_DEFAULT_DECL);
}
if p.cur_src() == "interface" {
if let Some(ref mut compl) = ts_interface(p) {
compl.err_if_not_ts(p, "interfaces can only be used in TypeScript files");
}
return m.complete(p, EXPORT_DEFAULT_DECL);
}
if p.at(T![class]) {
p.state.decorators_were_valid = true;
class_decl(
&mut *p.with_state(ParserState {
in_default: true,
..p.state.clone()
}),
false,
);
return m.complete(p, EXPORT_DEFAULT_DECL);
}
if p.cur_src() == "async" && p.nth_at(1, T![function]) && !p.has_linebreak_before_n(1) {
p.state.decorators_were_valid = true;
let mut guard = p.with_state(ParserState {
in_async: true,
..p.state.clone()
});
let inner = guard.start();
guard.bump_any();
function_decl(&mut *guard, inner, false);
return m.complete(&mut *guard, EXPORT_DEFAULT_DECL);
}
if p.cur_src() == "from" || (p.at(T![,]) && p.nth_at(1, T!['{'])) {
export_default = true;
} else {
assign_expr(p);
semi(p, start..p.cur_tok().range.start);
return m.complete(p, EXPORT_DEFAULT_EXPR);
}
}
if !only_ty && p.at(T![class]) {
p.state.decorators_were_valid = true;
class_decl(p, false);
} else if !only_ty
&& p.cur_src() == "async"
&& p.nth_at(1, T![function])
&& !p.has_linebreak_before_n(1)
{
p.state.decorators_were_valid = true;
let mut guard = p.with_state(ParserState {
in_async: true,
..p.state.clone()
});
let inner = guard.start();
guard.bump_any();
function_decl(&mut *guard, inner, false);
} else if !only_ty && p.at(T![function]) {
p.state.decorators_were_valid = true;
let m = p.start();
function_decl(p, m, false);
} else if !only_ty && p.at(T![const]) && p.nth_src(1) == "enum" {
ts_enum(p).err_if_not_ts(p, "enums can only be used in TypeScript files");
} else if !only_ty
&& (p.at(T![var])
|| p.at(T![const])
|| (p.cur_src() == "let" && FOLLOWS_LET.contains(p.nth(1))))
{
var_decl(p, false);
} else {
let m = p.start();
if p.cur_src() == "from" && exports_ns {
from_clause_and_semi(p, start);
return m.complete(p, EXPORT_WILDCARD);
}
if !export_default
&& (token_set![T![async], T![yield], T![yield]].contains(p.cur())
|| p.cur().is_keyword())
{
identifier_name(p);
export_default = true;
}
if p.cur_src() == "from" && export_default {
from_clause_and_semi(p, start);
return m.complete(p, EXPORT_NAMED);
}
if has_star && !exports_ns {
from_clause_and_semi(p, start);
return m.complete(p, EXPORT_WILDCARD);
}
if exports_ns || export_default {
p.expect(T![,]);
}
p.expect(T!['{']);
let mut first = true;
while (!p.at(EOF) && p.at(T![,])) || crate::at_ident_name!(p) {
if first {
first = false;
} else if p.eat(T![,]) && p.at(T!['}']) {
break;
}
named_export_specifier(p);
}
p.expect(T!['}']);
if p.cur_src() == "from" {
from_clause_and_semi(p, start);
} else {
semi(p, start..p.cur_tok().range.start);
if export_default || exports_ns {
let err = p
.err_builder(
"`export default` and `export as` declarations must have a `from` clause",
)
.primary(start..p.cur_tok().range.start, "");
p.error(err);
}
}
m.complete(p, EXPORT_NAMED);
}
m.complete(p, EXPORT_DECL)
}
fn from_clause_and_semi(p: &mut Parser, start: usize) {
debug_assert_eq!(p.cur_src(), "from");
p.bump_remap(T![from]);
literal(p);
semi(p, start..p.cur_tok().range.start);
}
pub fn ts_import_equals_decl(p: &mut Parser, m: Marker) -> CompletedMarker {
let start = p.cur_tok().range.start;
identifier_name(p);
p.expect(T![=]);
if p.cur_src() == "require" && p.nth_at(1, T!['(']) {
ts_external_module_ref(p);
} else {
ts_entity_name(p, None, false);
}
semi(p, start..p.cur_tok().range.start);
m.complete(p, TS_IMPORT_EQUALS_DECL)
}
pub fn ts_external_module_ref(p: &mut Parser) -> CompletedMarker {
let m = p.start();
if p.cur_src() != "require" {
let err = p
.err_builder("expected `require` for an external module reference, but found none")
.primary(p.cur_tok().range, "");
p.error(err);
} else {
p.bump_remap(T![require]);
}
p.expect(T!['(']);
p.expect(STRING);
p.expect(T![')']);
m.complete(p, TS_EXTERNAL_MODULE_REF)
}