mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 21:37:34 +00:00
LibJS: Parse assert clauses of in- and export statements
Based on proposal: https://tc39.es/proposal-import-assertions Since imports are not supported yet this is not functional.
This commit is contained in:
parent
81312986fe
commit
be3b806487
4 changed files with 124 additions and 17 deletions
|
@ -3510,6 +3510,16 @@ Value ExportStatement::execute(Interpreter& interpreter, GlobalObject& global_ob
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dump_assert_clauses(ModuleRequest const& request)
|
||||||
|
{
|
||||||
|
if (!request.assertions.is_empty()) {
|
||||||
|
out("[ ");
|
||||||
|
for (auto& assertion : request.assertions)
|
||||||
|
out("{}: {}, ", assertion.key, assertion.value);
|
||||||
|
out(" ]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExportStatement::dump(int indent) const
|
void ExportStatement::dump(int indent) const
|
||||||
{
|
{
|
||||||
ASTNode::dump(indent);
|
ASTNode::dump(indent);
|
||||||
|
@ -3525,7 +3535,12 @@ void ExportStatement::dump(int indent) const
|
||||||
|
|
||||||
for (auto& entry : m_entries) {
|
for (auto& entry : m_entries) {
|
||||||
print_indent(indent + 2);
|
print_indent(indent + 2);
|
||||||
outln("ModuleRequest: {}, ImportName: {}, LocalName: {}, ExportName: {}", string_or_null(entry.module_request), entry.kind == ExportEntry::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null", entry.kind != ExportEntry::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null", string_or_null(entry.export_name));
|
out("ModuleRequest: {}", entry.module_request.module_specifier);
|
||||||
|
dump_assert_clauses(entry.module_request);
|
||||||
|
outln(", ImportName: {}, LocalName: {}, ExportName: {}",
|
||||||
|
entry.kind == ExportEntry::Kind::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null",
|
||||||
|
entry.kind != ExportEntry::Kind::ModuleRequest ? string_or_null(entry.local_or_import_name) : "null",
|
||||||
|
string_or_null(entry.export_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3535,9 +3550,11 @@ void ImportStatement::dump(int indent) const
|
||||||
print_indent(indent + 1);
|
print_indent(indent + 1);
|
||||||
if (m_entries.is_empty()) {
|
if (m_entries.is_empty()) {
|
||||||
// direct from "module" import
|
// direct from "module" import
|
||||||
outln("Entire module '{}'", m_module_request);
|
outln("Entire module '{}'", m_module_request.module_specifier);
|
||||||
|
dump_assert_clauses(m_module_request);
|
||||||
} else {
|
} else {
|
||||||
outln("(ExportEntries) from {}", m_module_request);
|
outln("(ExportEntries) from {}", m_module_request.module_specifier);
|
||||||
|
dump_assert_clauses(m_module_request);
|
||||||
|
|
||||||
for (auto& entry : m_entries) {
|
for (auto& entry : m_entries) {
|
||||||
print_indent(indent + 2);
|
print_indent(indent + 2);
|
||||||
|
|
|
@ -217,6 +217,29 @@ private:
|
||||||
NonnullRefPtrVector<FunctionDeclaration> m_functions_hoistable_with_annexB_extension;
|
NonnullRefPtrVector<FunctionDeclaration> m_functions_hoistable_with_annexB_extension;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2.9 ModuleRequest Records, https://tc39.es/proposal-import-assertions/#sec-modulerequest-record
|
||||||
|
struct ModuleRequest {
|
||||||
|
struct Assertion {
|
||||||
|
String key;
|
||||||
|
String value;
|
||||||
|
};
|
||||||
|
|
||||||
|
ModuleRequest() = default;
|
||||||
|
|
||||||
|
explicit ModuleRequest(String specifier)
|
||||||
|
: module_specifier(move(specifier))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_assertion(String key, String value)
|
||||||
|
{
|
||||||
|
assertions.empend(move(key), move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
String module_specifier; // [[Specifier]]
|
||||||
|
Vector<Assertion> assertions; // [[Assertions]]
|
||||||
|
};
|
||||||
|
|
||||||
class ImportStatement final : public Statement {
|
class ImportStatement final : public Statement {
|
||||||
public:
|
public:
|
||||||
struct ImportEntry {
|
struct ImportEntry {
|
||||||
|
@ -224,9 +247,9 @@ public:
|
||||||
String local_name;
|
String local_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ImportStatement(SourceRange source_range, StringView from_module, Vector<ImportEntry> entries = {})
|
explicit ImportStatement(SourceRange source_range, ModuleRequest from_module, Vector<ImportEntry> entries = {})
|
||||||
: Statement(source_range)
|
: Statement(source_range)
|
||||||
, m_module_request(from_module)
|
, m_module_request(move(from_module))
|
||||||
, m_entries(move(entries))
|
, m_entries(move(entries))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -238,14 +261,14 @@ public:
|
||||||
bool has_bound_name(StringView name) const;
|
bool has_bound_name(StringView name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String m_module_request;
|
ModuleRequest m_module_request;
|
||||||
Vector<ImportEntry> m_entries;
|
Vector<ImportEntry> m_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExportStatement final : public Statement {
|
class ExportStatement final : public Statement {
|
||||||
public:
|
public:
|
||||||
struct ExportEntry {
|
struct ExportEntry {
|
||||||
enum Kind {
|
enum class Kind {
|
||||||
ModuleRequest,
|
ModuleRequest,
|
||||||
LocalExport
|
LocalExport
|
||||||
} kind;
|
} kind;
|
||||||
|
@ -253,13 +276,13 @@ public:
|
||||||
String export_name;
|
String export_name;
|
||||||
|
|
||||||
// Only if module request
|
// Only if module request
|
||||||
String module_request;
|
ModuleRequest module_request;
|
||||||
|
|
||||||
// Has just one of ones below
|
// Has just one of ones below
|
||||||
String local_or_import_name;
|
String local_or_import_name;
|
||||||
|
|
||||||
ExportEntry(String export_name, String local_name)
|
ExportEntry(String export_name, String local_name)
|
||||||
: kind(LocalExport)
|
: kind(Kind::LocalExport)
|
||||||
, export_name(move(export_name))
|
, export_name(move(export_name))
|
||||||
, local_or_import_name(move(local_name))
|
, local_or_import_name(move(local_name))
|
||||||
{
|
{
|
||||||
|
|
|
@ -529,7 +529,7 @@ void Parser::parse_module(Program& program)
|
||||||
if (export_statement.has_statement())
|
if (export_statement.has_statement())
|
||||||
continue;
|
continue;
|
||||||
for (auto& entry : export_statement.entries()) {
|
for (auto& entry : export_statement.entries()) {
|
||||||
if (entry.kind == ExportStatement::ExportEntry::ModuleRequest)
|
if (entry.kind == ExportStatement::ExportEntry::Kind::ModuleRequest)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto const& exported_name = entry.local_or_import_name;
|
auto const& exported_name = entry.local_or_import_name;
|
||||||
|
@ -3912,8 +3912,58 @@ void Parser::check_identifier_name_for_assignment_validity(StringView name, bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::match_assert_clause() const
|
||||||
|
{
|
||||||
|
return !m_state.current_token.trivia_contains_line_terminator() && m_state.current_token.original_value() == "assert"sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertClause, https://tc39.es/proposal-import-assertions/#prod-AssertClause
|
||||||
|
void Parser::parse_assert_clause(ModuleRequest& request)
|
||||||
|
{
|
||||||
|
VERIFY(m_state.current_token.original_value() == "assert"sv);
|
||||||
|
consume(TokenType::Identifier);
|
||||||
|
consume(TokenType::CurlyOpen);
|
||||||
|
|
||||||
|
while (!done() && !match(TokenType::CurlyClose)) {
|
||||||
|
String key;
|
||||||
|
if (match(TokenType::StringLiteral)) {
|
||||||
|
key = parse_string_literal(m_state.current_token)->value().to_string();
|
||||||
|
consume();
|
||||||
|
} else if (match_identifier_name()) {
|
||||||
|
key = consume().value();
|
||||||
|
} else {
|
||||||
|
expected("IdentifierName or StringValue as AssertionKey");
|
||||||
|
consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::Colon);
|
||||||
|
|
||||||
|
if (match(TokenType::StringLiteral)) {
|
||||||
|
for (auto& entries : request.assertions) {
|
||||||
|
if (entries.key == key)
|
||||||
|
syntax_error(String::formatted("Duplicate assertion clauses with name: {}", key));
|
||||||
|
}
|
||||||
|
request.add_assertion(move(key), parse_string_literal(m_state.current_token)->value().to_string());
|
||||||
|
}
|
||||||
|
consume(TokenType::StringLiteral);
|
||||||
|
|
||||||
|
if (match(TokenType::Comma))
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::CurlyClose);
|
||||||
|
}
|
||||||
|
|
||||||
NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
|
NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
|
||||||
{
|
{
|
||||||
|
// We use the extended syntax which adds:
|
||||||
|
// ImportDeclaration:
|
||||||
|
// import ImportClause FromClause [no LineTerminator here] AssertClause;
|
||||||
|
// import ModuleSpecifier [no LineTerminator here] AssertClause;
|
||||||
|
// From: https://tc39.es/proposal-import-assertions/#prod-ImportDeclaration
|
||||||
|
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
if (program.type() != Program::Type::Module)
|
if (program.type() != Program::Type::Module)
|
||||||
syntax_error("Cannot use import statement outside a module");
|
syntax_error("Cannot use import statement outside a module");
|
||||||
|
@ -3921,8 +3971,11 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
|
||||||
consume(TokenType::Import);
|
consume(TokenType::Import);
|
||||||
|
|
||||||
if (match(TokenType::StringLiteral)) {
|
if (match(TokenType::StringLiteral)) {
|
||||||
auto module_name = consume(TokenType::StringLiteral).value();
|
auto module_request = ModuleRequest(consume(TokenType::StringLiteral).value());
|
||||||
return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, module_name);
|
if (match_assert_clause())
|
||||||
|
parse_assert_clause(module_request);
|
||||||
|
|
||||||
|
return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto match_imported_binding = [&] {
|
auto match_imported_binding = [&] {
|
||||||
|
@ -4016,7 +4069,10 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
|
||||||
if (from_statement != "from"sv)
|
if (from_statement != "from"sv)
|
||||||
syntax_error(String::formatted("Expected 'from' got {}", from_statement));
|
syntax_error(String::formatted("Expected 'from' got {}", from_statement));
|
||||||
|
|
||||||
auto module_name = consume(TokenType::StringLiteral).value();
|
auto module_request = ModuleRequest(consume(TokenType::StringLiteral).value());
|
||||||
|
|
||||||
|
if (match_assert_clause())
|
||||||
|
parse_assert_clause(module_request);
|
||||||
|
|
||||||
Vector<ImportStatement::ImportEntry> entries;
|
Vector<ImportStatement::ImportEntry> entries;
|
||||||
entries.ensure_capacity(entries_with_location.size());
|
entries.ensure_capacity(entries_with_location.size());
|
||||||
|
@ -4035,11 +4091,16 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
|
||||||
entries.append(move(entry.entry));
|
entries.append(move(entry.entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, module_name, move(entries));
|
return create_ast_node<ImportStatement>({ m_state.current_token.filename(), rule_start.position(), position() }, move(module_request), move(entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
|
NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
|
||||||
{
|
{
|
||||||
|
// We use the extended syntax which adds:
|
||||||
|
// ExportDeclaration:
|
||||||
|
// export ExportFromClause FromClause [no LineTerminator here] AssertClause ;
|
||||||
|
// From: https://tc39.es/proposal-import-assertions/#prod-ExportDeclaration
|
||||||
|
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
if (program.type() != Program::Type::Module)
|
if (program.type() != Program::Type::Module)
|
||||||
syntax_error("Cannot use export statement outside a module");
|
syntax_error("Cannot use export statement outside a module");
|
||||||
|
@ -4062,10 +4123,10 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
|
||||||
ExportStatement::ExportEntry entry;
|
ExportStatement::ExportEntry entry;
|
||||||
Position position;
|
Position position;
|
||||||
|
|
||||||
void to_module_request(String from_module)
|
void to_module_request(ModuleRequest from_module)
|
||||||
{
|
{
|
||||||
entry.kind = ExportStatement::ExportEntry::Kind::ModuleRequest;
|
entry.kind = ExportStatement::ExportEntry::Kind::ModuleRequest;
|
||||||
entry.module_request = from_module;
|
entry.module_request = move(from_module);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4200,7 +4261,11 @@ NonnullRefPtr<ExportStatement> Parser::parse_export_statement(Program& program)
|
||||||
if (check_for_from != NotAllowed && match_from()) {
|
if (check_for_from != NotAllowed && match_from()) {
|
||||||
consume(TokenType::Identifier);
|
consume(TokenType::Identifier);
|
||||||
if (match(TokenType::StringLiteral)) {
|
if (match(TokenType::StringLiteral)) {
|
||||||
auto from_specifier = consume().value();
|
auto from_specifier = ModuleRequest(consume().value());
|
||||||
|
|
||||||
|
if (match_assert_clause())
|
||||||
|
parse_assert_clause(from_specifier);
|
||||||
|
|
||||||
for (auto& entry : entries_with_location)
|
for (auto& entry : entries_with_location)
|
||||||
entry.to_module_request(from_specifier);
|
entry.to_module_request(from_specifier);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -185,6 +185,7 @@ private:
|
||||||
bool match_secondary_expression(const Vector<TokenType>& forbidden = {}) const;
|
bool match_secondary_expression(const Vector<TokenType>& forbidden = {}) const;
|
||||||
bool match_statement() const;
|
bool match_statement() const;
|
||||||
bool match_export_or_import() const;
|
bool match_export_or_import() const;
|
||||||
|
bool match_assert_clause() const;
|
||||||
bool match_declaration() const;
|
bool match_declaration() const;
|
||||||
bool try_match_let_declaration() const;
|
bool try_match_let_declaration() const;
|
||||||
bool match_variable_declaration() const;
|
bool match_variable_declaration() const;
|
||||||
|
@ -220,6 +221,7 @@ private:
|
||||||
|
|
||||||
bool parse_directive(ScopeNode& body);
|
bool parse_directive(ScopeNode& body);
|
||||||
void parse_statement_list(ScopeNode& output_node, AllowLabelledFunction allow_labelled_functions = AllowLabelledFunction::No);
|
void parse_statement_list(ScopeNode& output_node, AllowLabelledFunction allow_labelled_functions = AllowLabelledFunction::No);
|
||||||
|
void parse_assert_clause(ModuleRequest& request);
|
||||||
|
|
||||||
struct RulePosition {
|
struct RulePosition {
|
||||||
AK_MAKE_NONCOPYABLE(RulePosition);
|
AK_MAKE_NONCOPYABLE(RulePosition);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue