diff options
author | Lars Knoll <lars.knoll@qt.io> | 2018-03-25 16:36:45 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2018-04-27 08:11:50 +0000 |
commit | cdbe4754bdd073dd464cc9224609cbfcd736bd49 (patch) | |
tree | 0d338865e0e06c91a217f801a9168df219b6e5ca /src | |
parent | 00b47c45097d7183ac7f3655aa4b2e6e74359e3f (diff) |
Support destructuring assignments
Not everything works yet, but basic destructuring
assignments do.
Change-Id: I5f74691fd6458092ecfde9d1a8a802f99fc57b9e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 45 | ||||
-rw-r--r-- | src/qml/parser/qqmljs.g | 26 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast.cpp | 179 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast_p.h | 53 | ||||
-rw-r--r-- | src/qml/parser/qqmljsastfwd_p.h | 1 |
5 files changed, 266 insertions, 38 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 151e98c673..0a46fc7a8e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -472,6 +472,7 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle else { throwSyntaxError(bindingList->firstSourceLocation(), QString::fromLatin1("Support for rest elements in binding arrays not implemented!")); } + ++index; } } @@ -811,19 +812,26 @@ bool Codegen::visit(BinaryExpression *ast) _expr.setResult(Reference::fromAccumulator(this)); } return false; - } - - Reference left = expression(ast->left); - if (hasError) - return false; - - switch (ast->op) { - case QSOperator::Or: - case QSOperator::And: - Q_UNREACHABLE(); // handled separately above - break; + } else if (ast->op == QSOperator::Assign) { + if (AST::Pattern *p = ast->left->patternCast()) { + RegisterScope scope(this); + Reference right = expression(ast->right).storeOnStack(); + if (auto *o = AST::cast<ObjectPattern *>(p)) + destructurePropertyList(right, o->properties); + else if (auto *a = AST::cast<ArrayPattern *>(p)) + destructureElementList(right, a->elements); + else + Q_UNREACHABLE(); + if (!_expr.accept(nx)) { + right.loadInAccumulator(); + _expr.setResult(Reference::fromAccumulator(this)); + } + return false; + } + Reference left = expression(ast->left); + if (hasError) + return false; - case QSOperator::Assign: { if (!left.isLValue()) { throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); return false; @@ -839,9 +847,20 @@ bool Codegen::visit(BinaryExpression *ast) _expr.setResult(left.storeConsumeAccumulator()); else _expr.setResult(left.storeRetainAccumulator()); - break; + return false; } + Reference left = expression(ast->left); + if (hasError) + return false; + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + case QSOperator::Assign: + Q_UNREACHABLE(); // handled separately above + break; + case QSOperator::InplaceAnd: case QSOperator::InplaceSub: case QSOperator::InplaceDiv: diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index bf733dd6cd..389938e053 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -395,6 +395,9 @@ protected: void syntaxError(const AST::SourceLocation &location, const char *message) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message))); } + void syntaxError(const AST::SourceLocation &location, const QString &message) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message)); + } protected: Engine *driver; @@ -1780,9 +1783,14 @@ PropertyDefinition: IdentifierReference; -- Using this production should result in a syntax error when used in an ObjectLiteral PropertyDefinition: CoverInitializedName; -/. case $rule_number: { - syntaxError(loc(1), "Expected token ':' after identifier."); - return false; + +CoverInitializedName: IdentifierReference Initializer_In; +/. + case $rule_number: { + AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1)); + AST::PatternProperty *node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression); + node->colonToken = loc(2); + sym(1).Node = node; } break; ./ @@ -1883,8 +1891,6 @@ ComputedPropertyName: T_LBRACKET AssignmentExpression_In T_RBRACKET; } break; ./ -CoverInitializedName: IdentifierReference Initializer_In; - Initializer: T_EQ AssignmentExpression; /. case $rule_number: Q_FALLTHROUGH(); ./ Initializer_In: T_EQ AssignmentExpression_In; @@ -2498,12 +2504,20 @@ AssignmentExpression_In: YieldExpression_In; AssignmentExpression: ArrowFunction; AssignmentExpression_In: ArrowFunction_In; --- ### Use AssignmentPattern in some cases for LHSexpression AssignmentExpression: LeftHandSideExpression T_EQ AssignmentExpression; /. case $rule_number: Q_FALLTHROUGH(); ./ AssignmentExpression_In: LeftHandSideExpression T_EQ AssignmentExpression_In; /. case $rule_number: { + // need to convert the LHS to an AssignmentPatthern if it was an Array/ObjectLiteral + if (AST::Pattern *p = sym(1).Expression->patternCast()) { + AST::SourceLocation errorLoc; + QString errorMsg; + if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) { + syntaxError(errorLoc, errorMsg); + return false; + } + } AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Assign, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index a9fd2e9278..762eb9536a 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -79,6 +79,16 @@ UiObjectMember *Node::uiObjectMemberCast() return nullptr; } +LeftHandSideExpression *Node::leftHandSideExpressionCast() +{ + return nullptr; +} + +Pattern *Node::patternCast() +{ + return nullptr; +} + ExpressionNode *ExpressionNode::expressionCast() { return this; @@ -221,6 +231,165 @@ void ObjectPattern::accept0(Visitor *visitor) visitor->endVisit(this); } +/* + This is the grammar for AssignmentPattern that we need to convert the literal to: + + AssignmentPattern: + ObjectAssignmentPattern + ArrayAssignmentPattern + ArrayAssignmentPattern: + [ ElisionOpt AssignmentRestElementOpt ] + [ AssignmentElementList ] + [ AssignmentElementList , ElisionOpt AssignmentRestElementOpt ] + AssignmentElementList: + AssignmentElisionElement + AssignmentElementList , AssignmentElisionElement + AssignmentElisionElement: + ElisionOpt AssignmentElement + AssignmentRestElement: + ... DestructuringAssignmentTarget + + ObjectAssignmentPattern: + {} + { AssignmentPropertyList } + { AssignmentPropertyList, } + AssignmentPropertyList: + AssignmentProperty + AssignmentPropertyList , AssignmentProperty + AssignmentProperty: + IdentifierReference InitializerOpt_In + PropertyName: + AssignmentElement + + AssignmentElement: + DestructuringAssignmentTarget InitializerOpt_In + DestructuringAssignmentTarget: + LeftHandSideExpression + + It was originally parsed with the following grammar: + +ArrayLiteral: + [ ElisionOpt ] + [ ElementList ] + [ ElementList , ElisionOpt ] +ElementList: + ElisionOpt AssignmentExpression_In + ElisionOpt SpreadElement + ElementList , ElisionOpt AssignmentExpression_In + ElementList , Elisionopt SpreadElement +SpreadElement: + ... AssignmentExpression_In +ObjectLiteral: + {} + { PropertyDefinitionList } + { PropertyDefinitionList , } +PropertyDefinitionList: + PropertyDefinition + PropertyDefinitionList , PropertyDefinition +PropertyDefinition: + IdentifierReference + CoverInitializedName + PropertyName : AssignmentExpression_In + MethodDefinition +PropertyName: + LiteralPropertyName + ComputedPropertyName + +*/ +bool ArrayPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + for (auto *it = elements; it; it = it->next) { + if (it->element->type == PatternElement::SpreadElement && it->next) { + *errorLocation = it->element->firstSourceLocation(); + *errorMessage = QString::fromLatin1("'...' can only appear as last element in a destructuring list."); + return false; + } + if (!it->element->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; + } + return true; +} + +bool ObjectPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + for (auto *it = properties; it; it = it->next) { + if (!it->property->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; + } + return true; +} + +bool PatternElement::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + Q_ASSERT(type == Literal || type == SpreadElement); + Q_ASSERT(bindingIdentifier.isNull()); + Q_ASSERT(bindingPattern == nullptr); + Q_ASSERT(bindingPattern == nullptr); + Q_ASSERT(initializer); + ExpressionNode *init = initializer; + + initializer = nullptr; + LeftHandSideExpression *lhs = init->leftHandSideExpressionCast(); + if (type == SpreadElement) { + if (!lhs) { + *errorLocation = init->firstSourceLocation(); + *errorMessage = QString::fromLatin1("Invalid lhs expression after '...' in destructuring expression."); + return false; + } + // ### Should be binding + initializer = lhs; + return true; + } + type = PatternElement::Binding; + + if (BinaryExpression *b = init->binaryExpressionCast()) { + if (b->op != QSOperator::Assign) { + *errorLocation = b->operatorToken; + *errorMessage = QString::fromLatin1("Invalid assignment operation in destructuring expression"); + return false; + } + lhs = b->left->leftHandSideExpressionCast(); + initializer = b->right; + Q_ASSERT(lhs); + } else { + lhs = init->leftHandSideExpressionCast(); + } + if (!lhs) { + *errorLocation = init->firstSourceLocation(); + *errorMessage = QString::fromLatin1("Destructuring target is not a left hand side expression."); + return false; + } + + if (auto *p = lhs->patternCast()) { + bindingPattern = p; + if (!p->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage)) + return false; + return true; + } + if (auto *i = cast<IdentifierExpression *>(lhs)) { + bindingIdentifier = i->name.toString(); + return true; + } + *errorLocation = lastSourceLocation(); + *errorMessage = QLatin1String("Unimplemented!"); + return false; +} + +bool PatternProperty::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) +{ + Q_ASSERT(type != SpreadElement); + if (type == Binding) + return true; + if (type == Getter || type == Setter) { + *errorLocation = firstSourceLocation(); + *errorMessage = QString::fromLatin1("Invalid getter/setter in destructuring expression."); + return false; + } + Q_ASSERT(type == Literal); + return PatternElement::convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage); +} + + void Elision::accept0(Visitor *visitor) { if (visitor->visit(this)) { @@ -1100,6 +1269,16 @@ ClassElementList *ClassElementList::finish() return front; } +Pattern *Pattern::patternCast() +{ + return this; +} + +LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast() +{ + return this; +} + } } // namespace QQmlJS::AST QT_QML_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index cc44c99c5d..10fe2e9e72 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -252,6 +252,8 @@ public: virtual BinaryExpression *binaryExpressionCast(); virtual Statement *statementCast(); virtual UiObjectMember *uiObjectMemberCast(); + virtual LeftHandSideExpression *leftHandSideExpressionCast(); + virtual Pattern *patternCast(); void accept(Visitor *visitor); static void accept(Node *node, Visitor *visitor); @@ -275,6 +277,11 @@ public: ExpressionNode *expressionCast() override; }; +class QML_PARSER_EXPORT LeftHandSideExpression : public ExpressionNode +{ + LeftHandSideExpression *leftHandSideExpressionCast() override; +}; + class QML_PARSER_EXPORT Statement: public Node { public: @@ -283,7 +290,7 @@ public: Statement *statementCast() override; }; -class QML_PARSER_EXPORT NestedExpression: public ExpressionNode +class QML_PARSER_EXPORT NestedExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NestedExpression) @@ -306,7 +313,7 @@ public: SourceLocation rparenToken; }; -class QML_PARSER_EXPORT ThisExpression: public ExpressionNode +class QML_PARSER_EXPORT ThisExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(ThisExpression) @@ -325,7 +332,7 @@ public: SourceLocation thisToken; }; -class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +class QML_PARSER_EXPORT IdentifierExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(IdentifierExpression) @@ -346,7 +353,7 @@ public: SourceLocation identifierToken; }; -class QML_PARSER_EXPORT NullExpression: public ExpressionNode +class QML_PARSER_EXPORT NullExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NullExpression) @@ -365,7 +372,7 @@ public: SourceLocation nullToken; }; -class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +class QML_PARSER_EXPORT TrueLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(TrueLiteral) @@ -384,7 +391,7 @@ public: SourceLocation trueToken; }; -class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +class QML_PARSER_EXPORT FalseLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(FalseLiteral) @@ -403,7 +410,7 @@ public: SourceLocation falseToken; }; -class QML_PARSER_EXPORT SuperLiteral : public ExpressionNode +class QML_PARSER_EXPORT SuperLiteral : public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(SuperLiteral) @@ -423,7 +430,7 @@ public: }; -class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +class QML_PARSER_EXPORT NumericLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NumericLiteral) @@ -444,7 +451,7 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(StringLiteral) @@ -465,7 +472,7 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT TemplateLiteral : public ExpressionNode +class QML_PARSER_EXPORT TemplateLiteral : public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(TemplateLiteral) @@ -488,7 +495,7 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +class QML_PARSER_EXPORT RegExpLiteral: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(RegExpLiteral) @@ -510,9 +517,11 @@ public: SourceLocation literalToken; }; -class QML_PARSER_EXPORT Pattern : public ExpressionNode +class QML_PARSER_EXPORT Pattern : public LeftHandSideExpression { - +public: + Pattern *patternCast() override; + virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) = 0; }; class QML_PARSER_EXPORT ArrayPattern : public Pattern @@ -542,6 +551,8 @@ public: bool isValidArrayLiteral(SourceLocation *errorLocation = nullptr) const; + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; + // attributes PatternElementList *elements = nullptr; Elision *elision = nullptr; @@ -570,6 +581,8 @@ public: SourceLocation lastSourceLocation() const override { return rbraceToken; } + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; + // attributes PatternPropertyList *properties = nullptr; SourceLocation lbraceToken; @@ -668,6 +681,7 @@ public: } void accept0(Visitor *visitor) override; + virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage); SourceLocation firstSourceLocation() const override { return identifierToken.isValid() ? identifierToken : (bindingPattern ? bindingPattern->firstSourceLocation() : initializer->firstSourceLocation()); } @@ -758,6 +772,7 @@ public: } void boundNames(QStringList *names) override; + bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override; // attributes PropertyName *name; @@ -875,7 +890,7 @@ public: }; -class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT ArrayMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression) @@ -899,7 +914,7 @@ public: SourceLocation rbracketToken; }; -class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT FieldMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(FieldMemberExpression) @@ -923,7 +938,7 @@ public: SourceLocation identifierToken; }; -class QML_PARSER_EXPORT TaggedTemplate : public ExpressionNode +class QML_PARSER_EXPORT TaggedTemplate : public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(TaggedTemplate) @@ -945,7 +960,7 @@ public: TemplateLiteral *templateLiteral; }; -class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +class QML_PARSER_EXPORT NewMemberExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NewMemberExpression) @@ -970,7 +985,7 @@ public: SourceLocation rparenToken; }; -class QML_PARSER_EXPORT NewExpression: public ExpressionNode +class QML_PARSER_EXPORT NewExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(NewExpression) @@ -991,7 +1006,7 @@ public: SourceLocation newToken; }; -class QML_PARSER_EXPORT CallExpression: public ExpressionNode +class QML_PARSER_EXPORT CallExpression: public LeftHandSideExpression { public: QQMLJS_DECLARE_AST_NODE(CallExpression) diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 063c43c524..465f227b6d 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -130,6 +130,7 @@ class ConditionalExpression; class Expression; // ### rename class YieldExpression; class Block; +class LeftHandSideExpression; class StatementList; class VariableStatement; class VariableDeclarationList; |