diff options
Diffstat (limited to 'src/qml/parser')
-rw-r--r-- | src/qml/parser/qqmljs.g | 707 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast.cpp | 156 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast_p.h | 383 | ||||
-rw-r--r-- | src/qml/parser/qqmljsastfwd_p.h | 43 | ||||
-rw-r--r-- | src/qml/parser/qqmljsastvisitor.cpp | 40 | ||||
-rw-r--r-- | src/qml/parser/qqmljsastvisitor_p.h | 849 | ||||
-rw-r--r-- | src/qml/parser/qqmljsengine_p.cpp | 157 | ||||
-rw-r--r-- | src/qml/parser/qqmljsengine_p.h | 90 | ||||
-rw-r--r-- | src/qml/parser/qqmljsglobal_p.h | 47 | ||||
-rw-r--r-- | src/qml/parser/qqmljskeywords_p.h | 40 | ||||
-rw-r--r-- | src/qml/parser/qqmljslexer.cpp | 1057 | ||||
-rw-r--r-- | src/qml/parser/qqmljslexer_p.h | 226 |
12 files changed, 1662 insertions, 2133 deletions
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index f10bde5ff1..57954d7d1a 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -1,41 +1,5 @@ ----------------------------------------------------------------------------- --- -- Copyright (C) 2016 The Qt Company Ltd. --- Contact: http://www.qt.io/licensing/ --- --- This file is part of the QtQml module of the Qt Toolkit. --- --- $QT_BEGIN_LICENSE:LGPL$ --- Commercial License Usage --- Licensees holding valid commercial Qt licenses may use this file in --- accordance with the commercial license agreement provided with the --- Software or, alternatively, in accordance with the terms contained in --- a written agreement between you and The Qt Company. For licensing terms --- and conditions see https://www.qt.io/terms-conditions. For further --- information use the contact form at https://www.qt.io/contact-us. --- --- GNU Lesser General Public License Usage --- Alternatively, this file may be used under the terms of the GNU Lesser --- General Public License version 3 as published by the Free Software --- Foundation and appearing in the file LICENSE.LGPL3 included in the --- packaging of this file. Please review the following information to --- ensure the GNU Lesser General Public License version 3 requirements --- will be met: https://www.gnu.org/licenses/lgpl-3.0.html. --- --- GNU General Public License Usage --- Alternatively, this file may be used under the terms of the GNU --- General Public License version 2.0 or (at your option) the GNU General --- Public license version 3 or any later version approved by the KDE Free --- Qt Foundation. The licenses are as published by the Free Software --- Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 --- included in the packaging of this file. Please review the following --- information to ensure the GNU General Public License requirements will --- be met: https://www.gnu.org/licenses/gpl-2.0.html and --- https://www.gnu.org/licenses/gpl-3.0.html. --- --- $QT_END_LICENSE$ --- ----------------------------------------------------------------------------- +-- SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only %parser QQmlJSGrammar %decl qqmljsparser_p.h @@ -81,6 +45,7 @@ %token T_COMPATIBILITY_SEMICOLON %token T_ARROW "=>" %token T_QUESTION_QUESTION "??" +%token T_QUESTION_DOT "?." %token T_ENUM "enum" %token T_ELLIPSIS "..." %token T_YIELD "yield" @@ -108,8 +73,19 @@ %token T_GET "get" %token T_SET "set" +-- token representing no token +%token T_NONE + %token T_ERROR +-- states for line by line parsing +%token T_EOL +%token T_PARTIAL_COMMENT "non closed multiline comment" +%token T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL "multiline single quote string literal" +%token T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL "multiline double quote string literal" +%token T_PARTIAL_TEMPLATE_HEAD "(template head)" +%token T_PARTIAL_TEMPLATE_MIDDLE "(template middle)" + --- feed tokens %token T_FEED_UI_PROGRAM %token T_FEED_UI_OBJECT_MEMBER @@ -131,44 +107,9 @@ %start TopLevel -/./**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +/.// Copyright (C) 2016 The Qt Company Ltd. +// Contact: https://www.qt.io/licensing/ +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> @@ -182,44 +123,9 @@ ./ -/:/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +/:// Copyright (C) 2016 The Qt Company Ltd. +// Contact: https://www.qt.io/licensing/ +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // @@ -306,14 +212,15 @@ public: AST::ExportClause *ExportClause; AST::ExportDeclaration *ExportDeclaration; AST::TypeAnnotation *TypeAnnotation; - AST::TypeArgumentList *TypeArgumentList; AST::Type *Type; AST::UiProgram *UiProgram; AST::UiHeaderItemList *UiHeaderItemList; + AST::UiPragmaValueList *UiPragmaValueList; AST::UiPragma *UiPragma; AST::UiImport *UiImport; AST::UiParameterList *UiParameterList; + AST::UiPropertyAttributes *UiPropertyAttributes; AST::UiPublicMember *UiPublicMember; AST::UiObjectDefinition *UiObjectDefinition; AST::UiObjectInitializer *UiObjectInitializer; @@ -395,6 +302,18 @@ public: inline int errorColumnNumber() const { return diagnosticMessage().loc.startColumn; } + inline bool identifierInsertionEnabled() const + { return m_identifierInsertionEnabled; } + + inline void setIdentifierInsertionEnabled(bool enable) + { m_identifierInsertionEnabled = enable; } + + inline bool incompleteBindingsEnabled() const + { return m_incompleteBindingsEnabled; } + + inline void setIncompleteBindingsEnabled(bool enable) + { m_incompleteBindingsEnabled = enable; } + protected: bool parse(int startToken); @@ -415,6 +334,7 @@ protected: AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); void pushToken(int token); + void pushTokenWithEmptyLocation(int token); int lookaheadToken(Lexer *lexer); static DiagnosticMessage compileError(const SourceLocation &location, @@ -444,8 +364,8 @@ protected: Value *sym_stack = nullptr; int *state_stack = nullptr; SourceLocation *location_stack = nullptr; - QList<QStringView> string_stack; - QList<QStringView> rawString_stack; + std::vector<QStringView> string_stack; + std::vector<QStringView> rawString_stack; AST::Node *program = nullptr; @@ -466,12 +386,14 @@ protected: QStringView yytokenraw; SourceLocation yylloc; SourceLocation yyprevlloc; + int yyprevtoken = -1; SavedToken token_buffer[TOKEN_BUFFER_SIZE]; SavedToken *first_token = nullptr; SavedToken *last_token = nullptr; int functionNestingLevel = 0; + int classNestingLevel = 0; enum CoverExpressionType { CE_Invalid, @@ -482,6 +404,8 @@ protected: CoverExpressionType coverExpressionType = CE_Invalid; QList<DiagnosticMessage> diagnostic_messages; + bool m_identifierInsertionEnabled = false; + bool m_incompleteBindingsEnabled = false; }; } // end of namespace QQmlJS @@ -557,11 +481,13 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) { QVarLengthArray<QStringView, 4> nameIds; QVarLengthArray<SourceLocation, 4> locations; + QVarLengthArray<SourceLocation, 4> dotLocations; AST::ExpressionNode *it = expr; while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { nameIds.append(m->name); locations.append(m->identifierToken); + dotLocations.append(m->dotToken); it = m->base; } @@ -573,6 +499,7 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) for (int i = nameIds.size() - 1; i != -1; --i) { currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); currentId->identifierToken = locations[i]; + currentId->dotToken = dotLocations[i]; } return currentId->finish(); @@ -594,9 +521,19 @@ void Parser::pushToken(int token) yytoken = token; } +void Parser::pushTokenWithEmptyLocation(int token) +{ + pushToken(token); + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; +} + int Parser::lookaheadToken(Lexer *lexer) { if (yytoken < 0) { + yyprevtoken = yytoken; yytoken = lexer->lex(); yylval = lexer->tokenValue(); yytokenspell = lexer->tokenSpell(); @@ -692,6 +629,7 @@ bool Parser::parse(int startToken) #endif if (action > 0) { if (action != ACCEPT_STATE) { + yyprevtoken = yytoken; yytoken = -1; sym(1).dval = yylval; stringRef(1) = yytokenspell; @@ -809,20 +747,56 @@ UiHeaderItemList: UiHeaderItemList UiImport; ./ PragmaId: JsIdentifier; +PragmaValue: JsIdentifier + | T_STRING_LITERAL; Semicolon: T_AUTOMATIC_SEMICOLON; Semicolon: T_SEMICOLON; +UiPragmaValueList: PragmaValue; +/. + case $rule_number: { + AST::UiPragmaValueList *list + = new (pool) AST::UiPragmaValueList(stringRef(1)); + list->location = loc(1); + sym(1).Node = list; + } break; +./ + +UiPragmaValueList: UiPragmaValueList T_COMMA PragmaValue; +/. + case $rule_number: { + AST::UiPragmaValueList *list + = new (pool) AST::UiPragmaValueList(sym(1).UiPragmaValueList, stringRef(3)); + list->location = loc(3); + sym(1).Node = list; + } break; +./ + UiPragma: T_PRAGMA PragmaId Semicolon; /. case $rule_number: { AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2)); pragma->pragmaToken = loc(1); + pragma->pragmaIdToken = loc(2); pragma->semicolonToken = loc(3); sym(1).Node = pragma; } break; ./ +UiPragma: T_PRAGMA PragmaId T_COLON UiPragmaValueList Semicolon; +/. + case $rule_number: { + AST::UiPragma *pragma = new (pool) AST::UiPragma( + stringRef(2), sym(4).UiPragmaValueList->finish()); + pragma->pragmaToken = loc(1); + pragma->pragmaIdToken = loc(2); + pragma->colonToken = loc(3); + pragma->semicolonToken = loc(5); + sym(1).Node = pragma; + } break; +./ + ImportId: MemberExpression; UiImport: UiImportHead Semicolon; @@ -1177,6 +1151,20 @@ case $rule_number: } break; ./ +UiObjectMember: UiQualifiedId Semicolon; +/. + case $rule_number: { + if (!m_incompleteBindingsEnabled) { + diagnostic_messages.append(compileError(loc(1), QLatin1String("Incomplete binding, expected token `:` or `{`"))); + return false; + } + AST::EmptyStatement *statement = new (pool) AST::EmptyStatement; + statement->semicolonToken = loc(2); + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(sym(1).UiQualifiedId, statement); + sym(1).Node = node; + } break; +./ + UiPropertyType: T_VAR; /. case $rule_number: Q_FALLTHROUGH(); ./ UiPropertyType: T_RESERVED_WORD; @@ -1213,10 +1201,10 @@ UiParameterListOpt: UiParameterList; } break; ./ -UiParameterList: QmlIdentifier T_COLON UiPropertyType; +UiParameterList: QmlIdentifier T_COLON Type; /. case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(3).UiQualifiedId->finish(), stringRef(1)); + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(3).Type, stringRef(1)); node->identifierToken = loc(1); node->colonToken = loc(2); node->propertyTypeToken = loc(3); @@ -1224,20 +1212,20 @@ UiParameterList: QmlIdentifier T_COLON UiPropertyType; } break; ./ -UiParameterList: UiPropertyType QmlIdentifier; +UiParameterList: Type QmlIdentifier; /. case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2)); + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).Type, stringRef(2)); node->propertyTypeToken = loc(1); node->identifierToken = loc(2); sym(1).Node = node; } break; ./ -UiParameterList: UiParameterList T_COMMA QmlIdentifier T_COLON UiPropertyType; +UiParameterList: UiParameterList T_COMMA QmlIdentifier T_COLON Type; /. case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(5).UiQualifiedId->finish(), stringRef(3)); + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(5).Type, stringRef(3)); node->propertyTypeToken = loc(5); node->commaToken = loc(2); node->identifierToken = loc(3); @@ -1246,10 +1234,10 @@ UiParameterList: UiParameterList T_COMMA QmlIdentifier T_COLON UiPropertyType; } break; ./ -UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier; +UiParameterList: UiParameterList T_COMMA Type QmlIdentifier; /. case $rule_number: { - AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4)); + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).Type, stringRef(4)); node->propertyTypeToken = loc(3); node->commaToken = loc(2); node->identifierToken = loc(4); @@ -1262,7 +1250,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN Semic case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); + node->setPropertyToken(loc(1)); node->typeToken = loc(2); node->identifierToken = loc(2); node->parameters = sym(4).UiParameterList; @@ -1276,7 +1264,7 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon; case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2)); node->type = AST::UiPublicMember::Signal; - node->propertyToken = loc(1); + node->setPropertyToken(loc(1)); node->typeToken = loc(2); node->identifierToken = loc(2); node->semicolonToken = loc(3); @@ -1284,126 +1272,97 @@ UiObjectMember: T_SIGNAL T_IDENTIFIER Semicolon; } break; ./ -UiObjectMemberListPropertyNoInitialiser: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon; -/. - case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); - node->typeModifier = stringRef(2); - node->propertyToken = loc(1); - node->typeModifierToken = loc(2); - node->typeToken = loc(4); - node->identifierToken = loc(6); - node->semicolonToken = loc(7); - sym(1).Node = node; - } break; -./ +------------------------------------------------------------------------------- +-- There is some ambiguity in whether required default property should be parsed +-- as required (default (property)) or as ((required (default)) property) +-- by reducing after each attribute modifier, we ensure that T_PROPERTY (which +-- is always available is used as the base case (so we only have to allocate the +-- node in the T_PROPERY case, and all other rules can assume that the node is +-- already available). +-------------------------------------------------------------------------------- -UiObjectMember: UiObjectMemberListPropertyNoInitialiser; +AttrRequired: T_REQUIRED %prec REDUCE_HERE; +AttrReadonly: T_READONLY %prec REDUCE_HERE; +AttrDefault: T_DEFAULT %prec REDUCE_HERE; -UiObjectMember: T_READONLY UiObjectMemberListPropertyNoInitialiser; +UiPropertyAttributes: AttrRequired UiPropertyAttributes; /. case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - sym(1).Node = node; + AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes; + if (node->isRequired()) + diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'required' attribute is not allowed."), QtCriticalMsg)); + node->m_requiredToken = loc(1); + sym(1).UiPropertyAttributes = node; } break; ./ -UiObjectMemberPropertyNoInitialiser: T_PROPERTY UiPropertyType QmlIdentifier Semicolon; +UiPropertyAttributes: AttrDefault UiPropertyAttributes; /. case $rule_number: { - AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); - node->typeToken = loc(2); - node->identifierToken = loc(3); - node->semicolonToken = loc(4); - sym(1).Node = node; + AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes; + if (node->isDefaultMember()) + diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'default' attribute is not allowed."), QtCriticalMsg)); + node->m_defaultToken = loc(1); + sym(1).UiPropertyAttributes = node; } break; ./ - -UiObjectMember: UiObjectMemberPropertyNoInitialiser; - -UiObjectMember: T_DEFAULT UiObjectMemberPropertyNoInitialiser; +UiPropertyAttributes: AttrReadonly UiPropertyAttributes; /. case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isDefaultMember = true; - node->defaultToken = loc(1); - sym(1).Node = node; + AST::UiPropertyAttributes *node = sym(2).UiPropertyAttributes; + if (node->isReadonly()) + diagnostic_messages.append(compileError(node->requiredToken(), QLatin1String("Duplicated 'readonly' attribute is not allowed."), QtCriticalMsg)); + node->m_readonlyToken = loc(1); + sym(1).UiPropertyAttributes = node; } break; ./ - -UiObjectMember: T_REQUIRED UiObjectMemberListPropertyNoInitialiser; +UiPropertyAttributes: T_PROPERTY; /. case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isRequired = true; - node->requiredToken = loc(1); - sym(1).Node = node; + AST::UiPropertyAttributes *node = new (pool) AST::UiPropertyAttributes(); + node->m_propertyToken = loc(1); + sym(1).UiPropertyAttributes = node; } break; ./ -UiObjectMember: T_DEFAULT T_REQUIRED UiObjectMemberListPropertyNoInitialiser; +UiObjectMemberListPropertyNoInitialiser: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier Semicolon; /. case $rule_number: { - AST::UiPublicMember *node = sym(3).UiPublicMember; - node->isRequired = true; - node->requiredToken = loc(2); - node->isDefaultMember = true; - node->defaultToken = loc(1); - sym(1).Node = node; - } break; -./ - -UiObjectMember: T_REQUIRED T_DEFAULT UiObjectMemberListPropertyNoInitialiser; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(3).UiPublicMember; - node->isRequired = true; - node->requiredToken = loc(1); - node->isDefaultMember = true; - node->defaultToken = loc(2); + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); + auto attributes = sym(1).UiPropertyAttributes; + node->setAttributes(attributes); + if (attributes->isReadonly()) + diagnostic_messages.append(compileError(attributes->readonlyToken(), QLatin1String("Read-only properties require an initializer."), QtWarningMsg)); + node->typeModifier = stringRef(2); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); sym(1).Node = node; } break; ./ -UiObjectMember: T_DEFAULT UiObjectMemberListPropertyNoInitialiser; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isDefaultMember = true; - node->defaultToken = loc(1); - sym(1).Node = node; - } break; -./ +UiObjectMember: UiObjectMemberListPropertyNoInitialiser; -UiObjectMember: T_DEFAULT T_REQUIRED UiObjectMemberPropertyNoInitialiser; +UiObjectMemberPropertyNoInitialiser: UiPropertyAttributes UiPropertyType QmlIdentifier Semicolon; /. case $rule_number: { - AST::UiPublicMember *node = sym(3).UiPublicMember; - node->isDefaultMember = true; - node->defaultToken = loc(1); - node->isRequired = true; - node->requiredToken = loc(2); + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); + auto attributes = sym(1).UiPropertyAttributes; + if (attributes->isReadonly()) + diagnostic_messages.append(compileError(attributes->readonlyToken(), QLatin1String("Read-only properties require an initializer."), QtCriticalMsg)); + node->setAttributes(attributes); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); sym(1).Node = node; } break; ./ -UiObjectMember: T_REQUIRED T_DEFAULT UiObjectMemberPropertyNoInitialiser; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(3).UiPublicMember; - node->isDefaultMember = true; - node->defaultToken = loc(2); - node->isRequired = true; - node->requiredToken = loc(1); - sym(1).Node = node; - } break; -./ +UiObjectMember: UiObjectMemberPropertyNoInitialiser; OptionalSemicolon: | Semicolon; /. @@ -1423,21 +1382,14 @@ UiRequired: T_REQUIRED QmlIdentifier Semicolon; UiObjectMember: UiRequired; -UiObjectMember: T_REQUIRED UiObjectMemberPropertyNoInitialiser; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->requiredToken = loc(1); - node->isRequired = true; - sym(1).Node = node; - } break; -./ - -UiObjectMemberWithScriptStatement: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; +UiObjectMemberWithScriptStatement: UiPropertyAttributes UiPropertyType QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement); - node->propertyToken = loc(1); + auto attributes = sym(1).UiPropertyAttributes; + if (attributes->isRequired()) + diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtCriticalMsg)); + node->setAttributes(attributes); node->typeToken = loc(2); node->identifierToken = loc(3); node->colonToken = loc(4); @@ -1445,47 +1397,48 @@ UiObjectMemberWithScriptStatement: T_PROPERTY UiPropertyType QmlIdentifier T_COL } break; ./ -UiObjectMember: UiObjectMemberWithScriptStatement; - -UiObjectMember: T_READONLY UiObjectMemberWithScriptStatement; +UiObjectMemberWithScriptStatement: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON UiScriptStatement OptionalSemicolon; /. case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isReadonlyMember = true; - node->readonlyToken = loc(1); + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6), sym(8).Statement); + node->typeModifier = stringRef(2); + auto attributes = sym(1).UiPropertyAttributes; + if (attributes->isRequired()) + diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtCriticalMsg)); + node->setAttributes(attributes); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->colonToken = loc(7); sym(1).Node = node; } break; ./ -UiObjectMember: T_DEFAULT UiObjectMemberWithScriptStatement; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isDefaultMember = true; - node->defaultToken = loc(1); - sym(1).Node = node; - } break; -./ +UiObjectMember: UiObjectMemberWithScriptStatement; -UiObjectMemberWithArray: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon; +UiObjectMemberWithArray: UiPropertyAttributes T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON ExpressionStatementLookahead T_LBRACKET UiArrayMemberList T_RBRACKET Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6)); + auto attributes = sym(1).UiPropertyAttributes; + if (attributes->isRequired()) + diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtCriticalMsg)); + node->setAttributes(attributes); node->typeModifier = stringRef(2); - node->propertyToken = loc(1); node->typeModifierToken = loc(2); node->typeToken = loc(4); node->identifierToken = loc(6); node->semicolonToken = loc(7); // insert a fake ';' before ':' + node->colonToken = loc(7); AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); propertyName->identifierToken = loc(6); - propertyName->next = 0; + propertyName->next = nullptr; - AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(9).UiArrayMemberList->finish()); + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(10).UiArrayMemberList->finish()); binding->colonToken = loc(7); - binding->lbracketToken = loc(8); - binding->rbracketToken = loc(10); + binding->lbracketToken = loc(9); + binding->rbracketToken = loc(11); node->binding = binding; @@ -1495,28 +1448,22 @@ UiObjectMemberWithArray: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIde UiObjectMember: UiObjectMemberWithArray; -UiObjectMember: T_READONLY UiObjectMemberWithArray; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - sym(1).Node = node; - } break; -./ - -UiObjectMemberExpressionStatementLookahead: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon; +UiObjectMemberExpressionStatementLookahead: UiPropertyAttributes UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer Semicolon; /. case $rule_number: { AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3)); - node->propertyToken = loc(1); + auto attributes = sym(1).UiPropertyAttributes; + if (attributes->isRequired()) + diagnostic_messages.append(compileError(attributes->requiredToken(), QLatin1String("Required properties with initializer do not make sense."), QtWarningMsg)); + node->setAttributes(attributes); node->typeToken = loc(2); node->identifierToken = loc(3); node->semicolonToken = loc(4); // insert a fake ';' before ':' + node->colonToken = loc(4); AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); propertyName->identifierToken = loc(3); - propertyName->next = 0; + propertyName->next = nullptr; AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer); @@ -1530,16 +1477,6 @@ UiObjectMemberExpressionStatementLookahead: T_PROPERTY UiPropertyType QmlIdentif UiObjectMember: UiObjectMemberExpressionStatementLookahead; -UiObjectMember: T_READONLY UiObjectMemberExpressionStatementLookahead; -/. - case $rule_number: { - AST::UiPublicMember *node = sym(2).UiPublicMember; - node->isReadonlyMember = true; - node->readonlyToken = loc(1); - sym(1).Node = node; - } break; -./ - UiObjectMember: GeneratorDeclaration; /. case $rule_number: { @@ -1590,6 +1527,8 @@ UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE; case $rule_number: { AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish()); enumDeclaration->enumToken = loc(1); + enumDeclaration->identifierToken = loc(2); + enumDeclaration->lbraceToken = loc(3); enumDeclaration->rbraceToken = loc(5); sym(1).Node = enumDeclaration; break; @@ -1605,6 +1544,7 @@ UiObjectMember: T_COMPONENT T_IDENTIFIER T_COLON UiObjectDefinition; } auto inlineComponent = new (pool) AST::UiInlineComponent(stringRef(2), sym(4).UiObjectDefinition); inlineComponent->componentToken = loc(1); + inlineComponent->identifierToken = loc(2); sym(1).Node = inlineComponent; } break; ./ @@ -1630,6 +1570,18 @@ EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; } ./ + +EnumMemberList: T_IDENTIFIER T_EQ T_MINUS T_NUMERIC_LITERAL; +/. + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), -sym(4).dval); + node->memberToken = loc(1); + node->valueToken = combine(loc(3), loc(4)); + sym(1).Node = node; + break; + } +./ + EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER; /. case $rule_number: { @@ -1651,6 +1603,18 @@ EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL; } ./ + +EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_MINUS T_NUMERIC_LITERAL; +/. + case $rule_number: { + AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), -sym(6).dval); + node->memberToken = loc(3); + node->valueToken = combine(loc(5), loc(6)); + sym(1).Node = node; + break; + } +./ + QmlIdentifier: T_IDENTIFIER | T_PROPERTY | T_SIGNAL @@ -1684,28 +1648,35 @@ BindingIdentifier: IdentifierReference; -- Types -------------------------------------------------------------------------------------------------------- -TypeArguments: Type; +Type: UiQualifiedId T_LT SimpleType T_GT; /. case $rule_number: { - sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).Type); + sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId, sym(3).Type); } break; ./ -TypeArguments: TypeArguments T_COMMA Type; +Type: SimpleType; + +SimpleType: T_RESERVED_WORD; /. case $rule_number: { - sym(1).TypeArgumentList = new (pool) AST::TypeArgumentList(sym(1).TypeArgumentList, sym(3).Type); + AST::UiQualifiedId *id = new (pool) AST::UiQualifiedId(stringRef(1)); + id->identifierToken = loc(1); + sym(1).Type = new (pool) AST::Type(id->finish()); } break; ./ -Type: UiQualifiedId T_LT TypeArguments T_GT; +SimpleType: UiQualifiedId; /. case $rule_number: { - sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId, sym(3).TypeArgumentList->finish()); + sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId); } break; ./ -Type: T_RESERVED_WORD; +SimpleType: T_VAR; +/. case $rule_number: Q_FALLTHROUGH(); ./ + +SimpleType: T_VOID; /. case $rule_number: { AST::UiQualifiedId *id = new (pool) AST::UiQualifiedId(stringRef(1)); @@ -1714,13 +1685,6 @@ Type: T_RESERVED_WORD; } break; ./ -Type: UiQualifiedId; -/. - case $rule_number: { - sym(1).Type = new (pool) AST::Type(sym(1).UiQualifiedId); - } break; -./ - TypeAnnotation: T_COLON Type; /. case $rule_number: { @@ -1729,14 +1693,6 @@ TypeAnnotation: T_COLON Type; } break; ./ - -TypeExpression: Type; -/. - case $rule_number: { - sym(1).Expression = new (pool) AST::TypeExpression(sym(1).Type); - } break; -./ - TypeAnnotationOpt: TypeAnnotation; TypeAnnotationOpt: ; /. @@ -2100,7 +2056,6 @@ PropertyDefinition: IdentifierReference; AST::IdentifierExpression *expr = new (pool) AST::IdentifierExpression(stringRef(1)); expr->identifierToken = loc(1); AST::PatternProperty *node = new (pool) AST::PatternProperty(name, expr); - node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -2121,8 +2076,8 @@ CoverInitializedName: IdentifierReference Initializer_In; if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) c->name = stringRef(1); AST::BinaryExpression *assignment = new (pool) AST::BinaryExpression(left, QSOperator::Assign, sym(2).Expression); + assignment->operatorToken = loc(2); AST::PatternProperty *node = new (pool) AST::PatternProperty(name, assignment); - node->colonToken = loc(1); sym(1).Node = node; } break; @@ -2235,7 +2190,9 @@ Initializer: T_EQ AssignmentExpression; Initializer_In: T_EQ AssignmentExpression_In; /. case $rule_number: { - sym(1) = sym(2); + auto node = new (pool) AST::InitializerExpression(sym(2).Expression); + node->equalToken = loc(1); + sym(1).Expression = node; } break; ./ @@ -2253,7 +2210,14 @@ InitializerOpt: Initializer; InitializerOpt_In: Initializer_In; TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE; -/. case $rule_number: Q_FALLTHROUGH(); ./ +/. + case $rule_number: { + AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), rawStringRef(1), nullptr); + node->literalToken = loc(1); + node->hasNoSubstitution = true; + sym(1).Node = node; + } break; +./ TemplateSpans: T_TEMPLATE_TAIL; /. @@ -2301,7 +2265,16 @@ MemberExpression: MemberExpression T_LBRACKET Expression_In T_RBRACKET; sym(1).Node = node; } break; ./ - +MemberExpression: MemberExpression T_QUESTION_DOT T_LBRACKET Expression_In T_RBRACKET; +/. + case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(4).Expression); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + node->isOptional = true; + sym(1).Node = node; + } break; +./ -- the identifier has to be "target", catched at codegen time NewTarget: T_NEW T_DOT T_IDENTIFIER; @@ -2324,6 +2297,17 @@ MemberExpression: MemberExpression T_DOT IdentifierName; } break; ./ +MemberExpression: MemberExpression T_QUESTION_DOT IdentifierName; +/. + case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + node->isOptional = true; + sym(1).Node = node; + } break; +./ + MemberExpression: MetaProperty; MemberExpression: T_NEW MemberExpression T_LPAREN Arguments T_RPAREN; @@ -2372,6 +2356,17 @@ CallExpression: MemberExpression T_LPAREN Arguments T_RPAREN; } break; ./ +CallExpression: MemberExpression T_QUESTION_DOT T_LPAREN Arguments T_RPAREN; +/. + case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(4).ArgumentList); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->isOptional = true; + sym(1).Node = node; + } break; +./ + CallExpression: Super T_LPAREN Arguments T_RPAREN; /. case $rule_number: Q_FALLTHROUGH(); ./ CallExpression: CallExpression T_LPAREN Arguments T_RPAREN; @@ -2384,6 +2379,18 @@ CallExpression: CallExpression T_LPAREN Arguments T_RPAREN; } break; ./ +CallExpression: CallExpression T_QUESTION_DOT T_LPAREN Arguments T_RPAREN; +/. + case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(4).ArgumentList); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->isOptional = true; + sym(1).Node = node; + } break; +./ + + CallExpression: CallExpression T_LBRACKET Expression_In T_RBRACKET; /. case $rule_number: { @@ -2394,6 +2401,17 @@ CallExpression: CallExpression T_LBRACKET Expression_In T_RBRACKET; } break; ./ +CallExpression: CallExpression T_QUESTION_DOT T_LBRACKET Expression_In T_RBRACKET; +/. + case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(4).Expression); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + node->isOptional = true; + sym(1).Node = node; + } break; +./ + CallExpression: CallExpression T_DOT IdentifierName; /. case $rule_number: { @@ -2404,6 +2422,17 @@ CallExpression: CallExpression T_DOT IdentifierName; } break; ./ +CallExpression: CallExpression T_QUESTION_DOT IdentifierName; +/. + case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + node->isOptional = true; + sym(1).Node = node; + } break; +./ + Arguments: ; /. case $rule_number: { @@ -2697,30 +2726,22 @@ RelationalOperator: T_INSTANCEOF; sym(1).ival = QSOperator::InstanceOf; } break; ./ - -RelationalExpression_In: RelationalExpression_In T_IN ShiftExpression; +RelationalOperator: T_AS; /. case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); - node->operatorToken = loc(2); - sym(1).Node = node; + sym(1).ival = QSOperator::As; } break; ./ -TypeAssertExpression_In: RelationalExpression_In T_AS TypeExpression; -/. case $rule_number: Q_FALLTHROUGH(); ./ -TypeAssertExpression: RelationalExpression T_AS TypeExpression; +RelationalExpression_In: RelationalExpression_In T_IN ShiftExpression; /. case $rule_number: { - AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::As, sym(3).Expression); + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; ./ -RelationalExpression_In: TypeAssertExpression_In; -RelationalExpression: TypeAssertExpression; - EqualityExpression_In: RelationalExpression_In; EqualityExpression: RelationalExpression; @@ -2897,6 +2918,9 @@ AssignmentExpression: LeftHandSideExpression T_EQ AssignmentExpression; AssignmentExpression_In: LeftHandSideExpression T_EQ AssignmentExpression_In; /. case $rule_number: { + if (sym(1).Expression->containsOptionalChain()) { + syntaxError(loc(1), QStringLiteral("Optional chains are not permitted on the left-hand-side in assignments")); + } // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral if (AST::Pattern *p = sym(1).Expression->patternCast()) { SourceLocation errorLoc; @@ -2927,6 +2951,9 @@ AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpres AssignmentExpression_In: LeftHandSideExpression AssignmentOperator AssignmentExpression_In; /. case $rule_number: { + if (sym(1).Expression->containsOptionalChain()) { + syntaxError(loc(1), QStringLiteral("Optional chains are not permitted on the left-hand-side in assignments")); + } AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; @@ -3355,14 +3382,15 @@ BindingElisionElement: ElisionOpt BindingElement; BindingProperty: BindingIdentifier InitializerOpt_In; /. case $rule_number: { - AST::StringLiteralPropertyName *name = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1)); name->propertyNameToken = loc(1); // if initializer is an anonymous function expression, we need to assign identifierref as it's name if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression)) f->name = stringRef(1); if (auto *c = asAnonymousClassDefinition(sym(2).Expression)) c->name = stringRef(1); - sym(1).Node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression); + AST::PatternProperty *node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression); + sym(1).Node = node; } break; ./ @@ -3370,6 +3398,7 @@ BindingProperty: PropertyName T_COLON BindingIdentifier InitializerOpt_In; /. case $rule_number: { AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, stringRef(3), sym(4).Expression); + node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -3378,6 +3407,7 @@ BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In; /. case $rule_number: { AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Pattern, sym(4).Expression); + node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -4043,14 +4073,14 @@ ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpress /. case $rule_number: { AST::ReturnStatement *ret = new (pool) AST::ReturnStatement(sym(4).Expression); - ret->returnToken = sym(4).Node->firstSourceLocation(); - ret->semicolonToken = sym(4).Node->lastSourceLocation(); + ret->returnToken = sym(4).Node->firstSourceLocation().startZeroLengthLocation(); + ret->semicolonToken = sym(4).Node->lastSourceLocation().endZeroLengthLocation(driver->code()); AST::StatementList *statements = (new (pool) AST::StatementList(ret))->finish(); AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringView(), sym(1).FormalParameterList, statements); f->isArrowFunction = true; - f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); - f->lbraceToken = sym(4).Node->firstSourceLocation(); - f->rbraceToken = sym(4).Node->lastSourceLocation(); + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation().startZeroLengthLocation() : loc(1).startZeroLengthLocation(); + f->lbraceToken = sym(4).Node->firstSourceLocation().startZeroLengthLocation(); + f->rbraceToken = sym(4).Node->lastSourceLocation().endZeroLengthLocation(driver->code()); sym(1).Node = f; } break; ./ @@ -4062,8 +4092,8 @@ ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK Fun case $rule_number: { AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringView(), sym(1).FormalParameterList, sym(6).StatementList); f->isArrowFunction = true; - f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); - f->lbraceToken = loc(6); + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation().startZeroLengthLocation() : loc(1).startZeroLengthLocation(); + f->lbraceToken = loc(5); f->rbraceToken = loc(7); sym(1).Node = f; } break; @@ -4136,7 +4166,6 @@ MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_R f->rbraceToken = loc(9); f->isGenerator = true; AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method); - node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -4154,7 +4183,6 @@ MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN TypeAnnotationOpt Functio f->lbraceToken = loc(6); f->rbraceToken = loc(8); AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter); - node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -4171,7 +4199,6 @@ MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN f->lbraceToken = loc(7); f->rbraceToken = loc(9); AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter); - node->colonToken = loc(2); sym(1).Node = node; } break; ./ @@ -4350,16 +4377,16 @@ ClassDeclaration_Default: ClassDeclaration; ClassLBrace: T_LBRACE; /. case $rule_number: { - lexer->setStaticIsKeyword(true); + if (++classNestingLevel == 1) + lexer->setStaticIsKeyword(true); } break; ./ ClassRBrace: T_RBRACE; -/. case $rule_number: ./ -ClassStaticQualifier: T_STATIC; /. case $rule_number: { - lexer->setStaticIsKeyword(false); + if (--classNestingLevel == 0) + lexer->setStaticIsKeyword(false); } break; ./ @@ -4414,10 +4441,9 @@ ClassElement: MethodDefinition; } break; ./ -ClassElement: ClassStaticQualifier MethodDefinition; +ClassElement: T_STATIC MethodDefinition; /. case $rule_number: { - lexer->setStaticIsKeyword(true); AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(2).PatternProperty, true); sym(1).Node = node; } break; @@ -4802,35 +4828,26 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; if (first_token == last_token) { const int errorState = state_stack[tos]; + // automatic insertion of missing identifiers after dots + if (yytoken != -1 && m_identifierInsertionEnabled && t_action(errorState, T_IDENTIFIER) && yyprevtoken == T_DOT) { +#ifdef PARSER_DEBUG + qDebug() << "Inserting missing identifier between" << spell[yyprevtoken] << "and" + << spell[yytoken]; +#endif + pushTokenWithEmptyLocation(T_IDENTIFIER); + action = errorState; + goto _Lcheck_token; + } + + // automatic insertion of `;' if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) { #ifdef PARSER_DEBUG qDebug() << "Inserting automatic semicolon."; #endif - SavedToken &tk = token_buffer[0]; - tk.token = yytoken; - tk.dval = yylval; - tk.spell = yytokenspell; - tk.raw = yytokenraw; - tk.loc = yylloc; - - yylloc = yyprevlloc; - yylloc.offset += yylloc.length; - yylloc.startColumn += yylloc.length; - yylloc.length = 0; - - //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'"); - //diagnostic_messages.append(compileError(yyloc, msg, QtWarningMsg)); - - first_token = &token_buffer[0]; - last_token = &token_buffer[1]; - - yytoken = T_SEMICOLON; - yylval = 0; - + pushTokenWithEmptyLocation(T_SEMICOLON); action = errorState; - goto _Lcheck_token; } diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 4b5d866662..8f13ad193e 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -1,48 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include <QString> #include <QLocale> +#include "common/qqmljssourcelocation_p.h" #include "qqmljsast_p.h" #include "qqmljsastvisitor_p.h" #include <qlocale.h> +#include <algorithm> +#include <array> + QT_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { @@ -118,6 +87,44 @@ ExpressionNode *ExpressionNode::expressionCast() return this; } +bool ExpressionNode::containsOptionalChain() const +{ + for (const Node *node = this;;) { + switch (node->kind) { + case Kind_FieldMemberExpression: { + const auto *fme = AST::cast<const FieldMemberExpression*>(node); + if (fme->isOptional) + return true; + node = fme->base; + break; + } + case Kind_ArrayMemberExpression: { + const auto *ame = AST::cast<const ArrayMemberExpression*>(node); + if (ame->isOptional) + return true; + node = ame->base; + break; + } + case Kind_CallExpression: { + const auto *ce = AST::cast<const CallExpression*>(node); + if (ce->isOptional) + return true; + node = ce->base; + break; + } + case Kind_NestedExpression: { + const auto *ne = AST::cast<const NestedExpression*>(node); + node = ne->expression; + break; + } + default: + // These unhandled nodes lead to invalid lvalues anyway, so they do not need to be handled here. + return false; + } + } + return false; +} + FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *pool) { AST::ExpressionNode *expr = this; @@ -1009,13 +1016,9 @@ BoundNames FormalParameterList::formals() const // change the name of the earlier argument to enforce the lookup semantics from the spec formals[duplicateIndex].id += QLatin1String("#") + QString::number(i); } - formals += { - name, - it->element->typeAnnotation, - it->element->isInjectedSignalParameter - ? BoundName::Injected - : BoundName::Declared - }; + formals += { name, it->element->firstSourceLocation(), it->element->typeAnnotation, + it->element->isInjectedSignalParameter ? BoundName::Injected + : BoundName::Declared }; } ++i; } @@ -1043,17 +1046,10 @@ void FormalParameterList::accept0(BaseVisitor *visitor) } } -FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *pool) +FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *) { FormalParameterList *front = next; next = nullptr; - - int i = 0; - for (const FormalParameterList *it = this; it; it = it->next) { - if (it->element && it->element->bindingIdentifier.isEmpty()) - it->element->bindingIdentifier = pool->newString(QLatin1String("arg#") + QString::number(i)); - ++i; - } return front; } @@ -1162,8 +1158,8 @@ void ExportClause::accept0(BaseVisitor *visitor) void ExportDeclaration::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { - accept(fromClause, visitor); accept(exportClause, visitor); + accept(fromClause, visitor); accept(variableStatementOrDeclaration, visitor); } @@ -1305,41 +1301,40 @@ void Type::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { accept(typeId, visitor); - accept(typeArguments, visitor); + accept(typeArgument, visitor); } visitor->endVisit(this); } -void TypeArgumentList::accept0(BaseVisitor *visitor) +void TypeAnnotation::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { - for (TypeArgumentList *it = this; it; it = it->next) - accept(it->typeId, visitor); + accept(type, visitor); } visitor->endVisit(this); } -void TypeAnnotation::accept0(BaseVisitor *visitor) +void UiImport::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { - accept(type, visitor); + accept(importUri, visitor); + // accept(version, visitor); // accept manually in visit if interested } visitor->endVisit(this); } -void UiImport::accept0(BaseVisitor *visitor) +void UiPragmaValueList::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { - accept(importUri, visitor); - // accept(version, visitor); // accept manually in visit if interested } visitor->endVisit(this); } + void UiPragma::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { @@ -1399,6 +1394,11 @@ void TaggedTemplate::accept0(BaseVisitor *visitor) visitor->endVisit(this); } +void InitializerExpression::accept0(BaseVisitor *visitor) +{ + expression->accept0(visitor); +} + void PatternElement::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { @@ -1418,8 +1418,8 @@ void PatternElement::boundNames(BoundNames *names) else if (PatternPropertyList *p = propertyList()) p->boundNames(names); } else { - names->append({bindingIdentifier.toString(), typeAnnotation, - isInjectedSignalParameter ? BoundName::Injected : BoundName::Declared}); + names->append({ bindingIdentifier.toString(), firstSourceLocation(), typeAnnotation, + isInjectedSignalParameter ? BoundName::Injected : BoundName::Declared }); } } @@ -1557,17 +1557,11 @@ QString Type::toString() const void Type::toString(QString *out) const { - for (QQmlJS::AST::UiQualifiedId *it = typeId; it; it = it->next) { - out->append(it->name); + typeId->toString(out); - if (it->next) - out->append(QLatin1Char('.')); - } - - if (typeArguments) { + if (typeArgument) { out->append(QLatin1Char('<')); - if (auto subType = static_cast<TypeArgumentList*>(typeArguments)->typeId) - subType->toString(out); + typeArgument->toString(out); out->append(QLatin1Char('>')); }; } @@ -1610,6 +1604,20 @@ void UiAnnotation::accept0(BaseVisitor *visitor) visitor->endVisit(this); } +SourceLocation UiPropertyAttributes::firstSourceLocation() const +{ + std::array<const SourceLocation *, 4> tokens {&m_propertyToken, &m_defaultToken, &m_readonlyToken, &m_requiredToken}; + const auto it = std::min_element(tokens.begin(), tokens.end(), compareLocationsByBegin<true>); + return **it; +} + +SourceLocation UiPropertyAttributes::lastSourceLocation() const +{ + std::array<const SourceLocation *, 4> tokens {&m_propertyToken, &m_defaultToken, &m_readonlyToken, &m_requiredToken}; + const auto it = std::max_element(tokens.begin(), tokens.end(), compareLocationsByBegin<false>); + return **it; +} + } } // namespace QQmlJS::AST QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index cb6d5fa3ee..2bb9b3f001 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSAST_P_H #define QQMLJSAST_P_H @@ -56,11 +20,19 @@ #include <private/qqmljsmemorypool_p.h> -#include <QtCore/qstring.h> +#include <QtCore/qtaggedpointer.h> #include <QtCore/qversionnumber.h> +#include <type_traits> + QT_BEGIN_NAMESPACE +class QString; + +namespace QQmlJS { + class Parser; +} + #define QQMLJS_DECLARE_AST_NODE(name) \ enum { K = Kind_##name }; @@ -126,10 +98,10 @@ enum class VariableScope { template <typename T1, typename T2> T1 cast(T2 *ast) { - if (ast && ast->kind == static_cast<T1>(0)->K) + if (ast && ast->kind == std::remove_pointer_t<T1>::K) return static_cast<T1>(ast); - return 0; + return nullptr; } FunctionExpression *asAnonymousFunctionDefinition(AST::Node *n); @@ -177,6 +149,7 @@ public: Kind_ClassDeclaration, Kind_IdentifierExpression, Kind_IdentifierPropertyName, + Kind_InitializerExpression, Kind_ComputedPropertyName, Kind_IfStatement, Kind_LabelledStatement, @@ -239,7 +212,7 @@ public: Kind_PatternProperty, Kind_PatternPropertyList, Kind_Type, - Kind_TypeArgumentList, + Kind_TypeArgument, Kind_TypeAnnotation, Kind_UiArrayBinding, @@ -250,9 +223,11 @@ public: Kind_UiObjectInitializer, Kind_UiObjectMemberList, Kind_UiArrayMemberList, + Kind_UiPragmaValueList, Kind_UiPragma, Kind_UiProgram, Kind_UiParameterList, + Kind_UiPropertyAttributes, Kind_UiPublicMember, Kind_UiQualifiedId, Kind_UiScriptBinding, @@ -307,12 +282,6 @@ public: node->accept(visitor); } - // ### Remove when we can. This is part of the qmldevtools library, though. - inline static void acceptChild(Node *node, BaseVisitor *visitor) - { - return accept(node, visitor); - } - virtual void accept0(BaseVisitor *visitor) = 0; virtual SourceLocation firstSourceLocation() const = 0; virtual SourceLocation lastSourceLocation() const = 0; @@ -360,12 +329,33 @@ public: { return identifierToken; } SourceLocation lastSourceLocation() const override - { return lastListElement(this)->identifierToken; } + { + return lastListElement(this)->lastOwnSourceLocation(); + } + + SourceLocation lastOwnSourceLocation() const { return identifierToken; } + + QString toString() const + { + QString result; + toString(&result); + return result; + } + + void toString(QString *out) const + { + for (const UiQualifiedId *it = this; it; it = it->next) { + out->append(it->name); + if (it->next) + out->append(QLatin1Char('.')); + } + } // attributes UiQualifiedId *next; QStringView name; SourceLocation identifierToken; + SourceLocation dotToken; }; class QML_PARSER_EXPORT Type: public Node @@ -373,9 +363,9 @@ class QML_PARSER_EXPORT Type: public Node public: QQMLJS_DECLARE_AST_NODE(Type) - Type(UiQualifiedId *typeId, Node *typeArguments = nullptr) + Type(UiQualifiedId *typeId, Type *typeArgument = nullptr) : typeId(typeId) - , typeArguments(typeArguments) + , typeArgument(typeArgument ? typeArgument->typeId : nullptr) { kind = K; } void accept0(BaseVisitor *visitor) override; @@ -384,53 +374,14 @@ public: { return typeId->firstSourceLocation(); } SourceLocation lastSourceLocation() const override - { return typeArguments ? typeArguments->lastSourceLocation() : typeId->lastSourceLocation(); } + { return typeArgument ? typeArgument->lastSourceLocation() : typeId->lastSourceLocation(); } QString toString() const; void toString(QString *out) const; // attributes UiQualifiedId *typeId; - Node *typeArguments; // TypeArgumentList -}; - - -class QML_PARSER_EXPORT TypeArgumentList: public Node -{ -public: - QQMLJS_DECLARE_AST_NODE(TypeArgumentList) - - TypeArgumentList(Type *typeId) - : typeId(typeId) - , next(nullptr) - { kind = K; } - - TypeArgumentList(TypeArgumentList *previous, Type *typeId) - : typeId(typeId) - { - kind = K; - next = previous->next; - previous->next = this; - } - - void accept0(BaseVisitor *visitor) override; - - SourceLocation firstSourceLocation() const override - { return typeId->firstSourceLocation(); } - - SourceLocation lastSourceLocation() const override - { return lastListElement(this)->typeId->lastSourceLocation(); } - - inline TypeArgumentList *finish() - { - TypeArgumentList *front = next; - next = nullptr; - return front; - } - -// attributes - Type *typeId; - TypeArgumentList *next; + UiQualifiedId *typeArgument; }; class QML_PARSER_EXPORT TypeAnnotation: public Node @@ -460,6 +411,7 @@ public: ExpressionNode() {} ExpressionNode *expressionCast() override; + bool containsOptionalChain() const; AST::FormalParameterList *reparseAsFormalParameterList(MemoryPool *pool); @@ -734,6 +686,7 @@ public: void accept0(BaseVisitor *visitor) override; + bool hasNoSubstitution = false; QStringView value; QStringView rawValue; ExpressionNode *expression; @@ -893,12 +846,14 @@ struct QML_PARSER_EXPORT BoundName }; QString id; + QQmlJS::SourceLocation location; QTaggedPointer<TypeAnnotation, Type> typeAnnotation; - BoundName(const QString &id, TypeAnnotation *typeAnnotation, Type type = Declared) - : id(id), typeAnnotation(typeAnnotation, type) + BoundName(const QString &id, const QQmlJS::SourceLocation &location, + TypeAnnotation *typeAnnotation, Type type = Declared) + : id(id), location(location), typeAnnotation(typeAnnotation, type) {} BoundName() = default; - QString typeName() const { return typeAnnotation ? typeAnnotation->type->toString() : QString(); } + bool isInjected() const { return typeAnnotation.tag() == Injected; } }; @@ -919,6 +874,39 @@ struct BoundNames : public QVector<BoundName> } }; +/*! +\internal +This class is needed to pass the information about the equalToken in the parser, and is only needed +during AST construction. It behaves exactly like the expression it contains: that avoids changing +all the usages in qqmljs.g from ExpressionNode to InitializerExpression for every rule expecting a +InitializerOpt_In or InitializerOpt. +*/ +class QML_PARSER_EXPORT InitializerExpression : public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(InitializerExpression) + + InitializerExpression(ExpressionNode *e) : expression(e) { kind = K; } + + void accept0(BaseVisitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return equalToken; } + + SourceLocation lastSourceLocation() const override { return expression->lastSourceLocation(); } + + FunctionExpression *asFunctionDefinition() override + { + return expression->asFunctionDefinition(); + } + + ClassExpression *asClassDefinition() override { return expression->asClassDefinition(); } + + // attributes + ExpressionNode *expression; + SourceLocation equalToken; +}; + class QML_PARSER_EXPORT PatternElement : public Node { public: @@ -939,9 +927,28 @@ public: Binding, }; +private: + /*! + \internal + Hide InitializerExpression from the AST. InitializerExpression is only needed during parsing for + the AST construction, and it is not possible for the parser to directly embed the location of + equal tokens inside the PatternElement without the InitializerExpression. + */ + void unwrapInitializer() + { + if (auto unwrapped = AST::cast<InitializerExpression *>(initializer)) { + equalToken = unwrapped->equalToken; + initializer = unwrapped->expression; + } + } +public: + PatternElement(ExpressionNode *i = nullptr, Type t = Literal) : initializer(i), type(t) - { kind = K; } + { + kind = K; + unwrapInitializer(); + } PatternElement(QStringView n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding) : bindingIdentifier(n), initializer(i), type(t) @@ -949,6 +956,7 @@ public: { Q_ASSERT(t >= RestElement); kind = K; + unwrapInitializer(); } PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding) @@ -956,6 +964,7 @@ public: { Q_ASSERT(t >= RestElement); kind = K; + unwrapInitializer(); } void accept0(BaseVisitor *visitor) override; @@ -979,6 +988,7 @@ public: // attributes SourceLocation identifierToken; + SourceLocation equalToken; QStringView bindingIdentifier; ExpressionNode *bindingTarget = nullptr; ExpressionNode *initializer = nullptr; @@ -1198,6 +1208,7 @@ public: ExpressionNode *expression; SourceLocation lbracketToken; SourceLocation rbracketToken; + bool isOptional = false; }; class QML_PARSER_EXPORT FieldMemberExpression: public LeftHandSideExpression @@ -1222,6 +1233,7 @@ public: QStringView name; SourceLocation dotToken; SourceLocation identifierToken; + bool isOptional = false; }; class QML_PARSER_EXPORT TaggedTemplate : public LeftHandSideExpression @@ -1314,6 +1326,7 @@ public: ArgumentList *arguments; SourceLocation lparenToken; SourceLocation rparenToken; + bool isOptional = false; }; class QML_PARSER_EXPORT ArgumentList: public Node @@ -2957,7 +2970,6 @@ public: ExportDeclaration(FromClause *fromClause) : fromClause(fromClause) { - exportAll = true; kind = K; } @@ -2980,6 +2992,11 @@ public: kind = K; } + bool exportsAll() const + { + return fromClause && !exportClause; + } + void accept0(BaseVisitor *visitor) override; SourceLocation firstSourceLocation() const override @@ -2989,7 +3006,6 @@ public: // attributes SourceLocation exportToken; - bool exportAll = false; ExportClause *exportClause = nullptr; FromClause *fromClause = nullptr; Node *variableStatementOrDeclaration = nullptr; @@ -3122,13 +3138,53 @@ public: UiObjectMember *member; }; +class QML_PARSER_EXPORT UiPragmaValueList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiPragmaValueList) + + UiPragmaValueList(QStringView value) + : value(value) + , next(this) + { + kind = K; + } + + UiPragmaValueList(UiPragmaValueList *previous, QStringView value) + : value(value) + { + kind = K; + next = previous->next; + previous->next = this; + } + + void accept0(BaseVisitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return location; } + + SourceLocation lastSourceLocation() const override + { return lastListElement(this)->location; } + + UiPragmaValueList *finish() + { + UiPragmaValueList *head = next; + next = nullptr; + return head; + } + + QStringView value; + UiPragmaValueList *next; + SourceLocation location; +}; + class QML_PARSER_EXPORT UiPragma: public Node { public: QQMLJS_DECLARE_AST_NODE(UiPragma) - UiPragma(QStringView name) - : name(name) + UiPragma(QStringView name, UiPragmaValueList *values = nullptr) + : name(name), values(values) { kind = K; } void accept0(BaseVisitor *visitor) override; @@ -3141,7 +3197,10 @@ public: // attributes QStringView name; + UiPragmaValueList *values; SourceLocation pragmaToken; + SourceLocation pragmaIdToken; + SourceLocation colonToken; SourceLocation semicolonToken; }; @@ -3316,11 +3375,11 @@ class QML_PARSER_EXPORT UiParameterList: public Node public: QQMLJS_DECLARE_AST_NODE(UiParameterList) - UiParameterList(UiQualifiedId *t, QStringView n): + UiParameterList(Type *t, QStringView n): type (t), name (n), next (this) { kind = K; } - UiParameterList(UiParameterList *previous, UiQualifiedId *t, QStringView n): + UiParameterList(UiParameterList *previous, Type *t, QStringView n): type (t), name (n) { kind = K; @@ -3336,7 +3395,12 @@ public: SourceLocation lastSourceLocation() const override { auto last = lastListElement(this); - return (last->colonToken.isValid() ? last->propertyTypeToken : last->identifierToken); + return last->lastOwnSourceLocation(); + } + + SourceLocation lastOwnSourceLocation() const + { + return (colonToken.isValid() ? propertyTypeToken : identifierToken); } inline UiParameterList *finish () @@ -3347,7 +3411,7 @@ public: } // attributes - UiQualifiedId *type; + Type *type; QStringView name; UiParameterList *next; SourceLocation commaToken; @@ -3356,6 +3420,46 @@ public: SourceLocation colonToken; }; +class QML_PARSER_EXPORT UiPropertyAttributes : public Node +{ + QQMLJS_DECLARE_AST_NODE(UiPropertyAttributes) +public: + UiPropertyAttributes() { kind = K; } + + SourceLocation defaultToken() const { return m_defaultToken; } + bool isDefaultMember() const { return defaultToken().isValid(); } + SourceLocation requiredToken() const { return m_requiredToken; } + bool isRequired() const { return requiredToken().isValid(); } + SourceLocation readonlyToken() const { return m_readonlyToken; } + bool isReadonly() const { return readonlyToken().isValid(); } + + SourceLocation propertyToken() const { return m_propertyToken; } + + template <bool InvalidIsLargest = true> + static bool compareLocationsByBegin(const SourceLocation *& lhs, const SourceLocation *& rhs) + { + if (lhs->isValid() && rhs->isValid()) + return lhs->begin() < rhs->begin(); + else if (lhs->isValid()) + return InvalidIsLargest; + else + return !InvalidIsLargest; + } + + void accept0(BaseVisitor *) override {} // intentionally do nothing + + SourceLocation firstSourceLocation() const override; + + SourceLocation lastSourceLocation() const override; + +private: + friend class QQmlJS::Parser; + SourceLocation m_defaultToken; + SourceLocation m_readonlyToken; + SourceLocation m_requiredToken; + SourceLocation m_propertyToken; +}; + class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember { public: @@ -3363,27 +3467,23 @@ public: UiPublicMember(UiQualifiedId *memberType, QStringView name) - : type(Property), memberType(memberType), name(name), statement(nullptr), binding(nullptr), isDefaultMember(false), isReadonlyMember(false), parameters(nullptr) + : type(Property), memberType(memberType), name(name), statement(nullptr), binding(nullptr), parameters(nullptr) { kind = K; } UiPublicMember(UiQualifiedId *memberType, QStringView name, Statement *statement) - : type(Property), memberType(memberType), name(name), statement(statement), binding(nullptr), isDefaultMember(false), isReadonlyMember(false), parameters(nullptr) + : type(Property), memberType(memberType), name(name), statement(statement), binding(nullptr), parameters(nullptr) { kind = K; } void accept0(BaseVisitor *visitor) override; SourceLocation firstSourceLocation() const override { - if (defaultToken.isValid()) - return defaultToken; - else if (readonlyToken.isValid()) - return readonlyToken; - else if (requiredToken.isValid()) - return requiredToken; - - return propertyToken; + if (hasAttributes) + return m_attributes->firstSourceLocation(); + else + return m_propertyToken; } SourceLocation lastSourceLocation() const override @@ -3396,27 +3496,61 @@ public: return semicolonToken; } + SourceLocation defaultToken() const + { + return hasAttributes ? m_attributes->defaultToken() : SourceLocation {}; + } + bool isDefaultMember() const { return defaultToken().isValid(); } + + SourceLocation requiredToken() const + { + return hasAttributes ? m_attributes->requiredToken() : SourceLocation {}; + } + bool isRequired() const { return requiredToken().isValid(); } + + SourceLocation readonlyToken() const + { + return hasAttributes ? m_attributes->readonlyToken() : SourceLocation {}; + } + bool isReadonly() const { return readonlyToken().isValid(); } + + void setAttributes(UiPropertyAttributes *attributes) + { + m_attributes = attributes; + hasAttributes = true; + } + + SourceLocation propertyToken() const + { + return hasAttributes ? m_attributes->propertyToken() : m_propertyToken; + } + + void setPropertyToken(SourceLocation token) + { + m_propertyToken = token; + hasAttributes = false; + } + // attributes - enum { Signal, Property } type; + enum : bool { Signal, Property } type; + bool hasAttributes = false; QStringView typeModifier; UiQualifiedId *memberType; QStringView name; Statement *statement; // initialized with a JS expression UiObjectMember *binding; // initialized with a QML object or array. - bool isDefaultMember; - bool isReadonlyMember; - bool isRequired = false; UiParameterList *parameters; // TODO: merge source locations - SourceLocation defaultToken; - SourceLocation readonlyToken; - SourceLocation propertyToken; - SourceLocation requiredToken; SourceLocation typeModifierToken; SourceLocation typeToken; SourceLocation identifierToken; SourceLocation colonToken; SourceLocation semicolonToken; +private: + union { + SourceLocation m_propertyToken = SourceLocation {}; + UiPropertyAttributes *m_attributes; + }; }; class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember @@ -3463,6 +3597,7 @@ public: QStringView name; UiObjectDefinition* component; SourceLocation componentToken; + SourceLocation identifierToken; }; class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember @@ -3574,7 +3709,7 @@ public: { kind = K; } SourceLocation firstSourceLocation() const override - { return qualifiedId->identifierToken; } + { Q_ASSERT(qualifiedId); return qualifiedId->identifierToken; } SourceLocation lastSourceLocation() const override { return rbracketToken; } @@ -3661,6 +3796,8 @@ public: // attributes SourceLocation enumToken; + SourceLocation identifierToken; + SourceLocation lbraceToken; SourceLocation rbraceToken; QStringView name; UiEnumMemberList *members; diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index c3a9b5aa83..c87f67c67e 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSAST_FWD_P_H #define QQMLJSAST_FWD_P_H -#include "qqmljsglobal_p.h" #include <private/qqmljssourcelocation_p.h> #include <QtCore/qglobal.h> @@ -159,12 +122,12 @@ class NestedExpression; class ClassExpression; class ClassDeclaration; class ClassElementList; -class TypeArgumentList; class Type; class TypeAnnotation; // ui elements class UiProgram; +class UiPragmaValueList; class UiPragma; class UiImport; class UiPublicMember; diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp index 7388eccebb..21a9e92e2c 100644 --- a/src/qml/parser/qqmljsastvisitor.cpp +++ b/src/qml/parser/qqmljsastvisitor.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmljsastvisitor_p.h" diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index 1e4c78c0d4..540d21d725 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSASTVISITOR_P_H #define QQMLJSASTVISITOR_P_H @@ -56,6 +20,129 @@ QT_BEGIN_NAMESPACE +// as done in https://en.wikipedia.org/wiki/X_Macro + +#define QQmlJSASTUiClassListToVisit \ + X(UiProgram) \ + X(UiHeaderItemList) \ + X(UiPragmaValueList) \ + X(UiPragma) \ + X(UiImport) \ + X(UiPublicMember) \ + X(UiSourceElement) \ + X(UiObjectDefinition) \ + X(UiObjectInitializer) \ + X(UiObjectBinding) \ + X(UiScriptBinding) \ + X(UiArrayBinding) \ + X(UiParameterList) \ + X(UiObjectMemberList) \ + X(UiArrayMemberList) \ + X(UiQualifiedId) \ + X(UiEnumDeclaration) \ + X(UiEnumMemberList) \ + X(UiVersionSpecifier) \ + X(UiInlineComponent) \ + X(UiAnnotation) \ + X(UiAnnotationList) \ + X(UiRequired) + +#define QQmlJSASTQQmlJSClassListToVisit \ + X(TypeExpression) \ + X(ThisExpression) \ + X(IdentifierExpression) \ + X(NullExpression) \ + X(TrueLiteral) \ + X(FalseLiteral) \ + X(SuperLiteral) \ + X(StringLiteral) \ + X(TemplateLiteral) \ + X(NumericLiteral) \ + X(RegExpLiteral) \ + X(ArrayPattern) \ + X(ObjectPattern) \ + X(PatternElementList) \ + X(PatternPropertyList) \ + X(PatternElement) \ + X(PatternProperty) \ + X(Elision) \ + X(NestedExpression) \ + X(IdentifierPropertyName) \ + X(StringLiteralPropertyName) \ + X(NumericLiteralPropertyName) \ + X(ComputedPropertyName) \ + X(ArrayMemberExpression) \ + X(FieldMemberExpression) \ + X(TaggedTemplate) \ + X(NewMemberExpression) \ + X(NewExpression) \ + X(CallExpression) \ + X(ArgumentList) \ + X(PostIncrementExpression) \ + X(PostDecrementExpression) \ + X(DeleteExpression) \ + X(VoidExpression) \ + X(TypeOfExpression) \ + X(PreIncrementExpression) \ + X(PreDecrementExpression) \ + X(UnaryPlusExpression) \ + X(UnaryMinusExpression) \ + X(TildeExpression) \ + X(NotExpression) \ + X(BinaryExpression) \ + X(ConditionalExpression) \ + X(Expression) \ + X(Block) \ + X(StatementList) \ + X(VariableStatement) \ + X(VariableDeclarationList) \ + X(EmptyStatement) \ + X(ExpressionStatement) \ + X(IfStatement) \ + X(DoWhileStatement) \ + X(WhileStatement) \ + X(ForStatement) \ + X(ForEachStatement) \ + X(ContinueStatement) \ + X(BreakStatement) \ + X(ReturnStatement) \ + X(YieldExpression) \ + X(WithStatement) \ + X(SwitchStatement) \ + X(CaseBlock) \ + X(CaseClauses) \ + X(CaseClause) \ + X(DefaultClause) \ + X(LabelledStatement) \ + X(ThrowStatement) \ + X(TryStatement) \ + X(Catch) \ + X(Finally) \ + X(FunctionDeclaration) \ + X(FunctionExpression) \ + X(FormalParameterList) \ + X(ClassExpression) \ + X(ClassDeclaration) \ + X(ClassElementList) \ + X(Program) \ + X(NameSpaceImport) \ + X(ImportSpecifier) \ + X(ImportsList) \ + X(NamedImports) \ + X(FromClause) \ + X(ImportClause) \ + X(ImportDeclaration) \ + X(ExportSpecifier) \ + X(ExportsList) \ + X(ExportClause) \ + X(ExportDeclaration) \ + X(ESModule) \ + X(DebuggerStatement) \ + X(Type) \ + X(TypeAnnotation) + +#define QQmlJSASTClassListToVisit QQmlJSASTUiClassListToVisit QQmlJSASTQQmlJSClassListToVisit + namespace QQmlJS { namespace AST { class QML_PARSER_EXPORT BaseVisitor @@ -93,332 +180,11 @@ public: virtual bool preVisit(Node *) = 0; virtual void postVisit(Node *) = 0; - // Ui - virtual bool visit(UiProgram *) = 0; - virtual bool visit(UiHeaderItemList *) = 0; - virtual bool visit(UiPragma *) = 0; - virtual bool visit(UiImport *) = 0; - virtual bool visit(UiPublicMember *) = 0; - virtual bool visit(UiSourceElement *) = 0; - virtual bool visit(UiObjectDefinition *) = 0; - virtual bool visit(UiObjectInitializer *) = 0; - virtual bool visit(UiObjectBinding *) = 0; - virtual bool visit(UiScriptBinding *) = 0; - virtual bool visit(UiArrayBinding *) = 0; - virtual bool visit(UiParameterList *) = 0; - virtual bool visit(UiObjectMemberList *) = 0; - virtual bool visit(UiArrayMemberList *) = 0; - virtual bool visit(UiQualifiedId *) = 0; - virtual bool visit(UiEnumDeclaration *) = 0; - virtual bool visit(UiEnumMemberList *) = 0; - virtual bool visit(UiVersionSpecifier *) = 0; - virtual bool visit(UiInlineComponent *) = 0; - virtual bool visit(UiAnnotation *) = 0; - virtual bool visit(UiAnnotationList *) = 0; - virtual bool visit(UiRequired *) = 0; - - virtual void endVisit(UiProgram *) = 0; - virtual void endVisit(UiImport *) = 0; - virtual void endVisit(UiHeaderItemList *) = 0; - virtual void endVisit(UiPragma *) = 0; - virtual void endVisit(UiPublicMember *) = 0; - virtual void endVisit(UiSourceElement *) = 0; - virtual void endVisit(UiObjectDefinition *) = 0; - virtual void endVisit(UiObjectInitializer *) = 0; - virtual void endVisit(UiObjectBinding *) = 0; - virtual void endVisit(UiScriptBinding *) = 0; - virtual void endVisit(UiArrayBinding *) = 0; - virtual void endVisit(UiParameterList *) = 0; - virtual void endVisit(UiObjectMemberList *) = 0; - virtual void endVisit(UiArrayMemberList *) = 0; - virtual void endVisit(UiQualifiedId *) = 0; - virtual void endVisit(UiEnumDeclaration *) = 0; - virtual void endVisit(UiEnumMemberList *) = 0; - virtual void endVisit(UiVersionSpecifier *) = 0; - virtual void endVisit(UiInlineComponent *) = 0; - virtual void endVisit(UiAnnotation *) = 0; - virtual void endVisit(UiAnnotationList *) = 0; - virtual void endVisit(UiRequired *) = 0; - - // QQmlJS - virtual bool visit(TypeExpression *) = 0; - virtual void endVisit(TypeExpression *) = 0; - - virtual bool visit(ThisExpression *) = 0; - virtual void endVisit(ThisExpression *) = 0; - - virtual bool visit(IdentifierExpression *) = 0; - virtual void endVisit(IdentifierExpression *) = 0; - - virtual bool visit(NullExpression *) = 0; - virtual void endVisit(NullExpression *) = 0; - - virtual bool visit(TrueLiteral *) = 0; - virtual void endVisit(TrueLiteral *) = 0; - - virtual bool visit(FalseLiteral *) = 0; - virtual void endVisit(FalseLiteral *) = 0; - - virtual bool visit(SuperLiteral *) = 0; - virtual void endVisit(SuperLiteral *) = 0; - - virtual bool visit(StringLiteral *) = 0; - virtual void endVisit(StringLiteral *) = 0; - - virtual bool visit(TemplateLiteral *) = 0; - virtual void endVisit(TemplateLiteral *) = 0; - - virtual bool visit(NumericLiteral *) = 0; - virtual void endVisit(NumericLiteral *) = 0; - - virtual bool visit(RegExpLiteral *) = 0; - virtual void endVisit(RegExpLiteral *) = 0; - - virtual bool visit(ArrayPattern *) = 0; - virtual void endVisit(ArrayPattern *) = 0; - - virtual bool visit(ObjectPattern *) = 0; - virtual void endVisit(ObjectPattern *) = 0; - - virtual bool visit(PatternElementList *) = 0; - virtual void endVisit(PatternElementList *) = 0; - - virtual bool visit(PatternPropertyList *) = 0; - virtual void endVisit(PatternPropertyList *) = 0; - - virtual bool visit(PatternElement *) = 0; - virtual void endVisit(PatternElement *) = 0; - - virtual bool visit(PatternProperty *) = 0; - virtual void endVisit(PatternProperty *) = 0; - - virtual bool visit(Elision *) = 0; - virtual void endVisit(Elision *) = 0; - - virtual bool visit(NestedExpression *) = 0; - virtual void endVisit(NestedExpression *) = 0; - - virtual bool visit(IdentifierPropertyName *) = 0; - virtual void endVisit(IdentifierPropertyName *) = 0; - - virtual bool visit(StringLiteralPropertyName *) = 0; - virtual void endVisit(StringLiteralPropertyName *) = 0; - - virtual bool visit(NumericLiteralPropertyName *) = 0; - virtual void endVisit(NumericLiteralPropertyName *) = 0; - - virtual bool visit(ComputedPropertyName *) = 0; - virtual void endVisit(ComputedPropertyName *) = 0; - - virtual bool visit(ArrayMemberExpression *) = 0; - virtual void endVisit(ArrayMemberExpression *) = 0; - - virtual bool visit(FieldMemberExpression *) = 0; - virtual void endVisit(FieldMemberExpression *) = 0; - - virtual bool visit(TaggedTemplate *) = 0; - virtual void endVisit(TaggedTemplate *) = 0; - - virtual bool visit(NewMemberExpression *) = 0; - virtual void endVisit(NewMemberExpression *) = 0; - - virtual bool visit(NewExpression *) = 0; - virtual void endVisit(NewExpression *) = 0; - - virtual bool visit(CallExpression *) = 0; - virtual void endVisit(CallExpression *) = 0; - - virtual bool visit(ArgumentList *) = 0; - virtual void endVisit(ArgumentList *) = 0; - - virtual bool visit(PostIncrementExpression *) = 0; - virtual void endVisit(PostIncrementExpression *) = 0; - - virtual bool visit(PostDecrementExpression *) = 0; - virtual void endVisit(PostDecrementExpression *) = 0; - - virtual bool visit(DeleteExpression *) = 0; - virtual void endVisit(DeleteExpression *) = 0; - - virtual bool visit(VoidExpression *) = 0; - virtual void endVisit(VoidExpression *) = 0; - - virtual bool visit(TypeOfExpression *) = 0; - virtual void endVisit(TypeOfExpression *) = 0; - - virtual bool visit(PreIncrementExpression *) = 0; - virtual void endVisit(PreIncrementExpression *) = 0; - - virtual bool visit(PreDecrementExpression *) = 0; - virtual void endVisit(PreDecrementExpression *) = 0; - - virtual bool visit(UnaryPlusExpression *) = 0; - virtual void endVisit(UnaryPlusExpression *) = 0; - - virtual bool visit(UnaryMinusExpression *) = 0; - virtual void endVisit(UnaryMinusExpression *) = 0; - - virtual bool visit(TildeExpression *) = 0; - virtual void endVisit(TildeExpression *) = 0; - - virtual bool visit(NotExpression *) = 0; - virtual void endVisit(NotExpression *) = 0; - - virtual bool visit(BinaryExpression *) = 0; - virtual void endVisit(BinaryExpression *) = 0; - - virtual bool visit(ConditionalExpression *) = 0; - virtual void endVisit(ConditionalExpression *) = 0; - - virtual bool visit(Expression *) = 0; - virtual void endVisit(Expression *) = 0; - - virtual bool visit(Block *) = 0; - virtual void endVisit(Block *) = 0; - - virtual bool visit(StatementList *) = 0; - virtual void endVisit(StatementList *) = 0; - - virtual bool visit(VariableStatement *) = 0; - virtual void endVisit(VariableStatement *) = 0; - - virtual bool visit(VariableDeclarationList *) = 0; - virtual void endVisit(VariableDeclarationList *) = 0; - - virtual bool visit(EmptyStatement *) = 0; - virtual void endVisit(EmptyStatement *) = 0; - - virtual bool visit(ExpressionStatement *) = 0; - virtual void endVisit(ExpressionStatement *) = 0; - - virtual bool visit(IfStatement *) = 0; - virtual void endVisit(IfStatement *) = 0; - - virtual bool visit(DoWhileStatement *) = 0; - virtual void endVisit(DoWhileStatement *) = 0; - - virtual bool visit(WhileStatement *) = 0; - virtual void endVisit(WhileStatement *) = 0; - - virtual bool visit(ForStatement *) = 0; - virtual void endVisit(ForStatement *) = 0; - - virtual bool visit(ForEachStatement *) = 0; - virtual void endVisit(ForEachStatement *) = 0; - - virtual bool visit(ContinueStatement *) = 0; - virtual void endVisit(ContinueStatement *) = 0; - - virtual bool visit(BreakStatement *) = 0; - virtual void endVisit(BreakStatement *) = 0; - - virtual bool visit(ReturnStatement *) = 0; - virtual void endVisit(ReturnStatement *) = 0; - - virtual bool visit(YieldExpression *) = 0; - virtual void endVisit(YieldExpression *) = 0; - - virtual bool visit(WithStatement *) = 0; - virtual void endVisit(WithStatement *) = 0; - - virtual bool visit(SwitchStatement *) = 0; - virtual void endVisit(SwitchStatement *) = 0; - - virtual bool visit(CaseBlock *) = 0; - virtual void endVisit(CaseBlock *) = 0; - - virtual bool visit(CaseClauses *) = 0; - virtual void endVisit(CaseClauses *) = 0; - - virtual bool visit(CaseClause *) = 0; - virtual void endVisit(CaseClause *) = 0; - - virtual bool visit(DefaultClause *) = 0; - virtual void endVisit(DefaultClause *) = 0; - - virtual bool visit(LabelledStatement *) = 0; - virtual void endVisit(LabelledStatement *) = 0; - - virtual bool visit(ThrowStatement *) = 0; - virtual void endVisit(ThrowStatement *) = 0; - - virtual bool visit(TryStatement *) = 0; - virtual void endVisit(TryStatement *) = 0; - - virtual bool visit(Catch *) = 0; - virtual void endVisit(Catch *) = 0; - - virtual bool visit(Finally *) = 0; - virtual void endVisit(Finally *) = 0; - - virtual bool visit(FunctionDeclaration *) = 0; - virtual void endVisit(FunctionDeclaration *) = 0; - - virtual bool visit(FunctionExpression *) = 0; - virtual void endVisit(FunctionExpression *) = 0; - - virtual bool visit(FormalParameterList *) = 0; - virtual void endVisit(FormalParameterList *) = 0; - - virtual bool visit(ClassExpression *) = 0; - virtual void endVisit(ClassExpression *) = 0; - - virtual bool visit(ClassDeclaration *) = 0; - virtual void endVisit(ClassDeclaration *) = 0; - - virtual bool visit(ClassElementList *) = 0; - virtual void endVisit(ClassElementList *) = 0; - - virtual bool visit(Program *) = 0; - virtual void endVisit(Program *) = 0; - - virtual bool visit(NameSpaceImport *) = 0; - virtual void endVisit(NameSpaceImport *) = 0; - - virtual bool visit(ImportSpecifier *) = 0; - virtual void endVisit(ImportSpecifier *) = 0; - - virtual bool visit(ImportsList *) = 0; - virtual void endVisit(ImportsList *) = 0; - - virtual bool visit(NamedImports *) = 0; - virtual void endVisit(NamedImports *) = 0; - - virtual bool visit(FromClause *) = 0; - virtual void endVisit(FromClause *) = 0; - - virtual bool visit(ImportClause *) = 0; - virtual void endVisit(ImportClause *) = 0; - - virtual bool visit(ImportDeclaration *) = 0; - virtual void endVisit(ImportDeclaration *) = 0; - - virtual bool visit(ExportSpecifier *) = 0; - virtual void endVisit(ExportSpecifier *) = 0; - - virtual bool visit(ExportsList *) = 0; - virtual void endVisit(ExportsList *) = 0; - - virtual bool visit(ExportClause *) = 0; - virtual void endVisit(ExportClause *) = 0; - - virtual bool visit(ExportDeclaration *) = 0; - virtual void endVisit(ExportDeclaration *) = 0; - - virtual bool visit(ESModule *) = 0; - virtual void endVisit(ESModule *) = 0; - - virtual bool visit(DebuggerStatement *) = 0; - virtual void endVisit(DebuggerStatement *) = 0; - - virtual bool visit(Type *) = 0; - virtual void endVisit(Type *) = 0; - - virtual bool visit(TypeArgumentList *) = 0; - virtual void endVisit(TypeArgumentList *) = 0; - - virtual bool visit(TypeAnnotation *) = 0; - virtual void endVisit(TypeAnnotation *) = 0; +#define X(name) \ + virtual bool visit(name *) = 0; \ + virtual void endVisit(name *) = 0; + QQmlJSASTClassListToVisit +#undef X virtual void throwRecursionDepthError() = 0; @@ -437,334 +203,37 @@ public: bool preVisit(Node *) override { return true; } void postVisit(Node *) override {} - // Ui - bool visit(UiProgram *) override { return true; } - bool visit(UiHeaderItemList *) override { return true; } - bool visit(UiPragma *) override { return true; } - bool visit(UiImport *) override { return true; } - bool visit(UiPublicMember *) override { return true; } - bool visit(UiSourceElement *) override { return true; } - bool visit(UiObjectDefinition *) override { return true; } - bool visit(UiObjectInitializer *) override { return true; } - bool visit(UiObjectBinding *) override { return true; } - bool visit(UiScriptBinding *) override { return true; } - bool visit(UiArrayBinding *) override { return true; } - bool visit(UiParameterList *) override { return true; } - bool visit(UiObjectMemberList *) override { return true; } - bool visit(UiArrayMemberList *) override { return true; } - bool visit(UiQualifiedId *) override { return true; } - bool visit(UiEnumDeclaration *) override { return true; } - bool visit(UiEnumMemberList *) override { return true; } - bool visit(UiVersionSpecifier *) override { return true; } - bool visit(UiInlineComponent *) override { return true; } - bool visit(UiAnnotation *) override { return true; } - bool visit(UiAnnotationList *) override { return true; } - bool visit(UiRequired *) override { return true; } - - void endVisit(UiProgram *) override {} - void endVisit(UiImport *) override {} - void endVisit(UiHeaderItemList *) override {} - void endVisit(UiPragma *) override {} - void endVisit(UiPublicMember *) override {} - void endVisit(UiSourceElement *) override {} - void endVisit(UiObjectDefinition *) override {} - void endVisit(UiObjectInitializer *) override {} - void endVisit(UiObjectBinding *) override {} - void endVisit(UiScriptBinding *) override {} - void endVisit(UiArrayBinding *) override {} - void endVisit(UiParameterList *) override {} - void endVisit(UiObjectMemberList *) override {} - void endVisit(UiArrayMemberList *) override {} - void endVisit(UiQualifiedId *) override {} - void endVisit(UiEnumDeclaration *) override {} - void endVisit(UiEnumMemberList *) override {} - void endVisit(UiVersionSpecifier *) override {} - void endVisit(UiInlineComponent *) override {} - void endVisit(UiAnnotation *) override {} - void endVisit(UiAnnotationList *) override {} - void endVisit(UiRequired *) override {} - - // QQmlJS - bool visit(TypeExpression *) override { return true; } - void endVisit(TypeExpression *) override {} - - bool visit(ThisExpression *) override { return true; } - void endVisit(ThisExpression *) override {} - - bool visit(IdentifierExpression *) override { return true; } - void endVisit(IdentifierExpression *) override {} - - bool visit(NullExpression *) override { return true; } - void endVisit(NullExpression *) override {} - - bool visit(TrueLiteral *) override { return true; } - void endVisit(TrueLiteral *) override {} - - bool visit(FalseLiteral *) override { return true; } - void endVisit(FalseLiteral *) override {} - - bool visit(SuperLiteral *) override { return true; } - void endVisit(SuperLiteral *) override {} - - bool visit(StringLiteral *) override { return true; } - void endVisit(StringLiteral *) override {} - - bool visit(TemplateLiteral *) override { return true; } - void endVisit(TemplateLiteral *) override {} - - bool visit(NumericLiteral *) override { return true; } - void endVisit(NumericLiteral *) override {} - - bool visit(RegExpLiteral *) override { return true; } - void endVisit(RegExpLiteral *) override {} - - bool visit(ArrayPattern *) override { return true; } - void endVisit(ArrayPattern *) override {} - - bool visit(ObjectPattern *) override { return true; } - void endVisit(ObjectPattern *) override {} - - bool visit(PatternElementList *) override { return true; } - void endVisit(PatternElementList *) override {} - - bool visit(PatternPropertyList *) override { return true; } - void endVisit(PatternPropertyList *) override {} - - bool visit(PatternElement *) override { return true; } - void endVisit(PatternElement *) override {} - - bool visit(PatternProperty *) override { return true; } - void endVisit(PatternProperty *) override {} - - bool visit(Elision *) override { return true; } - void endVisit(Elision *) override {} - - bool visit(NestedExpression *) override { return true; } - void endVisit(NestedExpression *) override {} - - bool visit(IdentifierPropertyName *) override { return true; } - void endVisit(IdentifierPropertyName *) override {} - - bool visit(StringLiteralPropertyName *) override { return true; } - void endVisit(StringLiteralPropertyName *) override {} - - bool visit(NumericLiteralPropertyName *) override { return true; } - void endVisit(NumericLiteralPropertyName *) override {} - - bool visit(ComputedPropertyName *) override { return true; } - void endVisit(ComputedPropertyName *) override {} - - bool visit(ArrayMemberExpression *) override { return true; } - void endVisit(ArrayMemberExpression *) override {} - - bool visit(FieldMemberExpression *) override { return true; } - void endVisit(FieldMemberExpression *) override {} - - bool visit(TaggedTemplate *) override { return true; } - void endVisit(TaggedTemplate *) override {} - - bool visit(NewMemberExpression *) override { return true; } - void endVisit(NewMemberExpression *) override {} - - bool visit(NewExpression *) override { return true; } - void endVisit(NewExpression *) override {} - - bool visit(CallExpression *) override { return true; } - void endVisit(CallExpression *) override {} - - bool visit(ArgumentList *) override { return true; } - void endVisit(ArgumentList *) override {} - - bool visit(PostIncrementExpression *) override { return true; } - void endVisit(PostIncrementExpression *) override {} - - bool visit(PostDecrementExpression *) override { return true; } - void endVisit(PostDecrementExpression *) override {} - - bool visit(DeleteExpression *) override { return true; } - void endVisit(DeleteExpression *) override {} - - bool visit(VoidExpression *) override { return true; } - void endVisit(VoidExpression *) override {} - - bool visit(TypeOfExpression *) override { return true; } - void endVisit(TypeOfExpression *) override {} - - bool visit(PreIncrementExpression *) override { return true; } - void endVisit(PreIncrementExpression *) override {} - - bool visit(PreDecrementExpression *) override { return true; } - void endVisit(PreDecrementExpression *) override {} - - bool visit(UnaryPlusExpression *) override { return true; } - void endVisit(UnaryPlusExpression *) override {} - - bool visit(UnaryMinusExpression *) override { return true; } - void endVisit(UnaryMinusExpression *) override {} - - bool visit(TildeExpression *) override { return true; } - void endVisit(TildeExpression *) override {} - - bool visit(NotExpression *) override { return true; } - void endVisit(NotExpression *) override {} - - bool visit(BinaryExpression *) override { return true; } - void endVisit(BinaryExpression *) override {} - - bool visit(ConditionalExpression *) override { return true; } - void endVisit(ConditionalExpression *) override {} - - bool visit(Expression *) override { return true; } - void endVisit(Expression *) override {} - - bool visit(Block *) override { return true; } - void endVisit(Block *) override {} - - bool visit(StatementList *) override { return true; } - void endVisit(StatementList *) override {} - - bool visit(VariableStatement *) override { return true; } - void endVisit(VariableStatement *) override {} - - bool visit(VariableDeclarationList *) override { return true; } - void endVisit(VariableDeclarationList *) override {} - - bool visit(EmptyStatement *) override { return true; } - void endVisit(EmptyStatement *) override {} - - bool visit(ExpressionStatement *) override { return true; } - void endVisit(ExpressionStatement *) override {} - - bool visit(IfStatement *) override { return true; } - void endVisit(IfStatement *) override {} - - bool visit(DoWhileStatement *) override { return true; } - void endVisit(DoWhileStatement *) override {} - - bool visit(WhileStatement *) override { return true; } - void endVisit(WhileStatement *) override {} - - bool visit(ForStatement *) override { return true; } - void endVisit(ForStatement *) override {} - - bool visit(ForEachStatement *) override { return true; } - void endVisit(ForEachStatement *) override {} - - bool visit(ContinueStatement *) override { return true; } - void endVisit(ContinueStatement *) override {} - - bool visit(BreakStatement *) override { return true; } - void endVisit(BreakStatement *) override {} - - bool visit(ReturnStatement *) override { return true; } - void endVisit(ReturnStatement *) override {} - - bool visit(YieldExpression *) override { return true; } - void endVisit(YieldExpression *) override {} - - bool visit(WithStatement *) override { return true; } - void endVisit(WithStatement *) override {} - - bool visit(SwitchStatement *) override { return true; } - void endVisit(SwitchStatement *) override {} - - bool visit(CaseBlock *) override { return true; } - void endVisit(CaseBlock *) override {} - - bool visit(CaseClauses *) override { return true; } - void endVisit(CaseClauses *) override {} - - bool visit(CaseClause *) override { return true; } - void endVisit(CaseClause *) override {} - - bool visit(DefaultClause *) override { return true; } - void endVisit(DefaultClause *) override {} - - bool visit(LabelledStatement *) override { return true; } - void endVisit(LabelledStatement *) override {} - - bool visit(ThrowStatement *) override { return true; } - void endVisit(ThrowStatement *) override {} - - bool visit(TryStatement *) override { return true; } - void endVisit(TryStatement *) override {} - - bool visit(Catch *) override { return true; } - void endVisit(Catch *) override {} - - bool visit(Finally *) override { return true; } - void endVisit(Finally *) override {} - - bool visit(FunctionDeclaration *) override { return true; } - void endVisit(FunctionDeclaration *) override {} - - bool visit(FunctionExpression *) override { return true; } - void endVisit(FunctionExpression *) override {} - - bool visit(FormalParameterList *) override { return true; } - void endVisit(FormalParameterList *) override {} - - bool visit(ClassExpression *) override { return true; } - void endVisit(ClassExpression *) override {} - - bool visit(ClassDeclaration *) override { return true; } - void endVisit(ClassDeclaration *) override {} - - bool visit(ClassElementList *) override { return true; } - void endVisit(ClassElementList *) override {} - - bool visit(Program *) override { return true; } - void endVisit(Program *) override {} - - bool visit(NameSpaceImport *) override { return true; } - void endVisit(NameSpaceImport *) override {} - - bool visit(ImportSpecifier *) override { return true; } - void endVisit(ImportSpecifier *) override {} - - bool visit(ImportsList *) override { return true; } - void endVisit(ImportsList *) override {} - - bool visit(NamedImports *) override { return true; } - void endVisit(NamedImports *) override {} - - bool visit(FromClause *) override { return true; } - void endVisit(FromClause *) override {} - - bool visit(ImportClause *) override { return true; } - void endVisit(ImportClause *) override {} - - bool visit(ImportDeclaration *) override { return true; } - void endVisit(ImportDeclaration *) override {} - - bool visit(ExportSpecifier *) override { return true; } - void endVisit(ExportSpecifier *) override {} - - bool visit(ExportsList *) override { return true; } - void endVisit(ExportsList *) override {} - - bool visit(ExportClause *) override { return true; } - void endVisit(ExportClause *) override {} - - bool visit(ExportDeclaration *) override { return true; } - void endVisit(ExportDeclaration *) override {} - - bool visit(ESModule *) override { return true; } - void endVisit(ESModule *) override {} - - bool visit(DebuggerStatement *) override { return true; } - void endVisit(DebuggerStatement *) override {} - - bool visit(Type *) override { return true; } - void endVisit(Type *) override {} - - bool visit(TypeArgumentList *) override { return true; } - void endVisit(TypeArgumentList *) override {} - - bool visit(TypeAnnotation *) override { return true; } - void endVisit(TypeAnnotation *) override {} +#define X(name) \ + bool visit(name *) override { return true; } \ + void endVisit(name *) override { } + QQmlJSASTClassListToVisit +#undef X }; +class QML_PARSER_EXPORT JSVisitor : public BaseVisitor +{ +public: + JSVisitor() = default; + + bool preVisit(Node *) override { return true; } + void postVisit(Node *) override { } + +#define X(name) \ + bool visit(name *) override { return true; } \ + void endVisit(name *) override { } + QQmlJSASTQQmlJSClassListToVisit +#undef X + +#define X(name) \ + bool visit(name *) override \ + { \ + Q_ASSERT(false); \ + return false; \ + } \ + void endVisit(name *) override { } + QQmlJSASTUiClassListToVisit +#undef X +}; // namespace AST } } // namespace AST QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp deleted file mode 100644 index 53ad8820dd..0000000000 --- a/src/qml/parser/qqmljsengine_p.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmljsengine_p.h" -#include "qqmljsglobal_p.h" - -#include <QtCore/private/qnumeric_p.h> -#include <QtCore/qhash.h> -#include <QtCore/qdebug.h> - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { - -static inline int toDigit(char c) -{ - if ((c >= '0') && (c <= '9')) - return c - '0'; - else if ((c >= 'a') && (c <= 'z')) - return 10 + c - 'a'; - else if ((c >= 'A') && (c <= 'Z')) - return 10 + c - 'A'; - return -1; -} - -double integerFromString(const char *buf, int size, int radix) -{ - if (size == 0) - return qt_qnan(); - - double sign = 1.0; - int i = 0; - if (buf[0] == '+') { - ++i; - } else if (buf[0] == '-') { - sign = -1.0; - ++i; - } - - if (((size-i) >= 2) && (buf[i] == '0')) { - if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) - && (radix < 34)) { - if ((radix != 0) && (radix != 16)) - return 0; - radix = 16; - i += 2; - } else { - if (radix == 0) { - radix = 8; - ++i; - } - } - } else if (radix == 0) { - radix = 10; - } - - int j = i; - for ( ; i < size; ++i) { - int d = toDigit(buf[i]); - if ((d == -1) || (d >= radix)) - break; - } - double result; - if (j == i) { - if (!qstrcmp(buf, "Infinity")) - result = qInf(); - else - result = qt_qnan(); - } else { - result = 0; - double multiplier = 1; - for (--i ; i >= j; --i, multiplier *= radix) - result += toDigit(buf[i]) * multiplier; - } - result *= sign; - return result; -} - -Engine::Engine() - : _lexer(nullptr), _directives(nullptr) -{ } - -Engine::~Engine() -{ } - -void Engine::setCode(const QString &code) -{ _code = code; } - -void Engine::addComment(int pos, int len, int line, int col) -{ if (len > 0) _comments.append(QQmlJS::SourceLocation(pos, len, line, col)); } - -QList<QQmlJS::SourceLocation> Engine::comments() const -{ return _comments; } - -Lexer *Engine::lexer() const -{ return _lexer; } - -void Engine::setLexer(Lexer *lexer) -{ _lexer = lexer; } - -Directives *Engine::directives() const -{ return _directives; } - -void Engine::setDirectives(Directives *directives) -{ _directives = directives; } - -MemoryPool *Engine::pool() -{ return &_pool; } - -QStringView Engine::newStringRef(const QString &text) -{ - _extraCode.append(text); - return QStringView{_extraCode.last()}; -} - -QStringView Engine::newStringRef(const QChar *chars, int size) -{ return newStringRef(QString(chars, size)); } - -} // end of namespace QQmlJS - -QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h index b57515982b..37f78a30e1 100644 --- a/src/qml/parser/qqmljsengine_p.h +++ b/src/qml/parser/qqmljsengine_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSENGINE_P_H #define QQMLJSENGINE_P_H @@ -92,40 +56,52 @@ public: } }; -class QML_PARSER_EXPORT Engine +class Engine { - Lexer *_lexer; - Directives *_directives; + Lexer *_lexer = nullptr; + Directives *_directives = nullptr; MemoryPool _pool; QList<SourceLocation> _comments; QStringList _extraCode; QString _code; public: - Engine(); - ~Engine(); - - void setCode(const QString &code); + void setCode(const QString &code) { _code = code; } const QString &code() const { return _code; } - void addComment(int pos, int len, int line, int col); - QList<SourceLocation> comments() const; + void addComment(int pos, int len, int line, int col) + { + Q_ASSERT(len >= 0); + _comments.append(QQmlJS::SourceLocation(pos, len, line, col)); + } - Lexer *lexer() const; - void setLexer(Lexer *lexer); + QList<SourceLocation> comments() const { return _comments; } - Directives *directives() const; - void setDirectives(Directives *directives); + Lexer *lexer() const { return _lexer; } + void setLexer(Lexer *lexer) { _lexer = lexer; } - MemoryPool *pool(); + Directives *directives() const { return _directives; } + void setDirectives(Directives *directives) { _directives = directives; } - inline QStringView midRef(int position, int size) { return QStringView{_code}.mid(position, size); } + MemoryPool *pool() { return &_pool; } + const MemoryPool *pool() const { return &_pool; } - QStringView newStringRef(const QString &s); - QStringView newStringRef(const QChar *chars, int size); -}; + QStringView midRef(int position, int size) + { + return QStringView{_code}.mid(position, size); + } -double integerFromString(const char *buf, int size, int radix); + QStringView newStringRef(const QString &text) + { + _extraCode.append(text); + return QStringView{_extraCode.last()}; + } + + QStringView newStringRef(const QChar *chars, int size) + { + return newStringRef(QString(chars, size)); + } +}; } // end of namespace QQmlJS diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h index e7937ebc3b..1ca98b2041 100644 --- a/src/qml/parser/qqmljsglobal_p.h +++ b/src/qml/parser/qqmljsglobal_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSGLOBAL_P_H #define QQMLJSGLOBAL_P_H @@ -50,13 +14,10 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <QtCore/private/qglobal_p.h> #ifndef QT_STATIC -# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) - // QmlDevTools is a static library -# define QML_PARSER_EXPORT -# elif defined(QT_BUILD_QML_LIB) +# if defined(QT_BUILD_QML_LIB) # define QML_PARSER_EXPORT Q_DECL_EXPORT # else # define QML_PARSER_EXPORT Q_DECL_IMPORT diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 5f08cc4353..72be5822a7 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSKEYWORDS_P_H #define QQMLJSKEYWORDS_P_H diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 5f0276da8f..704c7eb00d 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmljslexer_p.h" #include "qqmljsengine_p.h" @@ -51,6 +15,9 @@ #include <QtCore/qdebug.h> #include <QtCore/QScopedValueRollback> +#include <optional> + +QT_BEGIN_NAMESPACE using namespace QQmlJS; static inline int regExpFlagFromChar(const QChar &ch) @@ -80,31 +47,8 @@ static inline QChar convertHex(QChar c1, QChar c2) return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); } -Lexer::Lexer(Engine *engine) - : _engine(engine) - , _codePtr(nullptr) - , _endPtr(nullptr) - , _tokenStartPtr(nullptr) - , _char(u'\n') - , _errorCode(NoError) - , _currentLineNumber(0) - , _currentColumnNumber(0) - , _tokenValue(0) - , _parenthesesState(IgnoreParentheses) - , _parenthesesCount(0) - , _stackToken(-1) - , _patternFlags(0) - , _tokenKind(0) - , _tokenLength(0) - , _tokenLine(0) - , _tokenColumn(0) - , _validTokenText(false) - , _prohibitAutomaticSemicolon(false) - , _restrictedKeyword(false) - , _terminator(false) - , _followsClosingBrace(false) - , _delimited(true) - , _qmlMode(true) +Lexer::Lexer(Engine *engine, LexMode lexMode) + : _engine(engine), _lexMode(lexMode), _endPtr(nullptr), _qmlMode(true) { if (engine) engine->setLexer(this); @@ -120,13 +64,20 @@ QString Lexer::code() const return _code; } -void Lexer::setCode(const QString &code, int lineno, bool qmlMode) +void Lexer::setCode(const QString &code, int lineno, bool qmlMode, + Lexer::CodeContinuation codeContinuation) { + if (codeContinuation == Lexer::CodeContinuation::Continue) + _currentOffset += _code.size(); + else + _currentOffset = 0; if (_engine) _engine->setCode(code); _qmlMode = qmlMode; _code = code; + _skipLinefeed = false; + _tokenText.clear(); _tokenText.reserve(1024); _errorMessage.clear(); @@ -134,33 +85,18 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _rawString = QStringView(); _codePtr = code.unicode(); - _endPtr = _codePtr + code.length(); + _endPtr = _codePtr + code.size(); _tokenStartPtr = _codePtr; - _char = u'\n'; - _errorCode = NoError; - - _currentLineNumber = lineno; + if (lineno >= 0) + _currentLineNumber = lineno; _currentColumnNumber = 0; - _tokenValue = 0; - - // parentheses state - _parenthesesState = IgnoreParentheses; - _parenthesesCount = 0; - - _stackToken = -1; - - _patternFlags = 0; - _tokenLength = 0; - _tokenLine = lineno; + _tokenLine = _currentLineNumber; _tokenColumn = 0; + _tokenLength = 0; - _validTokenText = false; - _prohibitAutomaticSemicolon = false; - _restrictedKeyword = false; - _terminator = false; - _followsClosingBrace = false; - _delimited = true; + if (codeContinuation == Lexer::CodeContinuation::Reset) + _state = State {}; } void Lexer::scanChar() @@ -170,20 +106,28 @@ void Lexer::scanChar() ++_codePtr; _skipLinefeed = false; } - _char = *_codePtr++; + _state.currentChar = *_codePtr++; ++_currentColumnNumber; if (isLineTerminator()) { - if (_char == u'\r') { + if (_state.currentChar == u'\r') { if (_codePtr < _endPtr && *_codePtr == u'\n') _skipLinefeed = true; - _char = u'\n'; + _state.currentChar = u'\n'; } ++_currentLineNumber; _currentColumnNumber = 0; } } +QChar Lexer::peekChar() +{ + auto peekPtr = _codePtr; + if (peekPtr < _endPtr) + return *peekPtr; + return QChar(); +} + namespace { inline bool isBinop(int tok) { @@ -251,107 +195,184 @@ int octalDigit(QChar c) int Lexer::lex() { - const int previousTokenKind = _tokenKind; + const int previousTokenKind = _state.tokenKind; + int tokenKind; + bool firstPass = true; again: - _tokenSpell = QStringView(); - _rawString = QStringView(); - _tokenKind = scanToken(); - _tokenLength = _codePtr - _tokenStartPtr - 1; - - _delimited = false; - _restrictedKeyword = false; - _followsClosingBrace = (previousTokenKind == T_RBRACE); - - // update the flags - switch (_tokenKind) { - case T_LBRACE: - if (_bracesCount > 0) - ++_bracesCount; - Q_FALLTHROUGH(); - case T_SEMICOLON: - _importState = ImportState::NoQmlImport; - Q_FALLTHROUGH(); - case T_QUESTION: - case T_COLON: - case T_TILDE: - _delimited = true; - break; - case T_AUTOMATIC_SEMICOLON: - case T_AS: - _importState = ImportState::NoQmlImport; - Q_FALLTHROUGH(); - default: - if (isBinop(_tokenKind)) - _delimited = true; - break; - - case T_IMPORT: - if (qmlMode() || (_handlingDirectives && previousTokenKind == T_DOT)) - _importState = ImportState::SawImport; - if (isBinop(_tokenKind)) - _delimited = true; - break; - - case T_IF: - case T_FOR: - case T_WHILE: - case T_WITH: - _parenthesesState = CountParentheses; - _parenthesesCount = 0; - break; - - case T_ELSE: - case T_DO: - _parenthesesState = BalancedParentheses; - break; - - case T_CONTINUE: - case T_BREAK: - case T_RETURN: - case T_YIELD: - case T_THROW: - _restrictedKeyword = true; - break; - case T_RBRACE: - if (_bracesCount > 0) - --_bracesCount; - if (_bracesCount == 0) - goto again; - } // switch + tokenKind = T_ERROR; + _tokenSpell = QStringView(); + _rawString = QStringView(); + if (firstPass && _state.stackToken == -1) { + firstPass = false; + if (_codePtr > _endPtr && _lexMode == LexMode::LineByLine && !_code.isEmpty()) + return T_EOL; + + if (_state.comments == CommentState::InMultilineComment) { + scanChar(); + _tokenStartPtr = _codePtr - 1; + _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; + while (_codePtr <= _endPtr) { + if (_state.currentChar == u'*') { + scanChar(); + if (_state.currentChar == u'/') { + scanChar(); + if (_engine) { + _engine->addComment(tokenOffset() + 2, + _codePtr - _tokenStartPtr - 1 - 4, + tokenStartLine(), tokenStartColumn() + 2); + } + tokenKind = T_COMMENT; + break; + } + } else { + scanChar(); + } + } + if (tokenKind == T_ERROR) + tokenKind = T_PARTIAL_COMMENT; + } else { + // handle multiline continuation + std::optional<ScanStringMode> scanMode; + switch (previousTokenKind) { + case T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL: + scanMode = ScanStringMode::SingleQuote; + break; + case T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL: + scanMode = ScanStringMode::DoubleQuote; + break; + case T_PARTIAL_TEMPLATE_HEAD: + scanMode = ScanStringMode::TemplateHead; + break; + case T_PARTIAL_TEMPLATE_MIDDLE: + scanMode = ScanStringMode::TemplateContinuation; + break; + default: + break; + } + if (scanMode) { + scanChar(); + _tokenStartPtr = _codePtr - 1; + _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; + tokenKind = scanString(*scanMode); + } + } + } + if (tokenKind == T_ERROR) + tokenKind = scanToken(); + _tokenLength = _codePtr - _tokenStartPtr - 1; + switch (tokenKind) { + // end of line and comments should not "overwrite" the old token type... + case T_EOL: + return tokenKind; + case T_COMMENT: + _state.comments = CommentState::HadComment; + return tokenKind; + case T_PARTIAL_COMMENT: + _state.comments = CommentState::InMultilineComment; + return tokenKind; + default: + _state.comments = CommentState::NoComment; + break; + } + _state.tokenKind = tokenKind; + + _state.delimited = false; + _state.restrictedKeyword = false; + _state.followsClosingBrace = (previousTokenKind == T_RBRACE); + + // update the flags + switch (_state.tokenKind) { + case T_LBRACE: + if (_state.bracesCount > 0) + ++_state.bracesCount; + Q_FALLTHROUGH(); + case T_SEMICOLON: + _state.importState = ImportState::NoQmlImport; + Q_FALLTHROUGH(); + case T_QUESTION: + case T_COLON: + case T_TILDE: + _state.delimited = true; + break; + case T_AUTOMATIC_SEMICOLON: + case T_AS: + _state.importState = ImportState::NoQmlImport; + Q_FALLTHROUGH(); + default: + if (isBinop(_state.tokenKind)) + _state.delimited = true; + break; + + case T_IMPORT: + if (qmlMode() || (_state.handlingDirectives && previousTokenKind == T_DOT)) + _state.importState = ImportState::SawImport; + if (isBinop(_state.tokenKind)) + _state.delimited = true; + break; + + case T_IF: + case T_FOR: + case T_WHILE: + case T_WITH: + _state.parenthesesState = CountParentheses; + _state.parenthesesCount = 0; + break; + + case T_ELSE: + case T_DO: + _state.parenthesesState = BalancedParentheses; + break; + + case T_CONTINUE: + case T_BREAK: + case T_RETURN: + case T_YIELD: + case T_THROW: + _state.restrictedKeyword = true; + break; + case T_RBRACE: + if (_state.bracesCount > 0) + --_state.bracesCount; + if (_state.bracesCount == 0) + goto again; + } // switch // update the parentheses state - switch (_parenthesesState) { - case IgnoreParentheses: - break; - - case CountParentheses: - if (_tokenKind == T_RPAREN) { - --_parenthesesCount; - if (_parenthesesCount == 0) - _parenthesesState = BalancedParentheses; - } else if (_tokenKind == T_LPAREN) { - ++_parenthesesCount; - } - break; - - case BalancedParentheses: - if (_tokenKind != T_DO && _tokenKind != T_ELSE) - _parenthesesState = IgnoreParentheses; - break; - } // switch - - return _tokenKind; + switch (_state.parenthesesState) { + case IgnoreParentheses: + break; + + case CountParentheses: + if (_state.tokenKind == T_RPAREN) { + --_state.parenthesesCount; + if (_state.parenthesesCount == 0) + _state.parenthesesState = BalancedParentheses; + } else if (_state.tokenKind == T_LPAREN) { + ++_state.parenthesesCount; + } + break; + + case BalancedParentheses: + if (_state.tokenKind != T_DO && _state.tokenKind != T_ELSE) + _state.parenthesesState = IgnoreParentheses; + break; + } // switch + + return _state.tokenKind; } uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) { - Q_ASSERT(_char == u'u'); + Q_ASSERT(_state.currentChar == u'u'); scanChar(); // skip u - if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) { + constexpr int distanceFromFirstHexToLastHex = 3; + if (_codePtr + distanceFromFirstHexToLastHex <= _endPtr && isHexDigit(_state.currentChar)) { uint codePoint = 0; for (int i = 0; i < 4; ++i) { - int digit = hexDigit(_char); + int digit = hexDigit(_state.currentChar); if (digit < 0) goto error; codePoint *= 16; @@ -361,15 +382,15 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) *ok = true; return codePoint; - } else if (_codePtr < _endPtr && _char == u'{') { + } else if (_codePtr < _endPtr && _state.currentChar == u'{') { scanChar(); // skip '{' uint codePoint = 0; - if (!isHexDigit(_char)) + if (!isHexDigit(_state.currentChar)) // need at least one hex digit goto error; while (_codePtr <= _endPtr) { - int digit = hexDigit(_char); + int digit = hexDigit(_state.currentChar); if (digit < 0) break; codePoint *= 16; @@ -379,7 +400,7 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) scanChar(); } - if (_char != u'}') + if (_state.currentChar != u'}') goto error; scanChar(); // skip '}' @@ -389,8 +410,8 @@ uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) return codePoint; } - error: - _errorCode = IllegalUnicodeEscapeSequence; +error: + _state.errorCode = IllegalUnicodeEscapeSequence; _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); *ok = false; @@ -402,10 +423,10 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok) if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { scanChar(); - const QChar c1 = _char; + const QChar c1 = _state.currentChar; scanChar(); - const QChar c2 = _char; + const QChar c2 = _state.currentChar; scanChar(); if (ok) @@ -418,6 +439,40 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok) return QChar(); } +namespace QQmlJS { +QDebug operator<<(QDebug dbg, const Lexer &l) +{ + dbg << "{\n" + << " engine:" << qsizetype(l._engine) << ",\n" + << " lexMode:" << int(l._lexMode) << ",\n" + << " code.size:" << qsizetype(l._code.unicode()) << "+" << l._code.size() << ",\n" + << " endPtr: codePtr + " << (l._endPtr - l._codePtr) << ",\n" + << " qmlMode:" << l._qmlMode << ",\n" + << " staticIsKeyword:" << l._staticIsKeyword << ",\n" + << " currentLineNumber:" << l._currentLineNumber << ",\n" + << " currentColumnNumber:" << l._currentColumnNumber << ",\n" + << " currentOffset:" << l._currentOffset << ",\n" + << " tokenLength:" << l._tokenLength << ",\n" + << " tokenLine:" << l._tokenLine << ",\n" + << " tokenColumn:" << l._tokenColumn << ",\n" + << " tokenText:" << l._tokenText << ",\n" + << " skipLinefeed:" << l._skipLinefeed << ",\n" + << " errorMessage:" << l._errorMessage << ",\n" + << " tokenSpell:" << l._tokenSpell << ",\n" + << " rawString:" << l._rawString << ",\n"; + if (l._codePtr) + dbg << " codePtr: code.unicode()+" << (l._codePtr - l._code.unicode()) << ",\n"; + else + dbg << " codePtr: *null*,\n"; + if (l._tokenStartPtr) + dbg << " tokenStartPtr: codePtr " << (l._tokenStartPtr - l._codePtr) << ",\n"; + else + dbg << " tokenStartPtr: *null*,\n"; + dbg << " state:" << l._state << "\n}"; + return dbg; +} +} + static inline bool isIdentifierStart(uint ch) { // fast path for ascii @@ -473,71 +528,36 @@ static bool isIdentifierPart(uint ch) int Lexer::scanToken() { - if (_stackToken != -1) { - int tk = _stackToken; - _stackToken = -1; + if (_state.stackToken != -1) { + int tk = _state.stackToken; + _state.stackToken = -1; return tk; } - if (_bracesCount == 0) { + if (_state.bracesCount == 0) { // we're inside a Template string return scanString(TemplateContinuation); } - - _terminator = false; + if (_state.comments == CommentState::NoComment) + _state.terminator = false; again: - _validTokenText = false; - - // handle comment can be called after a '/' has been read - // and returns true if it actually encountered a comment - auto handleComment = [this](){ - if (_char == u'*') { - scanChar(); - while (_codePtr <= _endPtr) { - if (_char == u'*') { - scanChar(); - if (_char == u'/') { - scanChar(); - - if (_engine) { - _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4, - tokenStartLine(), tokenStartColumn() + 2); - } - - return true; - } - } else { - scanChar(); - } - } - } else if (_char == u'/') { - while (_codePtr <= _endPtr && !isLineTerminator()) { - scanChar(); - } - if (_engine) { - _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2, - tokenStartLine(), tokenStartColumn() + 2); - } - return true; - } - return false; - }; + _state.validTokenText = false; - - while (_char.isSpace()) { + while (_state.currentChar.isSpace()) { if (isLineTerminator()) { - if (_restrictedKeyword) { + bool isAtEnd = (_codePtr + (_skipLinefeed ? 1 : 0)) == _endPtr; + if (_state.restrictedKeyword) { // automatic semicolon insertion _tokenLine = _currentLineNumber; _tokenColumn = _currentColumnNumber; _tokenStartPtr = _codePtr - 1; return T_SEMICOLON; - } else { - _terminator = true; + } else if (_lexMode == LexMode::WholeCode || !isAtEnd) { + _state.terminator = true; syncProhibitAutomaticSemicolon(); - } + } // else we will do the previous things at the start of next line... } scanChar(); @@ -547,10 +567,20 @@ again: _tokenLine = _currentLineNumber; _tokenColumn = _currentColumnNumber; - if (_codePtr > _endPtr) - return EOF_SYMBOL; + if (_codePtr >= _endPtr) { + if (_lexMode == LexMode::LineByLine) { + if (!_code.isEmpty()) { + _state.currentChar = *(_codePtr - 2); + return T_EOL; + } else { + return EOF_SYMBOL; + } + } else if (_codePtr > _endPtr) { + return EOF_SYMBOL; + } + } - const QChar ch = _char; + const QChar ch = _state.currentChar; scanChar(); switch (ch.unicode()) { @@ -558,10 +588,10 @@ again: case u'}': return T_RBRACE; case u'|': - if (_char == u'|') { + if (_state.currentChar == u'|') { scanChar(); return T_OR_OR; - } else if (_char == u'=') { + } else if (_state.currentChar == u'=') { scanChar(); return T_OR_EQ; } @@ -570,7 +600,7 @@ again: case u'{': return T_LBRACE; case u'^': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_XOR_EQ; } @@ -579,56 +609,60 @@ again: case u']': return T_RBRACKET; case u'[': return T_LBRACKET; case u'?': { - if (_char == u'?') { + if (_state.currentChar == u'?') { scanChar(); return T_QUESTION_QUESTION; } + if (_state.currentChar == u'.' && !peekChar().isDigit()) { + scanChar(); + return T_QUESTION_DOT; + } return T_QUESTION; } case u'>': - if (_char == u'>') { + if (_state.currentChar == u'>') { scanChar(); - if (_char == u'>') { + if (_state.currentChar == u'>') { scanChar(); - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_GT_GT_GT_EQ; } return T_GT_GT_GT; - } else if (_char == u'=') { + } else if (_state.currentChar == u'=') { scanChar(); return T_GT_GT_EQ; } return T_GT_GT; - } else if (_char == u'=') { + } else if (_state.currentChar == u'=') { scanChar(); return T_GE; } return T_GT; case u'=': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_EQ_EQ_EQ; } return T_EQ_EQ; - } else if (_char == u'>') { + } else if (_state.currentChar == u'>') { scanChar(); return T_ARROW; } return T_EQ; case u'<': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_LE; - } else if (_char == u'<') { + } else if (_state.currentChar == u'<') { scanChar(); - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_LT_LT_EQ; } @@ -640,26 +674,62 @@ again: case u':': return T_COLON; case u'/': - if (handleComment()) - goto again; - else if (_char == u'=') { + switch (_state.currentChar.unicode()) { + case u'*': + scanChar(); + while (_codePtr <= _endPtr) { + if (_state.currentChar == u'*') { + scanChar(); + if (_state.currentChar == u'/') { + scanChar(); + if (_engine) { + _engine->addComment(tokenOffset() + 2, + _codePtr - _tokenStartPtr - 1 - 4, tokenStartLine(), + tokenStartColumn() + 2); + } + if (_lexMode == LexMode::LineByLine) + return T_COMMENT; + else + goto again; + } + } else { + scanChar(); + } + } + if (_lexMode == LexMode::LineByLine) + return T_PARTIAL_COMMENT; + else + goto again; + case u'/': + while (_codePtr <= _endPtr && !isLineTerminator()) { + scanChar(); + } + if (_engine) { + _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2, + tokenStartLine(), tokenStartColumn() + 2); + } + if (_lexMode == LexMode::LineByLine) + return T_COMMENT; + else + goto again; + case u'=': scanChar(); return T_DIVIDE_EQ; + default: + return T_DIVIDE_; } - return T_DIVIDE_; - case u'.': - if (_importState == ImportState::SawImport) + if (_state.importState == ImportState::SawImport) return T_DOT; - if (isDecimalDigit(_char.unicode())) + if (isDecimalDigit(_state.currentChar.unicode())) return scanNumber(ch); - if (_char == u'.') { + if (_state.currentChar == u'.') { scanChar(); - if (_char == u'.') { + if (_state.currentChar == u'.') { scanChar(); return T_ELLIPSIS; } else { - _errorCode = IllegalCharacter; + _state.errorCode = IllegalCharacter; _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'"); return T_ERROR; } @@ -667,14 +737,15 @@ again: return T_DOT; case u'-': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_MINUS_EQ; - } else if (_char == u'-') { + } else if (_state.currentChar == u'-') { scanChar(); - if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) { - _stackToken = T_MINUS_MINUS; + if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon + && _state.tokenKind != T_LPAREN) { + _state.stackToken = T_MINUS_MINUS; return T_SEMICOLON; } @@ -685,14 +756,15 @@ again: case u',': return T_COMMA; case u'+': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_PLUS_EQ; - } else if (_char == u'+') { + } else if (_state.currentChar == u'+') { scanChar(); - if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) { - _stackToken = T_PLUS_PLUS; + if (_state.terminator && !_state.delimited && !_state.prohibitAutomaticSemicolon + && _state.tokenKind != T_LPAREN) { + _state.stackToken = T_PLUS_PLUS; return T_SEMICOLON; } @@ -701,12 +773,12 @@ again: return T_PLUS; case u'*': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_STAR_EQ; - } else if (_char == u'*') { + } else if (_state.currentChar == u'*') { scanChar(); - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_STAR_STAR_EQ; } @@ -720,26 +792,26 @@ again: case u'@': return T_AT; case u'&': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_AND_EQ; - } else if (_char == u'&') { + } else if (_state.currentChar == u'&') { scanChar(); return T_AND_AND; } return T_AND; case u'%': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_REMAINDER_EQ; } return T_REMAINDER; case u'!': - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); - if (_char == u'=') { + if (_state.currentChar == u'=') { scanChar(); return T_NOT_EQ_EQ; } @@ -748,7 +820,7 @@ again: return T_NOT; case u'`': - _outerTemplateBraceCount.push(_bracesCount); + _state.outerTemplateBraceCount.push(_state.bracesCount); Q_FALLTHROUGH(); case u'\'': case u'"': @@ -763,7 +835,7 @@ again: case u'7': case u'8': case u'9': - if (_importState == ImportState::SawImport) + if (_state.importState == ImportState::SawImport) return scanVersionNumber(ch); else return scanNumber(ch); @@ -775,20 +847,23 @@ again: scanChar(); } if (_engine) { - _engine->addComment(tokenOffset(), _codePtr - _tokenStartPtr - 1, - tokenStartLine(), tokenStartColumn()); + _engine->addComment(tokenOffset(), _codePtr - _tokenStartPtr - 1, tokenStartLine(), + tokenStartColumn()); } - goto again; + if (_lexMode == LexMode::LineByLine) + return T_COMMENT; + else + goto again; } Q_FALLTHROUGH(); default: { uint c = ch.unicode(); bool identifierWithEscapeChars = false; - if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) { - c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); + if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_state.currentChar.unicode())) { + c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode()); scanChar(); - } else if (c == '\\' && _char == u'u') { + } else if (c == '\\' && _state.currentChar == u'u') { identifierWithEscapeChars = true; bool ok = false; c = decodeUnicodeEscapeCharacter(&ok); @@ -804,19 +879,19 @@ again: } else { _tokenText += QChar(c); } - _validTokenText = true; + _state.validTokenText = true; } while (_codePtr <= _endPtr) { - c = _char.unicode(); + c = _state.currentChar.unicode(); if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) { scanChar(); - c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); - } else if (_char == u'\\' && _codePtr[0] == u'u') { + c = QChar::surrogateToUcs4(ushort(c), _state.currentChar.unicode()); + } else if (_state.currentChar == u'\\' && _codePtr[0] == u'u') { if (!identifierWithEscapeChars) { identifierWithEscapeChars = true; _tokenText.resize(0); _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1); - _validTokenText = true; + _state.validTokenText = true; } scanChar(); // skip '\\' @@ -860,17 +935,57 @@ again: if (kind == T_FUNCTION) { continue_skipping: - while (_codePtr < _endPtr && _char.isSpace()) - scanChar(); - if (_char == u'*') { - _tokenLength = _codePtr - _tokenStartPtr - 1; - kind = T_FUNCTION_STAR; - scanChar(); - } else if (_char == u'/') { - scanChar(); - if (handleComment()) - goto continue_skipping; - } + while (_codePtr < _endPtr && _state.currentChar.isSpace()) + scanChar(); + if (_state.currentChar == u'*') { + _tokenLength = _codePtr - _tokenStartPtr - 1; + kind = T_FUNCTION_STAR; + scanChar(); + } else if (_state.currentChar == u'/') { + scanChar(); + switch (_state.currentChar.unicode()) { + case u'*': + scanChar(); + while (_codePtr <= _endPtr) { + if (_state.currentChar == u'*') { + scanChar(); + if (_state.currentChar == u'/') { + scanChar(); + if (_engine) { + _engine->addComment(tokenOffset() + 2, + _codePtr - _tokenStartPtr - 1 - 4, + tokenStartLine(), + tokenStartColumn() + 2); + } + if (_lexMode == LexMode::LineByLine) + return T_COMMENT; + goto continue_skipping; + } + } else { + scanChar(); + } + } + if (_lexMode == LexMode::LineByLine) + return T_PARTIAL_COMMENT; + else + goto continue_skipping; + case u'/': + while (_codePtr <= _endPtr && !isLineTerminator()) { + scanChar(); + } + if (_engine) { + _engine->addComment(tokenOffset() + 2, + _codePtr - _tokenStartPtr - 1 - 2, + tokenStartLine(), tokenStartColumn() + 2); + } + if (_lexMode == LexMode::LineByLine) + return T_COMMENT; + else + goto continue_skipping; + default: + break; + } + } } if (_engine) { @@ -893,6 +1008,11 @@ again: int Lexer::scanString(ScanStringMode mode) { QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode); + // we actually use T_STRING_LITERAL also for multiline strings, should we want to + // change that we should set it to: + // _state.tokenKind == T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL || + // _state.tokenKind == T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL + // here and uncomment the multilineStringLiteral = true below. bool multilineStringLiteral = false; const QChar *startCode = _codePtr - 1; @@ -909,30 +1029,33 @@ int Lexer::scanString(ScanStringMode mode) --_currentLineNumber; // will be read again in scanChar() break; } - _errorCode = IllegalCharacter; - _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal"); + _state.errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", + "Stray newline in string literal"); return T_ERROR; - } else if (_char == u'\\') { + } else if (_state.currentChar == u'\\') { break; - } else if (_char == u'$' && quote == u'`') { + } else if (_state.currentChar == u'$' && quote == u'`') { break; - } else if (_char == quote) { - _tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1); + } else if (_state.currentChar == quote) { + _tokenSpell = + _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1); _rawString = _tokenSpell; scanChar(); if (quote == u'`') - _bracesCount = _outerTemplateBraceCount.pop(); - + _state.bracesCount = _state.outerTemplateBraceCount.pop(); if (mode == TemplateHead) return T_NO_SUBSTITUTION_TEMPLATE; else if (mode == TemplateContinuation) return T_TEMPLATE_TAIL; + else if (multilineStringLiteral) + return T_MULTILINE_STRING_LITERAL; else return T_STRING_LITERAL; } // don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result - _char = *_codePtr++; + _state.currentChar = *_codePtr++; ++_currentColumnNumber; first = false; } @@ -942,7 +1065,7 @@ int Lexer::scanString(ScanStringMode mode) --_codePtr; --_currentColumnNumber; - _validTokenText = true; + _state.validTokenText = true; _tokenText = QString(startCode, _codePtr - startCode); auto setRawString = [&](const QChar *end) { @@ -955,7 +1078,7 @@ int Lexer::scanString(ScanStringMode mode) scanChar(); while (_codePtr <= _endPtr) { - if (_char == quote) { + if (_state.currentChar == quote) { scanChar(); if (_engine) { @@ -965,7 +1088,7 @@ int Lexer::scanString(ScanStringMode mode) } if (quote == u'`') - _bracesCount = _outerTemplateBraceCount.pop(); + _state.bracesCount = _state.outerTemplateBraceCount.pop(); if (mode == TemplateContinuation) return T_TEMPLATE_TAIL; @@ -973,27 +1096,28 @@ int Lexer::scanString(ScanStringMode mode) return T_NO_SUBSTITUTION_TEMPLATE; return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; - } else if (quote == u'`' && _char == u'$' && *_codePtr == u'{') { + } else if (quote == u'`' && _state.currentChar == u'$' && *_codePtr == u'{') { scanChar(); scanChar(); - _bracesCount = 1; + _state.bracesCount = 1; if (_engine) { _tokenSpell = _engine->newStringRef(_tokenText); setRawString(_codePtr - 2); } return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE); - } else if (_char == u'\\') { + } else if (_state.currentChar == u'\\') { scanChar(); if (_codePtr > _endPtr) { - _errorCode = IllegalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence"); + _state.errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate( + "QQmlParser", "End of file reached at escape sequence"); return T_ERROR; } QChar u; - switch (_char.unicode()) { + switch (_state.currentChar.unicode()) { // unicode escape sequence case u'u': { bool ok = false; @@ -1014,8 +1138,9 @@ int Lexer::scanString(ScanStringMode mode) bool ok = false; u = decodeHexEscapeCharacter(&ok); if (!ok) { - _errorCode = IllegalHexadecimalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence"); + _state.errorCode = IllegalHexadecimalEscapeSequence; + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Illegal hexadecimal escape sequence"); return T_ERROR; } } break; @@ -1032,7 +1157,7 @@ int Lexer::scanString(ScanStringMode mode) case u'v': u = u'\v'; scanChar(); break; case u'0': - if (! _codePtr->isDigit()) { + if (!_codePtr->isDigit()) { scanChar(); u = u'\0'; break; @@ -1047,31 +1172,42 @@ int Lexer::scanString(ScanStringMode mode) case u'7': case u'8': case u'9': - _errorCode = IllegalEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed"); + _state.errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Octal escape sequences are not allowed"); return T_ERROR; case u'\r': case u'\n': case 0x2028u: case 0x2029u: + // uncomment the following to use T_MULTILINE_STRING_LITERAL + // multilineStringLiteral = true; scanChar(); continue; default: // non escape character - u = _char; + u = _state.currentChar; scanChar(); } _tokenText += u; } else { - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); } } - - _errorCode = UnclosedStringLiteral; + if (_lexMode == LexMode::LineByLine && !_code.isEmpty()) { + if (mode == TemplateContinuation) + return T_PARTIAL_TEMPLATE_MIDDLE; + else if (mode == TemplateHead) + return T_PARTIAL_TEMPLATE_HEAD; + else if (mode == SingleQuote) + return T_PARTIAL_SINGLE_QUOTE_STRING_LITERAL; + return T_PARTIAL_DOUBLE_QUOTE_STRING_LITERAL; + } + _state.errorCode = UnclosedStringLiteral; _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line"); return T_ERROR; } @@ -1079,21 +1215,24 @@ int Lexer::scanString(ScanStringMode mode) int Lexer::scanNumber(QChar ch) { if (ch == u'0') { - if (_char == u'x' || _char == u'X') { - ch = _char; // remember the x or X to use it in the error message below. + if (_state.currentChar == u'x' || _state.currentChar == u'X') { + ch = _state.currentChar; // remember the x or X to use it in the error message below. // parse hex integer literal scanChar(); // consume 'x' - if (!isHexDigit(_char)) { - _errorCode = IllegalNumber; - _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + if (!isHexDigit(_state.currentChar)) { + _state.errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate( + "QQmlParser", + "At least one hexadecimal digit is required after '0%1'") + .arg(ch); return T_ERROR; } double d = 0.; while (1) { - int digit = ::hexDigit(_char); + int digit = ::hexDigit(_state.currentChar); if (digit < 0) break; d *= 16; @@ -1101,23 +1240,26 @@ int Lexer::scanNumber(QChar ch) scanChar(); } - _tokenValue = d; + _state.tokenValue = d; return T_NUMERIC_LITERAL; - } else if (_char == u'o' || _char == u'O') { - ch = _char; // remember the o or O to use it in the error message below. + } else if (_state.currentChar == u'o' || _state.currentChar == u'O') { + ch = _state.currentChar; // remember the o or O to use it in the error message below. // parse octal integer literal scanChar(); // consume 'o' - if (!isOctalDigit(_char.unicode())) { - _errorCode = IllegalNumber; - _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch); + if (!isOctalDigit(_state.currentChar.unicode())) { + _state.errorCode = IllegalNumber; + _errorMessage = + QCoreApplication::translate( + "QQmlParser", "At least one octal digit is required after '0%1'") + .arg(ch); return T_ERROR; } double d = 0.; while (1) { - int digit = ::octalDigit(_char); + int digit = ::octalDigit(_state.currentChar); if (digit < 0) break; d *= 8; @@ -1125,37 +1267,41 @@ int Lexer::scanNumber(QChar ch) scanChar(); } - _tokenValue = d; + _state.tokenValue = d; return T_NUMERIC_LITERAL; - } else if (_char == u'b' || _char == u'B') { - ch = _char; // remember the b or B to use it in the error message below. + } else if (_state.currentChar == u'b' || _state.currentChar == u'B') { + ch = _state.currentChar; // remember the b or B to use it in the error message below. // parse binary integer literal scanChar(); // consume 'b' - if (_char.unicode() != u'0' && _char.unicode() != u'1') { - _errorCode = IllegalNumber; - _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch); + if (_state.currentChar.unicode() != u'0' && _state.currentChar.unicode() != u'1') { + _state.errorCode = IllegalNumber; + _errorMessage = + QCoreApplication::translate( + "QQmlParser", "At least one binary digit is required after '0%1'") + .arg(ch); return T_ERROR; } double d = 0.; while (1) { int digit = 0; - if (_char.unicode() == u'1') + if (_state.currentChar.unicode() == u'1') digit = 1; - else if (_char.unicode() != u'0') + else if (_state.currentChar.unicode() != u'0') break; d *= 2; d += digit; scanChar(); } - _tokenValue = d; + _state.tokenValue = d; return T_NUMERIC_LITERAL; - } else if (_char.isDigit() && !qmlMode()) { - _errorCode = IllegalCharacter; - _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); + } else if (_state.currentChar.isDigit() && !qmlMode()) { + _state.errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", + "Decimal numbers can't start with '0'"); return T_ERROR; } } @@ -1165,36 +1311,36 @@ int Lexer::scanNumber(QChar ch) chars.append(ch.unicode()); if (ch != u'.') { - while (_char.isDigit()) { - chars.append(_char.unicode()); + while (_state.currentChar.isDigit()) { + chars.append(_state.currentChar.unicode()); scanChar(); // consume the digit } - if (_char == u'.') { - chars.append(_char.unicode()); + if (_state.currentChar == u'.') { + chars.append(_state.currentChar.unicode()); scanChar(); // consume `.' } } - while (_char.isDigit()) { - chars.append(_char.unicode()); + while (_state.currentChar.isDigit()) { + chars.append(_state.currentChar.unicode()); scanChar(); } - if (_char == u'e' || _char == u'E') { - if (_codePtr[0].isDigit() || ((_codePtr[0] == u'+' || _codePtr[0] == u'-') && - _codePtr[1].isDigit())) { + if (_state.currentChar == u'e' || _state.currentChar == u'E') { + if (_codePtr[0].isDigit() + || ((_codePtr[0] == u'+' || _codePtr[0] == u'-') && _codePtr[1].isDigit())) { - chars.append(_char.unicode()); + chars.append(_state.currentChar.unicode()); scanChar(); // consume `e' - if (_char == u'+' || _char == u'-') { - chars.append(_char.unicode()); + if (_state.currentChar == u'+' || _state.currentChar == u'-') { + chars.append(_state.currentChar.unicode()); scanChar(); // consume the sign } - while (_char.isDigit()) { - chars.append(_char.unicode()); + while (_state.currentChar.isDigit()) { + chars.append(_state.currentChar.unicode()); scanChar(); } } @@ -1204,11 +1350,12 @@ int Lexer::scanNumber(QChar ch) const char *end = nullptr; bool ok = false; - _tokenValue = qstrntod(begin, chars.size(), &end, &ok); + _state.tokenValue = qstrntod(begin, chars.size(), &end, &ok); if (end - begin != chars.size()) { - _errorCode = IllegalExponentIndicator; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); + _state.errorCode = IllegalExponentIndicator; + _errorMessage = + QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); return T_ERROR; } @@ -1218,47 +1365,48 @@ int Lexer::scanNumber(QChar ch) int Lexer::scanVersionNumber(QChar ch) { if (ch == u'0') { - _tokenValue = 0; + _state.tokenValue = 0; return T_VERSION_NUMBER; } int acc = 0; acc += ch.digitValue(); - while (_char.isDigit()) { + while (_state.currentChar.isDigit()) { acc *= 10; - acc += _char.digitValue(); + acc += _state.currentChar.digitValue(); scanChar(); // consume the digit } - _tokenValue = acc; + _state.tokenValue = acc; return T_VERSION_NUMBER; } bool Lexer::scanRegExp(RegExpBodyPrefix prefix) { _tokenText.resize(0); - _validTokenText = true; - _patternFlags = 0; + _state.validTokenText = true; + _state.patternFlags = 0; if (prefix == EqualPrefix) _tokenText += u'='; while (true) { - switch (_char.unicode()) { + switch (_state.currentChar.unicode()) { case u'/': scanChar(); // scan the flags - _patternFlags = 0; - while (isIdentLetter(_char)) { - int flag = regExpFlagFromChar(_char); - if (flag == 0 || _patternFlags & flag) { - _errorMessage = QCoreApplication::translate("QQmlParser", "Invalid regular expression flag '%0'") - .arg(QChar(_char)); + _state.patternFlags = 0; + while (isIdentLetter(_state.currentChar)) { + int flag = regExpFlagFromChar(_state.currentChar); + if (flag == 0 || _state.patternFlags & flag) { + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Invalid regular expression flag '%0'") + .arg(QChar(_state.currentChar)); return false; } - _patternFlags |= flag; + _state.patternFlags |= flag; scanChar(); } @@ -1267,59 +1415,63 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) case u'\\': // regular expression backslash sequence - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); if (_codePtr > _endPtr || isLineTerminator()) { - _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Unterminated regular expression backslash sequence"); return false; } - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); break; case u'[': // regular expression class - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); - while (_codePtr <= _endPtr && ! isLineTerminator()) { - if (_char == u']') + while (_codePtr <= _endPtr && !isLineTerminator()) { + if (_state.currentChar == u']') break; - else if (_char == u'\\') { + else if (_state.currentChar == u'\\') { // regular expression backslash sequence - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); if (_codePtr > _endPtr || isLineTerminator()) { - _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Unterminated regular expression backslash sequence"); return false; } - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); } else { - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); } } - if (_char != u']') { - _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression class"); + if (_state.currentChar != u']') { + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Unterminated regular expression class"); return false; } - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); // skip ] break; default: if (_codePtr > _endPtr || isLineTerminator()) { - _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression literal"); + _errorMessage = QCoreApplication::translate( + "QQmlParser", "Unterminated regular expression literal"); return false; } else { - _tokenText += _char; + _tokenText += _state.currentChar; scanChar(); } } // switch @@ -1330,7 +1482,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) bool Lexer::isLineTerminator() const { - const ushort unicode = _char.unicode(); + const ushort unicode = _state.currentChar.unicode(); return unicode == 0x000Au || unicode == 0x000Du || unicode == 0x2028u @@ -1339,7 +1491,7 @@ bool Lexer::isLineTerminator() const unsigned Lexer::isLineTerminatorSequence() const { - switch (_char.unicode()) { + switch (_state.currentChar.unicode()) { case 0x000Au: case 0x2028u: case 0x2029u: @@ -1386,10 +1538,10 @@ bool Lexer::isOctalDigit(ushort c) QString Lexer::tokenText() const { - if (_validTokenText) + if (_state.validTokenText) return _tokenText; - if (_tokenKind == T_STRING_LITERAL) + if (_state.tokenKind == T_STRING_LITERAL) return QString(_tokenStartPtr + 1, _tokenLength - 2); return QString(_tokenStartPtr, _tokenLength); @@ -1397,7 +1549,7 @@ QString Lexer::tokenText() const Lexer::Error Lexer::errorCode() const { - return _errorCode; + return _state.errorCode; } QString Lexer::errorMessage() const @@ -1407,33 +1559,31 @@ QString Lexer::errorMessage() const void Lexer::syncProhibitAutomaticSemicolon() { - if (_parenthesesState == BalancedParentheses) { + if (_state.parenthesesState == BalancedParentheses) { // we have seen something like "if (foo)", which means we should // never insert an automatic semicolon at this point, since it would // then be expanded into an empty statement (ECMA-262 7.9.1) - _prohibitAutomaticSemicolon = true; - _parenthesesState = IgnoreParentheses; + _state.prohibitAutomaticSemicolon = true; + _state.parenthesesState = IgnoreParentheses; } else { - _prohibitAutomaticSemicolon = false; + _state.prohibitAutomaticSemicolon = false; } } bool Lexer::prevTerminator() const { - return _terminator; + return _state.terminator; } bool Lexer::followsClosingBrace() const { - return _followsClosingBrace; + return _state.followsClosingBrace; } bool Lexer::canInsertAutomaticSemicolon(int token) const { - return token == T_RBRACE - || token == EOF_SYMBOL - || _terminator - || _followsClosingBrace; + return token == T_RBRACE || token == EOF_SYMBOL || _state.terminator + || _state.followsClosingBrace; } static const int uriTokens[] = { @@ -1497,12 +1647,12 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) error->loc.startColumn = tokenStartColumn(); }; - QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true); + QScopedValueRollback<bool> directivesGuard(_state.handlingDirectives, true); Q_ASSERT(!_qmlMode); lex(); // fetch the first token - if (_tokenKind != T_DOT) + if (_state.tokenKind != T_DOT) return true; do { @@ -1511,7 +1661,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) lex(); // skip T_DOT - if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_IMPORT)) + if (!(_state.tokenKind == T_IDENTIFIER || _state.tokenKind == T_IMPORT)) return true; // expected a valid QML/JS directive const QString directiveName = tokenText(); @@ -1541,7 +1691,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) QString version; bool fileImport = false; // file or uri import - if (_tokenKind == T_STRING_LITERAL) { + if (_state.tokenKind == T_STRING_LITERAL) { // .import T_STRING_LITERAL as T_IDENTIFIER fileImport = true; @@ -1553,10 +1703,10 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) } lex(); - } else if (_tokenKind == T_IDENTIFIER) { + } else if (_state.tokenKind == T_IDENTIFIER) { // .import T_IDENTIFIER (. T_IDENTIFIER)* (T_VERSION_NUMBER (. T_VERSION_NUMBER)?)? as T_IDENTIFIER while (true) { - if (!isUriToken(_tokenKind)) { + if (!isUriToken(_state.tokenKind)) { setError(QCoreApplication::translate("QQmlParser","Invalid module URI")); return false; } @@ -1568,7 +1718,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) setError(QCoreApplication::translate("QQmlParser","Invalid module URI")); return false; } - if (_tokenKind != QQmlJSGrammar::T_DOT) + if (_state.tokenKind != QQmlJSGrammar::T_DOT) break; pathOrUri.append(u'.'); @@ -1580,13 +1730,13 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) } } - if (_tokenKind == T_VERSION_NUMBER) { + if (_state.tokenKind == T_VERSION_NUMBER) { version = tokenText(); lex(); - if (_tokenKind == T_DOT) { + if (_state.tokenKind == T_DOT) { version += u'.'; lex(); - if (_tokenKind != T_VERSION_NUMBER) { + if (_state.tokenKind != T_VERSION_NUMBER) { setError(QCoreApplication::translate( "QQmlParser", "Incomplete version number (dot but no minor)")); return false; // expected the module version number @@ -1600,7 +1750,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) // // recognize the mandatory `as' followed by the module name // - if (! (_tokenKind == T_AS && tokenStartLine() == lineNumber)) { + if (!(_state.tokenKind == T_AS && tokenStartLine() == lineNumber)) { if (fileImport) setError(QCoreApplication::translate("QQmlParser", "File import requires a qualifier")); else @@ -1639,7 +1789,56 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) // fetch the first token after the .pragma/.import directive lex(); - } while (_tokenKind == T_DOT); + } while (_state.tokenKind == T_DOT); return true; } + +const Lexer::State &Lexer::state() const +{ + return _state; +} +void Lexer::setState(const Lexer::State &state) +{ + _state = state; +} + +int Lexer::parseModeFlags() const { + int flags = 0; + if (qmlMode()) + flags |= QmlMode|StaticIsKeyword; + if (yieldIsKeyWord()) + flags |= YieldIsKeyword; + if (_staticIsKeyword) + flags |= StaticIsKeyword; + return flags; +} + +namespace QQmlJS { +QDebug operator<<(QDebug dbg, const Lexer::State &s) +{ + dbg << "{\n" + << " errorCode:" << int(s.errorCode) << ",\n" + << " currentChar:" << s.currentChar << ",\n" + << " tokenValue:" << s.tokenValue << ",\n" + << " parenthesesState:" << s.parenthesesState << ",\n" + << " parenthesesCount:" << s.parenthesesCount << ",\n" + << " outerTemplateBraceCount:" << s.outerTemplateBraceCount << ",\n" + << " bracesCount:" << s.bracesCount << ",\n" + << " stackToken:" << s.stackToken << ",\n" + << " patternFlags:" << s.patternFlags << ",\n" + << " tokenKind:" << s.tokenKind << ",\n" + << " importState:" << int(s.importState) << ",\n" + << " validTokenText:" << s.validTokenText << ",\n" + << " prohibitAutomaticSemicolon:" << s.prohibitAutomaticSemicolon << ",\n" + << " restrictedKeyword:" << s.restrictedKeyword << ",\n" + << " terminator:" << s.terminator << ",\n" + << " followsClosingBrace:" << s.followsClosingBrace << ",\n" + << " delimited:" << s.delimited << ",\n" + << " handlingDirectives:" << s.handlingDirectives << ",\n" + << " generatorLevel:" << s.generatorLevel << "\n}"; + return dbg; +} +} + +QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 7b4c219506..b6144e8894 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLJSLEXER_P_H #define QQMLJSLEXER_P_H @@ -59,6 +23,8 @@ QT_BEGIN_NAMESPACE +class QDebug; + namespace QQmlJS { class Engine; @@ -129,37 +95,31 @@ public: NoQmlImport }; + enum class LexMode { WholeCode, LineByLine }; + + enum class CodeContinuation { Reset, Continue }; + public: - Lexer(Engine *engine); - - int parseModeFlags() const { - int flags = 0; - if (qmlMode()) - flags |= QmlMode|StaticIsKeyword; - if (yieldIsKeyWord()) - flags |= YieldIsKeyword; - if (_staticIsKeyword) - flags |= StaticIsKeyword; - return flags; - } + Lexer(Engine *engine, LexMode lexMode = LexMode::WholeCode); bool qmlMode() const; - bool yieldIsKeyWord() const { return _generatorLevel != 0; } + bool yieldIsKeyWord() const { return _state.generatorLevel != 0; } void setStaticIsKeyword(bool b) { _staticIsKeyword = b; } QString code() const; - void setCode(const QString &code, int lineno, bool qmlMode = true); + void setCode(const QString &code, int lineno, bool qmlMode = true, + CodeContinuation codeContinuation = CodeContinuation::Reset); int lex(); bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); bool scanDirectives(Directives *directives, DiagnosticMessage *error); - int regExpFlags() const { return _patternFlags; } + int regExpFlags() const { return _state.patternFlags; } QString regExpPattern() const { return _tokenText; } - int tokenKind() const { return _tokenKind; } - int tokenOffset() const { return _tokenStartPtr - _code.unicode(); } + int tokenKind() const { return _state.tokenKind; } + int tokenOffset() const { return _currentOffset + _tokenStartPtr - _code.unicode(); } int tokenLength() const { return _tokenLength; } int tokenStartLine() const { return _tokenLine; } @@ -167,14 +127,12 @@ public: inline QStringView tokenSpell() const { return _tokenSpell; } inline QStringView rawString() const { return _rawString; } - double tokenValue() const { return _tokenValue; } + double tokenValue() const { return _state.tokenValue; } QString tokenText() const; Error errorCode() const; QString errorMessage() const; - bool prevTerminator() const; - bool followsClosingBrace() const; bool canInsertAutomaticSemicolon(int token) const; enum ParenthesesState { @@ -183,14 +141,102 @@ public: BalancedParentheses }; - void enterGeneratorBody() { ++_generatorLevel; } - void leaveGeneratorBody() { --_generatorLevel; } + enum class CommentState { NoComment, HadComment, InMultilineComment }; + + void enterGeneratorBody() { ++_state.generatorLevel; } + void leaveGeneratorBody() { --_state.generatorLevel; } + + struct State + { + Error errorCode = NoError; + + QChar currentChar = u'\n'; + double tokenValue = 0; + + // parentheses state + ParenthesesState parenthesesState = IgnoreParentheses; + int parenthesesCount = 0; + + // template string stack + QStack<int> outerTemplateBraceCount; + int bracesCount = -1; + + int stackToken = -1; + + int patternFlags = 0; + int tokenKind = 0; + ImportState importState = ImportState::NoQmlImport; + + bool validTokenText = false; + bool prohibitAutomaticSemicolon = false; + bool restrictedKeyword = false; + bool terminator = false; + bool followsClosingBrace = false; + bool delimited = true; + bool handlingDirectives = false; + CommentState comments = CommentState::NoComment; + int generatorLevel = 0; + + friend bool operator==(State const &s1, State const &s2) + { + if (s1.errorCode != s2.errorCode) + return false; + if (s1.currentChar != s2.currentChar) + return false; + if (s1.tokenValue != s2.tokenValue) + return false; + if (s1.parenthesesState != s2.parenthesesState) + return false; + if (s1.parenthesesCount != s2.parenthesesCount) + return false; + if (s1.outerTemplateBraceCount != s2.outerTemplateBraceCount) + return false; + if (s1.bracesCount != s2.bracesCount) + return false; + if (s1.stackToken != s2.stackToken) + return false; + if (s1.patternFlags != s2.patternFlags) + return false; + if (s1.tokenKind != s2.tokenKind) + return false; + if (s1.importState != s2.importState) + return false; + if (s1.validTokenText != s2.validTokenText) + return false; + if (s1.prohibitAutomaticSemicolon != s2.prohibitAutomaticSemicolon) + return false; + if (s1.restrictedKeyword != s2.restrictedKeyword) + return false; + if (s1.terminator != s2.terminator) + return false; + if (s1.followsClosingBrace != s2.followsClosingBrace) + return false; + if (s1.delimited != s2.delimited) + return false; + if (s1.handlingDirectives != s2.handlingDirectives) + return false; + if (s1.generatorLevel != s2.generatorLevel) + return false; + return true; + } + + friend bool operator!=(State const &s1, State const &s2) { return !(s1 == s2); } + + friend QML_PARSER_EXPORT QDebug operator<<(QDebug dbg, State const &s); + }; + + const State &state() const; + void setState(const State &state); protected: static int classify(const QChar *s, int n, int parseModeFlags); private: + int parseModeFlags() const; + bool prevTerminator() const; + bool followsClosingBrace() const; inline void scanChar(); + inline QChar peekChar(); int scanToken(); int scanNumber(QChar ch); int scanVersionNumber(QChar ch); @@ -213,54 +259,36 @@ private: uint decodeUnicodeEscapeCharacter(bool *ok); QChar decodeHexEscapeCharacter(bool *ok); + friend QML_PARSER_EXPORT QDebug operator<<(QDebug dbg, const Lexer &l); + private: Engine *_engine; + LexMode _lexMode = LexMode::WholeCode; QString _code; + const QChar *_endPtr; + bool _qmlMode; + bool _staticIsKeyword = false; + + bool _skipLinefeed = false; + + int _currentLineNumber = 0; + int _currentColumnNumber = 0; + int _currentOffset = 0; + + int _tokenLength = 0; + int _tokenLine = 0; + int _tokenColumn = 0; + QString _tokenText; QString _errorMessage; QStringView _tokenSpell; QStringView _rawString; - const QChar *_codePtr; - const QChar *_endPtr; - const QChar *_tokenStartPtr; - - QChar _char; - Error _errorCode; - - int _currentLineNumber; - int _currentColumnNumber; - double _tokenValue; - - // parentheses state - ParenthesesState _parenthesesState; - int _parenthesesCount; - - // template string stack - QStack<int> _outerTemplateBraceCount; - int _bracesCount = -1; - - int _stackToken; - - int _patternFlags; - int _tokenKind; - int _tokenLength; - int _tokenLine; - int _tokenColumn; - ImportState _importState = ImportState::NoQmlImport; - - bool _validTokenText; - bool _prohibitAutomaticSemicolon; - bool _restrictedKeyword; - bool _terminator; - bool _followsClosingBrace; - bool _delimited; - bool _qmlMode; - bool _skipLinefeed = false; - int _generatorLevel = 0; - bool _staticIsKeyword = false; - bool _handlingDirectives = false; + const QChar *_codePtr = nullptr; + const QChar *_tokenStartPtr = nullptr; + + State _state; }; } // end of namespace QQmlJS |