in tools/hermes/repl.cpp [164:288]
static bool needsAnotherLine(llvh::StringRef input) {
SourceErrorManager sm;
SourceErrorManager::SaveAndSuppressMessages suppress(&sm);
hermes::BumpPtrAllocator allocator{};
parser::JSLexer lexer(input, sm, allocator, nullptr, false);
std::stack<parser::TokenKind> stack{};
if (input.empty()) {
return false;
}
if (input.back() == '\\') {
return true;
}
// Given a right delimiter token kind, get the corresponding left one.
auto getLeft = [](const parser::TokenKind kind) {
switch (kind) {
case parser::TokenKind::r_brace:
return parser::TokenKind::l_brace;
case parser::TokenKind::r_paren:
return parser::TokenKind::l_paren;
case parser::TokenKind::r_square:
return parser::TokenKind::l_square;
default:
llvm_unreachable("getLeft executed with unsupported kind");
}
};
// Look at the previous token to see if the next token could possibly be the
// beginning of a regular expression.
// https://github.com/mikesamuel/es5-lexer/blob/master/src/guess_is_regexp.js
auto isRegexPossible = [](OptValue<parser::TokenKind> previousTokenKind) {
if (!previousTokenKind) {
return true;
}
switch (*previousTokenKind) {
case parser::TokenKind::rw_break:
case parser::TokenKind::rw_case:
case parser::TokenKind::rw_continue:
case parser::TokenKind::rw_delete:
case parser::TokenKind::rw_do:
case parser::TokenKind::rw_else:
case parser::TokenKind::rw_finally:
case parser::TokenKind::rw_in:
case parser::TokenKind::rw_instanceof:
case parser::TokenKind::rw_return:
case parser::TokenKind::rw_throw:
case parser::TokenKind::rw_try:
case parser::TokenKind::rw_typeof:
case parser::TokenKind::rw_void:
case parser::TokenKind::plus:
case parser::TokenKind::minus:
case parser::TokenKind::period:
case parser::TokenKind::slash:
case parser::TokenKind::comma:
case parser::TokenKind::star:
case parser::TokenKind::exclaim:
case parser::TokenKind::percent:
case parser::TokenKind::amp:
case parser::TokenKind::l_paren:
case parser::TokenKind::colon:
case parser::TokenKind::semi:
case parser::TokenKind::less:
case parser::TokenKind::equal:
case parser::TokenKind::greater:
case parser::TokenKind::question:
case parser::TokenKind::l_square:
case parser::TokenKind::caret:
case parser::TokenKind::l_brace:
case parser::TokenKind::pipe:
case parser::TokenKind::r_brace:
case parser::TokenKind::tilde:
return true;
default:
return false;
}
};
OptValue<parser::TokenKind> previousTokenKind;
// Use AllowRegExp when a regular expression is possible and use AllowDiv
// otherwise so that division is correctly parsed.
while (const parser::Token *token = lexer.advance(
isRegexPossible(previousTokenKind)
? parser::JSLexer::GrammarContext::AllowRegExp
: parser::JSLexer::GrammarContext::AllowDiv)) {
if (token->getKind() == parser::TokenKind::eof) {
break;
}
switch (token->getKind()) {
case parser::TokenKind::l_brace:
case parser::TokenKind::l_paren:
case parser::TokenKind::l_square:
// Push any left side delimiters.
stack.push(token->getKind());
break;
case parser::TokenKind::r_brace:
case parser::TokenKind::r_paren:
case parser::TokenKind::r_square: {
// Try to match the right delimiter, and if it can't be matched,
// the failure is unrecoverable.
auto left = getLeft(token->getKind());
if (!stack.empty() && stack.top() == left) {
// Matched the delimiter, pop it off and continue.
stack.pop();
} else {
// Failed to match, so we can't be recoverable.
return false;
}
break;
}
default:
// Do nothing for the other tokens.
break;
}
previousTokenKind = token->getKind();
}
// If the stack is empty, then we can't recover from the error,
// because there was some other problem besides mismatched delimiters.
// TODO: Handle other classes of recoverable errors.
return !stack.empty();
}