diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-12-05 16:32:56 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2015-01-08 22:08:19 +0100 |
commit | 9d7b27f5bf44a46707e6d50ebf51ecf73f91dd1b (patch) | |
tree | 69ec84f34655b892c8bc855aa9a378a4bb2716fa /src/qml/parser/qqmljslexer.cpp | |
parent | bede2a3ac794120be65fa50bfbc8ed04082c10e0 (diff) |
Clean up JS .import/.pragma directive scanning
There's a scanner in QQmlJS::Lexer::scanDirectives that can parse those, so
let's get rid of extra parser that operates on a string. Instead this way we
can do the scanning all in one shot, avoid detaching a copy of the source code
string and (most importantly) bring the parser closer to the copy in Qt
Creator, which uses the directives approach to extract imports and pragma.
Change-Id: Iff6eb8d91a45d8a70f383f953115692be48259de
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@theqtcompany.com>
Diffstat (limited to 'src/qml/parser/qqmljslexer.cpp')
-rw-r--r-- | src/qml/parser/qqmljslexer.cpp | 159 |
1 files changed, 138 insertions, 21 deletions
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 2e2a4e5f8b..a69fa21c32 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -1224,12 +1224,60 @@ bool Lexer::canInsertAutomaticSemicolon(int token) const || _followsClosingBrace; } -bool Lexer::scanDirectives(Directives *directives) +static const int uriTokens[] = { + QQmlJSGrammar::T_IDENTIFIER, + QQmlJSGrammar::T_PROPERTY, + QQmlJSGrammar::T_SIGNAL, + QQmlJSGrammar::T_READONLY, + QQmlJSGrammar::T_ON, + QQmlJSGrammar::T_BREAK, + QQmlJSGrammar::T_CASE, + QQmlJSGrammar::T_CATCH, + QQmlJSGrammar::T_CONTINUE, + QQmlJSGrammar::T_DEFAULT, + QQmlJSGrammar::T_DELETE, + QQmlJSGrammar::T_DO, + QQmlJSGrammar::T_ELSE, + QQmlJSGrammar::T_FALSE, + QQmlJSGrammar::T_FINALLY, + QQmlJSGrammar::T_FOR, + QQmlJSGrammar::T_FUNCTION, + QQmlJSGrammar::T_IF, + QQmlJSGrammar::T_IN, + QQmlJSGrammar::T_INSTANCEOF, + QQmlJSGrammar::T_NEW, + QQmlJSGrammar::T_NULL, + QQmlJSGrammar::T_RETURN, + QQmlJSGrammar::T_SWITCH, + QQmlJSGrammar::T_THIS, + QQmlJSGrammar::T_THROW, + QQmlJSGrammar::T_TRUE, + QQmlJSGrammar::T_TRY, + QQmlJSGrammar::T_TYPEOF, + QQmlJSGrammar::T_VAR, + QQmlJSGrammar::T_VOID, + QQmlJSGrammar::T_WHILE, + QQmlJSGrammar::T_CONST, + QQmlJSGrammar::T_DEBUGGER, + QQmlJSGrammar::T_RESERVED_WORD, + QQmlJSGrammar::T_WITH, + + QQmlJSGrammar::EOF_SYMBOL +}; +static inline bool isUriToken(int token) { - if (_qmlMode) { - // the directives are a Javascript-only extension. - return false; + const int *current = uriTokens; + while (*current != QQmlJSGrammar::EOF_SYMBOL) { + if (*current == token) + return true; + ++current; } + return false; +} + +bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error) +{ + Q_ASSERT(!_qmlMode); lex(); // fetch the first token @@ -1237,24 +1285,33 @@ bool Lexer::scanDirectives(Directives *directives) return true; do { - lex(); // skip T_DOT - const int lineNumber = tokenStartLine(); + const int column = tokenStartColumn(); + + lex(); // skip T_DOT if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD)) - return false; // expected a valid QML/JS directive + return true; // expected a valid QML/JS directive const QString directiveName = tokenText(); if (! (directiveName == QLatin1String("pragma") || - directiveName == QLatin1String("import"))) + directiveName == QLatin1String("import"))) { + error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; // not a valid directive name + } // it must be a pragma or an import directive. if (directiveName == QLatin1String("pragma")) { // .pragma library - if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) + if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) { + error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; // expected `library + } // we found a .pragma library directive directives->pragmaLibrary(); @@ -1273,22 +1330,53 @@ bool Lexer::scanDirectives(Directives *directives) fileImport = true; pathOrUri = tokenText(); + if (!pathOrUri.endsWith(QLatin1String("js"))) { + error->message = QCoreApplication::translate("QQmlParser","Imported file must be a script"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); + return false; + } + } else if (_tokenKind == T_IDENTIFIER) { // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER - pathOrUri = tokenText(); + while (true) { + if (!isUriToken(_tokenKind)) { + error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); + return false; + } - lex(); // skip the first T_IDENTIFIER - for (; _tokenKind == T_DOT; lex()) { - if (lex() != T_IDENTIFIER) + pathOrUri.append(tokenText()); + + lex(); + if (tokenStartLine() != lineNumber) { + error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; + } + if (_tokenKind != QQmlJSGrammar::T_DOT) + break; + + pathOrUri.append(QLatin1Char('.')); - pathOrUri += QLatin1Char('.'); - pathOrUri += tokenText(); + lex(); + if (tokenStartLine() != lineNumber) { + error->message = QCoreApplication::translate("QQmlParser","Invalid module URI"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); + return false; + } } - if (_tokenKind != T_NUMERIC_LITERAL) + if (_tokenKind != T_NUMERIC_LITERAL) { + error->message = QCoreApplication::translate("QQmlParser","Module import requires a version"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; // expected the module version number + } version = tokenText(); } @@ -1296,22 +1384,51 @@ bool Lexer::scanDirectives(Directives *directives) // // recognize the mandatory `as' followed by the module name // - if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as"))) + if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as") && tokenStartLine() == lineNumber)) { + if (fileImport) + error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier"); + else + error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"); + if (tokenStartLine() != lineNumber) { + error->loc.startLine = lineNumber; + error->loc.startColumn = column; + } else { + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); + } return false; // expected `as' + } - if (lex() != T_IDENTIFIER) + if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) { + if (fileImport) + error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier"); + else + error->message = QCoreApplication::translate("QQmlParser", "Module import requires a qualifier"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; // expected module name + } const QString module = tokenText(); + if (!module.at(0).isUpper()) { + error->message = QCoreApplication::translate("QQmlParser","Invalid import qualifier"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); + return false; + } if (fileImport) - directives->importFile(pathOrUri, module); + directives->importFile(pathOrUri, module, lineNumber, column); else - directives->importModule(pathOrUri, version, module); + directives->importModule(pathOrUri, version, module, lineNumber, column); } - if (tokenStartLine() != lineNumber) + if (tokenStartLine() != lineNumber) { + error->message = QCoreApplication::translate("QQmlParser", "Syntax error"); + error->loc.startLine = tokenStartLine(); + error->loc.startColumn = tokenStartColumn(); return false; // the directives cannot span over multiple lines + } // fetch the first token after the .pragma/.import directive lex(); |