Bug 979865 - Part 3: Implement ES6 array comprehensions r=jorendorff

This commit is contained in:
Andy Wingo 2014-03-07 22:01:13 +01:00
parent 8f023c2c14
commit f4cc4f7ce2
7 changed files with 195 additions and 6 deletions

View File

@ -6423,6 +6423,178 @@ static const char js_generator_str[] = "generator";
#endif /* JS_HAS_GENERATOR_EXPRS */
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
uint32_t begin = pos().begin;
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
// FIXME: Destructuring binding (bug 980828).
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
RootedPropertyName name(context, tokenStream.currentName());
if (name == context->names().let) {
report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
return null();
}
if (!tokenStream.matchContextualKeyword(context->names().of)) {
report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
return null();
}
Node rhs = assignExpr();
if (!rhs)
return null();
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
TokenPos headPos(begin, pos().end);
StmtInfoPC stmtInfo(context);
BindData<ParseHandler> data(context);
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
if (!blockObj)
return null();
data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS);
Node lhs = newName(name);
if (!lhs)
return null();
Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP);
if (!decls)
return null();
data.pn = lhs;
if (!data.binder(&data, name, this))
return null();
Node letScope = pushLetScope(blockObj, &stmtInfo);
if (!letScope)
return null();
handler.setLexicalScopeBody(letScope, decls);
Node assignLhs = newName(name);
if (!assignLhs)
return null();
if (!noteNameUse(name, assignLhs))
return null();
handler.setOp(assignLhs, JSOP_SETNAME);
Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos);
if (!head)
return null();
Node tail = comprehensionTail(comprehensionKind);
if (!tail)
return null();
PopStatementPC(tokenStream, pc);
return handler.newForStatement(begin, head, tail, JSOP_ITER);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
uint32_t begin = pos().begin;
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
Node cond = assignExpr();
if (!cond)
return null();
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
/* Check for (a = b) and warn about possible (a == b) mistype. */
if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
{
return null();
}
Node then = comprehensionTail(comprehensionKind);
if (!then)
return null();
return handler.newIfStatement(begin, cond, then, null());
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
{
JS_CHECK_RECURSION(context, return null());
if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
return comprehensionFor(comprehensionKind);
if (tokenStream.matchToken(TOK_IF, TokenStream::Operand))
return comprehensionIf(comprehensionKind);
uint32_t begin = pos().begin;
Node bodyExpr = assignExpr();
if (!bodyExpr)
return null();
if (comprehensionKind == NotGenerator)
return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
JS_ASSERT(comprehensionKind == StarGenerator);
Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr);
if (!yieldExpr)
return null();
handler.setInParens(yieldExpr);
return handler.newExprStatement(yieldExpr, pos().end);
}
// Parse an ES6 generator or array comprehension, starting at the first 'for'.
// The caller is responsible for matching the ending TOK_RP or TOK_RB.
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
uint32_t startYieldOffset = pc->lastYieldOffset;
Node body = comprehensionFor(comprehensionKind);
if (!body)
return null();
if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
JSMSG_BAD_GENEXP_BODY, js_yield_str);
return null();
}
return body;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::arrayComprehension(uint32_t begin)
{
Node inner = comprehension(NotGenerator);
if (!inner)
return null();
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
Node comp = handler.newList(PNK_ARRAYCOMP, inner);
if (!comp)
return null();
handler.setBeginPosition(comp, begin);
handler.setEndPosition(comp, pos().end);
return comp;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
@ -6678,7 +6850,8 @@ Parser<ParseHandler>::arrayInitializer()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
Node literal = handler.newArrayLiteral(pos().begin, pc->blockidGen);
uint32_t begin = pos().begin;
Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
if (!literal)
return null();
@ -6688,6 +6861,9 @@ Parser<ParseHandler>::arrayInitializer()
* determine their type.
*/
handler.setListFlag(literal, PNX_NONCONST);
} else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
// ES6 array comprehension.
return arrayComprehension(begin);
} else {
bool spread = false, missingTrailingComma = false;
uint32_t index = 0;

View File

@ -547,6 +547,12 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node legacyArrayComprehension(Node array);
Node legacyGeneratorExpr(Node kid);
Node comprehensionTail(GeneratorKind comprehensionKind);
Node comprehensionIf(GeneratorKind comprehensionKind);
Node comprehensionFor(GeneratorKind comprehensionKind);
Node comprehension(GeneratorKind comprehensionKind);
Node arrayComprehension(uint32_t begin);
bool argumentList(Node listNode, bool *isSpread);
Node letBlock(LetContext letContext);
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);

View File

@ -166,6 +166,14 @@ class SyntaxParseHandler
void setFunctionBox(Node pn, FunctionBox *funbox) {}
void addFunctionArgument(Node pn, Node argpn) {}
Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
return NodeGeneric;
}
Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
return NodeGeneric;
}
Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
void setLexicalScopeBody(Node block, Node body) {}

View File

@ -226,7 +226,7 @@ MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 172, 0, JSEXN_TYPEERR, "iterable for map s
MSG_DEF(JSMSG_NOT_A_CODEPOINT, 173, 1, JSEXN_RANGEERR, "{0} is not a valid code point")
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 174, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 0, JSEXN_TYPEERR, "already executing generator")
MSG_DEF(JSMSG_UNUSED176, 176, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 176, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
MSG_DEF(JSMSG_UNUSED177, 177, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_UNUSED178, 178, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_UNUSED179, 179, 0, JSEXN_NONE, "")
@ -263,11 +263,11 @@ MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous gener
MSG_DEF(JSMSG_PROTO_SETTING_SLOW, 210, 0, JSEXN_TYPEERR, "mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create")
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
MSG_DEF(JSMSG_UNUSED213, 213, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_OF_AFTER_FOR_NAME, 213, 0, JSEXN_SYNTAXERR, "missing 'of' after for")
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
MSG_DEF(JSMSG_UNUSED217, 217, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_LET_COMP_BINDING, 217, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
MSG_DEF(JSMSG_BAD_SYMBOL, 219, 1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_REFERENCEERR, "invalid delete operand")

View File

@ -86,7 +86,6 @@ const char js_import_str[] = "import";
const char js_in_str[] = "in";
const char js_instanceof_str[] = "instanceof";
const char js_interface_str[] = "interface";
const char js_let_str[] = "let";
const char js_new_str[] = "new";
const char js_package_str[] = "package";
const char js_private_str[] = "private";

View File

@ -146,7 +146,6 @@ extern const char js_import_str[];
extern const char js_in_str[];
extern const char js_instanceof_str[];
extern const char js_interface_str[];
extern const char js_let_str[];
extern const char js_new_str[];
extern const char js_package_str[];
extern const char js_private_str[];

View File

@ -110,6 +110,7 @@
macro(keys, keys, "keys") \
macro(lastIndex, lastIndex, "lastIndex") \
macro(length, length, "length") \
macro(let, let, "let") \
macro(line, line, "line") \
macro(lineNumber, lineNumber, "lineNumber") \
macro(loc, loc, "loc") \