1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:47:44 +00:00

LibJS: Implement the 'Hashbang Grammar for JS' proposal

Stage 3 since August 2019 - we already have shebang stripping
implemented in js(1), so this removes it from there in favor of adding
support to the lexer directly.

Most straightforward proposal and implementation I've ever seen :^)

https://github.com/tc39/proposal-hashbang
This commit is contained in:
Linus Groh 2021-06-18 19:11:26 +01:00
parent 299c3069c1
commit 597cf88c08
3 changed files with 14 additions and 28 deletions

View file

@ -308,7 +308,9 @@ bool Lexer::is_line_comment_start(bool line_has_token_yet) const
// "-->" is considered a line comment start if the current line is only whitespace and/or // "-->" is considered a line comment start if the current line is only whitespace and/or
// other block comment(s); or in other words: the current line does not have a token or // other block comment(s); or in other words: the current line does not have a token or
// ongoing line comment yet // ongoing line comment yet
|| (match('-', '-', '>') && !line_has_token_yet); || (match('-', '-', '>') && !line_has_token_yet)
// https://tc39.es/proposal-hashbang/out.html#sec-updated-syntax
|| (match('#', '!') && m_position == 1);
} }
bool Lexer::is_block_comment_start() const bool Lexer::is_block_comment_start() const

View file

@ -33,3 +33,12 @@ test("unterminated multi-line comment", () => {
expect("/* foo").not.toEval(); expect("/* foo").not.toEval();
expect("foo /*").not.toEval(); expect("foo /*").not.toEval();
}); });
test("hashbang comments", () => {
expect("#!").toEvalTo(undefined);
expect("#!/bin/js").toEvalTo(undefined);
expect("#!\n1").toEvalTo(1);
expect(" #!").not.toEval();
expect("\n#!").not.toEval();
expect("#!\n#!").not.toEval();
});

View file

@ -508,23 +508,6 @@ static void print(JS::Value value)
outln(); outln();
} }
static bool file_has_shebang(ByteBuffer const& file_contents)
{
if (file_contents.size() >= 2 && file_contents[0] == '#' && file_contents[1] == '!')
return true;
return false;
}
static StringView strip_shebang(ByteBuffer const& file_contents)
{
size_t i = 0;
for (i = 2; i < file_contents.size(); ++i) {
if (file_contents[i] == '\n')
break;
}
return StringView((const char*)file_contents.data() + i, file_contents.size() - i);
}
static bool write_to_file(const String& path) static bool write_to_file(const String& path)
{ {
int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666); int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
@ -650,9 +633,7 @@ static JS::Value load_file_impl(JS::VM& vm, JS::GlobalObject& global_object)
return {}; return {};
} }
auto file_contents = file->read_all(); auto file_contents = file->read_all();
auto source = file_has_shebang(file_contents) auto source = StringView { file_contents };
? strip_shebang(file_contents)
: StringView { file_contents };
auto parser = JS::Parser(JS::Lexer(source)); auto parser = JS::Parser(JS::Lexer(source));
auto program = parser.parse_program(); auto program = parser.parse_program();
if (parser.has_errors()) { if (parser.has_errors()) {
@ -1099,13 +1080,7 @@ int main(int argc, char** argv)
return 1; return 1;
} }
auto file_contents = file->read_all(); auto file_contents = file->read_all();
auto source = StringView { file_contents };
StringView source;
if (file_has_shebang(file_contents)) {
source = strip_shebang(file_contents);
} else {
source = file_contents;
}
builder.append(source); builder.append(source);
} }