2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2016-01-15 14:58:39 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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
|
2016-01-15 14:58:39 +01:00
|
|
|
** 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.
|
2011-05-12 13:25:35 +02:00
|
|
|
**
|
2016-01-15 14:58:39 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** 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-3.0.html.
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2010-01-18 13:13:34 +01:00
|
|
|
|
|
|
|
|
#include "qmljslexer_p.h"
|
|
|
|
|
#include "qmljsengine_p.h"
|
2013-11-06 14:17:23 +01:00
|
|
|
#include "qmljskeywords_p.h"
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
#include "qmljs/parser/qmljsdiagnosticmessage_p.h"
|
|
|
|
|
#include "qmljs/parser/qmljsmemorypool_p.h"
|
|
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
#include <QtCore/qcoreapplication.h>
|
|
|
|
|
#include <QtCore/qvarlengtharray.h>
|
|
|
|
|
#include <QtCore/qdebug.h>
|
2019-10-04 16:11:02 +02:00
|
|
|
#include <QtCore/QScopedValueRollback>
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2020-03-04 06:45:26 +01:00
|
|
|
QT_BEGIN_NAMESPACE
|
2010-10-19 16:08:44 +02:00
|
|
|
Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
|
2020-03-04 06:45:26 +01:00
|
|
|
QT_END_NAMESPACE
|
2010-03-15 15:28:18 +01:00
|
|
|
|
2020-03-04 10:41:59 +01:00
|
|
|
QT_QML_BEGIN_NAMESPACE
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
using namespace QmlJS;
|
2010-03-03 11:38:33 +01:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
static inline int regExpFlagFromChar(const QChar &ch)
|
2011-09-13 08:42:52 +02:00
|
|
|
{
|
|
|
|
|
switch (ch.unicode()) {
|
2011-09-19 14:09:26 +02:00
|
|
|
case 'g': return Lexer::RegExp_Global;
|
|
|
|
|
case 'i': return Lexer::RegExp_IgnoreCase;
|
|
|
|
|
case 'm': return Lexer::RegExp_Multiline;
|
2018-10-16 15:32:58 +02:00
|
|
|
case 'u': return Lexer::RegExp_Unicode;
|
|
|
|
|
case 'y': return Lexer::RegExp_Sticky;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
static inline unsigned char convertHex(ushort c)
|
2011-09-13 08:42:52 +02:00
|
|
|
{
|
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
|
return (c - '0');
|
|
|
|
|
else if (c >= 'a' && c <= 'f')
|
|
|
|
|
return (c - 'a' + 10);
|
|
|
|
|
else
|
|
|
|
|
return (c - 'A' + 10);
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
static inline QChar convertHex(QChar c1, QChar c2)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
Lexer::Lexer(Engine *engine)
|
|
|
|
|
: _engine(engine)
|
2018-10-16 15:32:58 +02:00
|
|
|
, _codePtr(nullptr)
|
|
|
|
|
, _endPtr(nullptr)
|
|
|
|
|
, _tokenStartPtr(nullptr)
|
2011-09-13 08:42:52 +02:00
|
|
|
, _char(QLatin1Char('\n'))
|
|
|
|
|
, _errorCode(NoError)
|
|
|
|
|
, _currentLineNumber(0)
|
2018-10-16 15:32:58 +02:00
|
|
|
, _currentColumnNumber(0)
|
2011-09-13 08:42:52 +02:00
|
|
|
, _tokenValue(0)
|
|
|
|
|
, _parenthesesState(IgnoreParentheses)
|
|
|
|
|
, _parenthesesCount(0)
|
|
|
|
|
, _stackToken(-1)
|
|
|
|
|
, _patternFlags(0)
|
|
|
|
|
, _tokenKind(0)
|
|
|
|
|
, _tokenLength(0)
|
|
|
|
|
, _tokenLine(0)
|
2018-10-16 15:32:58 +02:00
|
|
|
, _tokenColumn(0)
|
2011-09-13 08:42:52 +02:00
|
|
|
, _validTokenText(false)
|
|
|
|
|
, _prohibitAutomaticSemicolon(false)
|
|
|
|
|
, _restrictedKeyword(false)
|
|
|
|
|
, _terminator(false)
|
2011-09-19 14:09:26 +02:00
|
|
|
, _followsClosingBrace(false)
|
2011-12-07 11:16:26 +01:00
|
|
|
, _delimited(true)
|
2011-09-13 08:42:52 +02:00
|
|
|
, _qmlMode(true)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
if (engine)
|
|
|
|
|
engine->setLexer(this);
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2011-12-07 11:16:26 +01:00
|
|
|
bool Lexer::qmlMode() const
|
|
|
|
|
{
|
|
|
|
|
return _qmlMode;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
QString Lexer::code() const
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
return _code;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
if (_engine)
|
|
|
|
|
_engine->setCode(code);
|
|
|
|
|
|
|
|
|
|
_qmlMode = qmlMode;
|
|
|
|
|
_code = code;
|
|
|
|
|
_tokenText.clear();
|
|
|
|
|
_tokenText.reserve(1024);
|
|
|
|
|
_errorMessage.clear();
|
|
|
|
|
_tokenSpell = QStringRef();
|
2019-06-19 19:42:15 +02:00
|
|
|
_rawString = QStringRef();
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
_codePtr = code.unicode();
|
2013-11-06 14:17:23 +01:00
|
|
|
_endPtr = _codePtr + code.length();
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenStartPtr = _codePtr;
|
|
|
|
|
|
|
|
|
|
_char = QLatin1Char('\n');
|
|
|
|
|
_errorCode = NoError;
|
|
|
|
|
|
|
|
|
|
_currentLineNumber = lineno;
|
2018-10-16 15:32:58 +02:00
|
|
|
_currentColumnNumber = 0;
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenValue = 0;
|
|
|
|
|
|
|
|
|
|
// parentheses state
|
|
|
|
|
_parenthesesState = IgnoreParentheses;
|
|
|
|
|
_parenthesesCount = 0;
|
|
|
|
|
|
|
|
|
|
_stackToken = -1;
|
|
|
|
|
|
|
|
|
|
_patternFlags = 0;
|
|
|
|
|
_tokenLength = 0;
|
|
|
|
|
_tokenLine = lineno;
|
2018-10-16 15:32:58 +02:00
|
|
|
_tokenColumn = 0;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
_validTokenText = false;
|
|
|
|
|
_prohibitAutomaticSemicolon = false;
|
|
|
|
|
_restrictedKeyword = false;
|
|
|
|
|
_terminator = false;
|
2011-09-19 14:09:26 +02:00
|
|
|
_followsClosingBrace = false;
|
2011-12-07 11:16:26 +01:00
|
|
|
_delimited = true;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
void Lexer::scanChar()
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2019-06-19 19:42:15 +02:00
|
|
|
if (_skipLinefeed) {
|
|
|
|
|
Q_ASSERT(*_codePtr == QLatin1Char('\n'));
|
|
|
|
|
++_codePtr;
|
|
|
|
|
_skipLinefeed = false;
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
_char = *_codePtr++;
|
2018-10-16 15:32:58 +02:00
|
|
|
++_currentColumnNumber;
|
2019-06-19 19:42:15 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (isLineTerminator()) {
|
2019-06-19 19:42:15 +02:00
|
|
|
if (_char == QLatin1Char('\r')) {
|
|
|
|
|
if (_codePtr < _endPtr && *_codePtr == QLatin1Char('\n'))
|
|
|
|
|
_skipLinefeed = true;
|
|
|
|
|
_char = QLatin1Char('\n');
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
++_currentLineNumber;
|
2018-10-16 15:32:58 +02:00
|
|
|
_currentColumnNumber = 0;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
namespace {
|
|
|
|
|
inline bool isBinop(int tok)
|
|
|
|
|
{
|
|
|
|
|
switch (tok) {
|
|
|
|
|
case Lexer::T_AND:
|
|
|
|
|
case Lexer::T_AND_AND:
|
|
|
|
|
case Lexer::T_AND_EQ:
|
|
|
|
|
case Lexer::T_DIVIDE_:
|
|
|
|
|
case Lexer::T_DIVIDE_EQ:
|
|
|
|
|
case Lexer::T_EQ:
|
|
|
|
|
case Lexer::T_EQ_EQ:
|
|
|
|
|
case Lexer::T_EQ_EQ_EQ:
|
|
|
|
|
case Lexer::T_GE:
|
|
|
|
|
case Lexer::T_GT:
|
|
|
|
|
case Lexer::T_GT_GT:
|
|
|
|
|
case Lexer::T_GT_GT_EQ:
|
|
|
|
|
case Lexer::T_GT_GT_GT:
|
|
|
|
|
case Lexer::T_GT_GT_GT_EQ:
|
|
|
|
|
case Lexer::T_LE:
|
|
|
|
|
case Lexer::T_LT:
|
|
|
|
|
case Lexer::T_LT_LT:
|
|
|
|
|
case Lexer::T_LT_LT_EQ:
|
|
|
|
|
case Lexer::T_MINUS:
|
|
|
|
|
case Lexer::T_MINUS_EQ:
|
|
|
|
|
case Lexer::T_NOT_EQ:
|
|
|
|
|
case Lexer::T_NOT_EQ_EQ:
|
|
|
|
|
case Lexer::T_OR:
|
|
|
|
|
case Lexer::T_OR_EQ:
|
|
|
|
|
case Lexer::T_OR_OR:
|
|
|
|
|
case Lexer::T_PLUS:
|
|
|
|
|
case Lexer::T_PLUS_EQ:
|
|
|
|
|
case Lexer::T_REMAINDER:
|
|
|
|
|
case Lexer::T_REMAINDER_EQ:
|
|
|
|
|
case Lexer::T_RETURN:
|
|
|
|
|
case Lexer::T_STAR:
|
|
|
|
|
case Lexer::T_STAR_EQ:
|
|
|
|
|
case Lexer::T_XOR:
|
|
|
|
|
case Lexer::T_XOR_EQ:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
int hexDigit(QChar c)
|
|
|
|
|
{
|
|
|
|
|
if (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
|
|
|
|
|
return c.unicode() - '0';
|
|
|
|
|
if (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
|
|
|
|
|
return c.unicode() - 'a' + 10;
|
|
|
|
|
if (c >= QLatin1Char('A') && c <= QLatin1Char('F'))
|
|
|
|
|
return c.unicode() - 'A' + 10;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int octalDigit(QChar c)
|
|
|
|
|
{
|
|
|
|
|
if (c >= QLatin1Char('0') && c <= QLatin1Char('7'))
|
|
|
|
|
return c.unicode() - '0';
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
int Lexer::lex()
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-19 14:09:26 +02:00
|
|
|
const int previousTokenKind = _tokenKind;
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
again:
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenSpell = QStringRef();
|
2019-06-19 19:42:15 +02:00
|
|
|
_rawString = QStringRef();
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenKind = scanToken();
|
|
|
|
|
_tokenLength = _codePtr - _tokenStartPtr - 1;
|
|
|
|
|
|
|
|
|
|
_delimited = false;
|
|
|
|
|
_restrictedKeyword = false;
|
2011-09-19 14:09:26 +02:00
|
|
|
_followsClosingBrace = (previousTokenKind == T_RBRACE);
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
// update the flags
|
|
|
|
|
switch (_tokenKind) {
|
|
|
|
|
case T_LBRACE:
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_bracesCount > 0)
|
|
|
|
|
++_bracesCount;
|
|
|
|
|
Q_FALLTHROUGH();
|
2011-09-13 08:42:52 +02:00
|
|
|
case T_SEMICOLON:
|
2019-10-04 16:11:02 +02:00
|
|
|
_importState = ImportState::NoQmlImport;
|
|
|
|
|
Q_FALLTHROUGH();
|
2013-11-06 14:17:23 +01:00
|
|
|
case T_QUESTION:
|
2011-12-07 11:16:26 +01:00
|
|
|
case T_COLON:
|
2013-11-06 14:17:23 +01:00
|
|
|
case T_TILDE:
|
2011-09-13 08:42:52 +02:00
|
|
|
_delimited = true;
|
|
|
|
|
break;
|
2019-10-04 16:11:02 +02:00
|
|
|
case T_AUTOMATIC_SEMICOLON:
|
|
|
|
|
case T_AS:
|
|
|
|
|
_importState = ImportState::NoQmlImport;
|
|
|
|
|
Q_FALLTHROUGH();
|
2013-11-06 14:17:23 +01:00
|
|
|
default:
|
|
|
|
|
if (isBinop(_tokenKind))
|
|
|
|
|
_delimited = true;
|
|
|
|
|
break;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2019-10-04 16:11:02 +02:00
|
|
|
case T_IMPORT:
|
|
|
|
|
if (qmlMode() || (_handlingDirectives && previousTokenKind == T_DOT))
|
|
|
|
|
_importState = ImportState::SawImport;
|
|
|
|
|
if (isBinop(_tokenKind))
|
|
|
|
|
_delimited = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
case T_IF:
|
|
|
|
|
case T_FOR:
|
|
|
|
|
case T_WHILE:
|
|
|
|
|
case T_WITH:
|
|
|
|
|
_parenthesesState = CountParentheses;
|
|
|
|
|
_parenthesesCount = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
case T_ELSE:
|
2011-09-13 08:42:52 +02:00
|
|
|
case T_DO:
|
|
|
|
|
_parenthesesState = BalancedParentheses;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_CONTINUE:
|
|
|
|
|
case T_BREAK:
|
|
|
|
|
case T_RETURN:
|
2018-10-16 15:32:58 +02:00
|
|
|
case T_YIELD:
|
2011-09-13 08:42:52 +02:00
|
|
|
case T_THROW:
|
|
|
|
|
_restrictedKeyword = true;
|
|
|
|
|
break;
|
2018-10-16 15:32:58 +02:00
|
|
|
case T_RBRACE:
|
|
|
|
|
if (_bracesCount > 0)
|
|
|
|
|
--_bracesCount;
|
|
|
|
|
if (_bracesCount == 0)
|
|
|
|
|
goto again;
|
2011-09-13 08:42:52 +02:00
|
|
|
} // 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;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
break;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
case BalancedParentheses:
|
2013-11-06 14:17:23 +01:00
|
|
|
if (_tokenKind != T_DO && _tokenKind != T_ELSE)
|
|
|
|
|
_parenthesesState = IgnoreParentheses;
|
2011-09-13 08:42:52 +02:00
|
|
|
break;
|
2010-01-18 13:13:34 +01:00
|
|
|
} // switch
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
return _tokenKind;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
Q_ASSERT(_char == QLatin1Char('u'));
|
|
|
|
|
scanChar(); // skip u
|
|
|
|
|
if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) {
|
|
|
|
|
uint codePoint = 0;
|
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
|
int digit = hexDigit(_char);
|
|
|
|
|
if (digit < 0)
|
|
|
|
|
goto error;
|
|
|
|
|
codePoint *= 16;
|
|
|
|
|
codePoint += digit;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
*ok = true;
|
|
|
|
|
return codePoint;
|
|
|
|
|
} else if (_codePtr < _endPtr && _char == QLatin1Char('{')) {
|
|
|
|
|
scanChar(); // skip '{'
|
|
|
|
|
uint codePoint = 0;
|
|
|
|
|
if (!isHexDigit(_char))
|
|
|
|
|
// need at least one hex digit
|
|
|
|
|
goto error;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
while (_codePtr <= _endPtr) {
|
|
|
|
|
int digit = hexDigit(_char);
|
|
|
|
|
if (digit < 0)
|
|
|
|
|
break;
|
|
|
|
|
codePoint *= 16;
|
|
|
|
|
codePoint += digit;
|
|
|
|
|
if (codePoint > 0x10ffff)
|
|
|
|
|
goto error;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_char != QLatin1Char('}'))
|
|
|
|
|
goto error;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
scanChar(); // skip '}'
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
*ok = true;
|
|
|
|
|
return codePoint;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
error:
|
|
|
|
|
_errorCode = IllegalUnicodeEscapeSequence;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence");
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
*ok = false;
|
2018-10-16 15:32:58 +02:00
|
|
|
return 0;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
2010-10-19 16:08:44 +02:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
QChar Lexer::decodeHexEscapeCharacter(bool *ok)
|
|
|
|
|
{
|
|
|
|
|
if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) {
|
|
|
|
|
scanChar();
|
|
|
|
|
|
|
|
|
|
const QChar c1 = _char;
|
|
|
|
|
scanChar();
|
|
|
|
|
|
|
|
|
|
const QChar c2 = _char;
|
|
|
|
|
scanChar();
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
*ok = true;
|
|
|
|
|
|
|
|
|
|
return convertHex(c1, c2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*ok = false;
|
|
|
|
|
return QChar();
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
static inline bool isIdentifierStart(uint ch)
|
2013-11-06 14:17:23 +01:00
|
|
|
{
|
|
|
|
|
// fast path for ascii
|
2018-10-16 15:32:58 +02:00
|
|
|
if ((ch >= 'a' && ch <= 'z') ||
|
|
|
|
|
(ch >= 'A' && ch <= 'Z') ||
|
2016-04-29 11:00:30 +02:00
|
|
|
ch == '$' || ch == '_')
|
2013-11-06 14:17:23 +01:00
|
|
|
return true;
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
switch (QChar::category(ch)) {
|
2013-11-06 14:17:23 +01:00
|
|
|
case QChar::Number_Letter:
|
|
|
|
|
case QChar::Letter_Uppercase:
|
|
|
|
|
case QChar::Letter_Lowercase:
|
|
|
|
|
case QChar::Letter_Titlecase:
|
|
|
|
|
case QChar::Letter_Modifier:
|
|
|
|
|
case QChar::Letter_Other:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
static bool isIdentifierPart(uint ch)
|
2013-11-06 14:17:23 +01:00
|
|
|
{
|
|
|
|
|
// fast path for ascii
|
2018-10-16 15:32:58 +02:00
|
|
|
if ((ch >= 'a' && ch <= 'z') ||
|
|
|
|
|
(ch >= 'A' && ch <= 'Z') ||
|
|
|
|
|
(ch >= '0' && ch <= '9') ||
|
2016-04-29 11:00:30 +02:00
|
|
|
ch == '$' || ch == '_' ||
|
2018-10-16 15:32:58 +02:00
|
|
|
ch == 0x200c /* ZWNJ */ || ch == 0x200d /* ZWJ */)
|
2013-11-06 14:17:23 +01:00
|
|
|
return true;
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
switch (QChar::category(ch)) {
|
2013-11-06 14:17:23 +01:00
|
|
|
case QChar::Mark_NonSpacing:
|
|
|
|
|
case QChar::Mark_SpacingCombining:
|
|
|
|
|
|
|
|
|
|
case QChar::Number_DecimalDigit:
|
|
|
|
|
case QChar::Number_Letter:
|
|
|
|
|
|
|
|
|
|
case QChar::Letter_Uppercase:
|
|
|
|
|
case QChar::Letter_Lowercase:
|
|
|
|
|
case QChar::Letter_Titlecase:
|
|
|
|
|
case QChar::Letter_Modifier:
|
|
|
|
|
case QChar::Letter_Other:
|
|
|
|
|
|
|
|
|
|
case QChar::Punctuation_Connector:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
int Lexer::scanToken()
|
|
|
|
|
{
|
|
|
|
|
if (_stackToken != -1) {
|
|
|
|
|
int tk = _stackToken;
|
|
|
|
|
_stackToken = -1;
|
|
|
|
|
return tk;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_bracesCount == 0) {
|
|
|
|
|
// we're inside a Template string
|
|
|
|
|
return scanString(TemplateContinuation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
_terminator = false;
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
_validTokenText = false;
|
|
|
|
|
|
2019-10-04 16:11:02 +02:00
|
|
|
// handle comment can be called after a '/' has been read
|
|
|
|
|
// and returns true if it actually encountered a comment
|
|
|
|
|
auto handleComment = [this](){
|
|
|
|
|
if (_char == QLatin1Char('*')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
while (_codePtr <= _endPtr) {
|
|
|
|
|
if (_char == QLatin1Char('*')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('/')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
|
|
|
|
|
if (_engine) {
|
|
|
|
|
_engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
|
|
|
|
|
tokenStartLine(), tokenStartColumn() + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (_char == QLatin1Char('/')) {
|
|
|
|
|
while (_codePtr <= _endPtr && !isLineTerminator()) {
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
if (_engine) {
|
|
|
|
|
_engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
|
|
|
|
|
tokenStartLine(), tokenStartColumn() + 2);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2011-09-19 14:09:26 +02:00
|
|
|
while (_char.isSpace()) {
|
2018-10-16 15:32:58 +02:00
|
|
|
if (isLineTerminator()) {
|
2011-09-13 08:42:52 +02:00
|
|
|
if (_restrictedKeyword) {
|
|
|
|
|
// automatic semicolon insertion
|
|
|
|
|
_tokenLine = _currentLineNumber;
|
2018-10-16 15:32:58 +02:00
|
|
|
_tokenColumn = _currentColumnNumber;
|
2013-01-22 11:15:23 +01:00
|
|
|
_tokenStartPtr = _codePtr - 1;
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_SEMICOLON;
|
2010-01-18 13:13:34 +01:00
|
|
|
} else {
|
2011-09-13 08:42:52 +02:00
|
|
|
_terminator = true;
|
|
|
|
|
syncProhibitAutomaticSemicolon();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenStartPtr = _codePtr - 1;
|
|
|
|
|
_tokenLine = _currentLineNumber;
|
2018-10-16 15:32:58 +02:00
|
|
|
_tokenColumn = _currentColumnNumber;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
if (_codePtr > _endPtr)
|
2011-09-13 08:42:52 +02:00
|
|
|
return EOF_SYMBOL;
|
|
|
|
|
|
|
|
|
|
const QChar ch = _char;
|
|
|
|
|
scanChar();
|
|
|
|
|
|
|
|
|
|
switch (ch.unicode()) {
|
|
|
|
|
case '~': return T_TILDE;
|
|
|
|
|
case '}': return T_RBRACE;
|
|
|
|
|
|
|
|
|
|
case '|':
|
|
|
|
|
if (_char == QLatin1Char('|')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_OR_OR;
|
|
|
|
|
} else if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_OR_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_OR;
|
|
|
|
|
|
|
|
|
|
case '{': return T_LBRACE;
|
|
|
|
|
|
|
|
|
|
case '^':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_XOR_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_XOR;
|
|
|
|
|
|
|
|
|
|
case ']': return T_RBRACKET;
|
|
|
|
|
case '[': return T_LBRACKET;
|
2020-02-28 17:51:32 +01:00
|
|
|
case '?': {
|
|
|
|
|
if (_char == QLatin1Char('?')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_QUESTION_QUESTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return T_QUESTION;
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
case '>':
|
|
|
|
|
if (_char == QLatin1Char('>')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('>')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_GT_GT_GT_EQ;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_GT_GT_GT;
|
|
|
|
|
} else if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_GT_GT_EQ;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_GT_GT;
|
|
|
|
|
} else if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_GE;
|
|
|
|
|
}
|
|
|
|
|
return T_GT;
|
|
|
|
|
|
|
|
|
|
case '=':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_EQ_EQ_EQ;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_EQ_EQ;
|
2018-10-16 15:32:58 +02:00
|
|
|
} else if (_char == QLatin1Char('>')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_ARROW;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
return T_EQ;
|
|
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_LE;
|
|
|
|
|
} else if (_char == QLatin1Char('<')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_LT_LT_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_LT_LT;
|
|
|
|
|
}
|
|
|
|
|
return T_LT;
|
|
|
|
|
|
|
|
|
|
case ';': return T_SEMICOLON;
|
|
|
|
|
case ':': return T_COLON;
|
|
|
|
|
|
|
|
|
|
case '/':
|
2019-10-04 16:11:02 +02:00
|
|
|
if (handleComment())
|
2011-09-13 08:42:52 +02:00
|
|
|
goto again;
|
2019-10-04 16:11:02 +02:00
|
|
|
else if (_char == QLatin1Char('=')) {
|
2011-09-13 08:42:52 +02:00
|
|
|
scanChar();
|
|
|
|
|
return T_DIVIDE_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_DIVIDE_;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
case '.':
|
2019-10-04 16:11:02 +02:00
|
|
|
if (_importState == ImportState::SawImport)
|
|
|
|
|
return T_DOT;
|
2018-10-16 15:32:58 +02:00
|
|
|
if (isDecimalDigit(_char.unicode()))
|
|
|
|
|
return scanNumber(ch);
|
|
|
|
|
if (_char == QLatin1Char('.')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('.')) {
|
2011-09-13 08:42:52 +02:00
|
|
|
scanChar();
|
2018-10-16 15:32:58 +02:00
|
|
|
return T_ELLIPSIS;
|
|
|
|
|
} else {
|
|
|
|
|
_errorCode = IllegalCharacter;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unexpected token '.'");
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_ERROR;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
return T_DOT;
|
|
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_MINUS_EQ;
|
|
|
|
|
} else if (_char == QLatin1Char('-')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
|
2011-09-21 10:29:21 +02:00
|
|
|
_stackToken = T_MINUS_MINUS;
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_SEMICOLON;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
return T_MINUS_MINUS;
|
|
|
|
|
}
|
|
|
|
|
return T_MINUS;
|
|
|
|
|
|
|
|
|
|
case ',': return T_COMMA;
|
|
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_PLUS_EQ;
|
|
|
|
|
} else if (_char == QLatin1Char('+')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
|
2011-09-13 08:42:52 +02:00
|
|
|
_stackToken = T_PLUS_PLUS;
|
|
|
|
|
return T_SEMICOLON;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
return T_PLUS_PLUS;
|
|
|
|
|
}
|
|
|
|
|
return T_PLUS;
|
|
|
|
|
|
|
|
|
|
case '*':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_STAR_EQ;
|
2018-10-16 15:32:58 +02:00
|
|
|
} else if (_char == QLatin1Char('*')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_STAR_STAR_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_STAR_STAR;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
return T_STAR;
|
|
|
|
|
|
|
|
|
|
case ')': return T_RPAREN;
|
|
|
|
|
case '(': return T_LPAREN;
|
|
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
case '@': return T_AT;
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
case '&':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_AND_EQ;
|
|
|
|
|
} else if (_char == QLatin1Char('&')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_AND_AND;
|
|
|
|
|
}
|
|
|
|
|
return T_AND;
|
|
|
|
|
|
|
|
|
|
case '%':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_REMAINDER_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_REMAINDER;
|
|
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('=')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
return T_NOT_EQ_EQ;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_NOT_EQ;
|
|
|
|
|
}
|
|
|
|
|
return T_NOT;
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
case '`':
|
|
|
|
|
_outerTemplateBraceCount.push(_bracesCount);
|
|
|
|
|
Q_FALLTHROUGH();
|
2011-09-13 08:42:52 +02:00
|
|
|
case '\'':
|
2018-10-16 15:32:58 +02:00
|
|
|
case '"':
|
|
|
|
|
return scanString(ScanStringMode(ch.unicode()));
|
2012-07-31 10:12:26 +02:00
|
|
|
case '0':
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
2019-10-04 16:11:02 +02:00
|
|
|
if (_importState == ImportState::SawImport)
|
|
|
|
|
return scanVersionNumber(ch);
|
|
|
|
|
else
|
|
|
|
|
return scanNumber(ch);
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
default: {
|
2018-10-16 15:32:58 +02:00
|
|
|
uint c = ch.unicode();
|
2013-11-06 14:17:23 +01:00
|
|
|
bool identifierWithEscapeChars = false;
|
2018-10-16 15:32:58 +02:00
|
|
|
if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) {
|
|
|
|
|
c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
|
|
|
|
|
scanChar();
|
|
|
|
|
} else if (c == '\\' && _char == QLatin1Char('u')) {
|
2013-11-06 14:17:23 +01:00
|
|
|
identifierWithEscapeChars = true;
|
|
|
|
|
bool ok = false;
|
|
|
|
|
c = decodeUnicodeEscapeCharacter(&ok);
|
2018-10-16 15:32:58 +02:00
|
|
|
if (!ok)
|
2013-11-06 14:17:23 +01:00
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (isIdentifierStart(c)) {
|
|
|
|
|
if (identifierWithEscapeChars) {
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText.resize(0);
|
2018-10-16 15:32:58 +02:00
|
|
|
if (QChar::requiresSurrogates(c)) {
|
|
|
|
|
_tokenText += QChar(QChar::highSurrogate(c));
|
|
|
|
|
_tokenText += QChar(QChar::lowSurrogate(c));
|
|
|
|
|
} else {
|
|
|
|
|
_tokenText += QChar(c);
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
_validTokenText = true;
|
|
|
|
|
}
|
2018-10-16 15:32:58 +02:00
|
|
|
while (_codePtr <= _endPtr) {
|
|
|
|
|
c = _char.unicode();
|
|
|
|
|
if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) {
|
|
|
|
|
scanChar();
|
|
|
|
|
c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
|
|
|
|
|
} else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
|
|
|
|
|
if (!identifierWithEscapeChars) {
|
2011-09-13 08:42:52 +02:00
|
|
|
identifierWithEscapeChars = true;
|
|
|
|
|
_tokenText.resize(0);
|
|
|
|
|
_tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
|
|
|
|
|
_validTokenText = true;
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
scanChar(); // skip '\\'
|
|
|
|
|
bool ok = false;
|
2013-11-06 14:17:23 +01:00
|
|
|
c = decodeUnicodeEscapeCharacter(&ok);
|
2018-10-16 15:32:58 +02:00
|
|
|
if (!ok)
|
2011-09-13 08:42:52 +02:00
|
|
|
return T_ERROR;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (!isIdentifierPart(c))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (identifierWithEscapeChars) {
|
|
|
|
|
if (QChar::requiresSurrogates(c)) {
|
|
|
|
|
_tokenText += QChar(QChar::highSurrogate(c));
|
|
|
|
|
_tokenText += QChar(QChar::lowSurrogate(c));
|
|
|
|
|
} else {
|
|
|
|
|
_tokenText += QChar(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-06 14:17:23 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (!isIdentifierPart(c))
|
|
|
|
|
break;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (identifierWithEscapeChars) {
|
|
|
|
|
if (QChar::requiresSurrogates(c)) {
|
|
|
|
|
_tokenText += QChar(QChar::highSurrogate(c));
|
|
|
|
|
_tokenText += QChar(QChar::lowSurrogate(c));
|
|
|
|
|
} else {
|
|
|
|
|
_tokenText += QChar(c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenLength = _codePtr - _tokenStartPtr - 1;
|
2013-11-06 14:17:23 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
int kind = T_IDENTIFIER;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (!identifierWithEscapeChars)
|
|
|
|
|
kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags());
|
2013-11-06 14:17:23 +01:00
|
|
|
|
2019-10-04 16:11:02 +02:00
|
|
|
if (kind == T_FUNCTION) {
|
|
|
|
|
continue_skipping:
|
|
|
|
|
while (_codePtr < _endPtr && _char.isSpace())
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_char == QLatin1Char('*')) {
|
|
|
|
|
_tokenLength = _codePtr - _tokenStartPtr - 1;
|
|
|
|
|
kind = T_FUNCTION_STAR;
|
|
|
|
|
scanChar();
|
|
|
|
|
} else if (_char == QLatin1Char('/')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (handleComment())
|
|
|
|
|
goto continue_skipping;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_engine) {
|
|
|
|
|
if (kind == T_IDENTIFIER && identifierWithEscapeChars)
|
|
|
|
|
_tokenSpell = _engine->newStringRef(_tokenText);
|
|
|
|
|
else
|
|
|
|
|
_tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
return kind;
|
2012-07-31 10:12:26 +02:00
|
|
|
}
|
2013-11-06 14:17:23 +01:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
int Lexer::scanString(ScanStringMode mode)
|
2012-07-31 10:12:26 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
|
|
|
|
|
bool multilineStringLiteral = false;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2019-06-19 19:42:15 +02:00
|
|
|
const QChar *startCode = _codePtr - 1;
|
|
|
|
|
// in case we just parsed a \r, we need to reset this flag to get things working
|
|
|
|
|
// correctly in the loop below and afterwards
|
|
|
|
|
_skipLinefeed = false;
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
if (_engine) {
|
|
|
|
|
while (_codePtr <= _endPtr) {
|
2019-06-19 19:42:15 +02:00
|
|
|
if (isLineTerminator()) {
|
|
|
|
|
if ((quote == QLatin1Char('`') || qmlMode()))
|
2018-10-16 15:32:58 +02:00
|
|
|
break;
|
|
|
|
|
_errorCode = IllegalCharacter;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Stray newline in string literal");
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
} else if (_char == QLatin1Char('\\')) {
|
2020-04-29 15:06:32 +02:00
|
|
|
break;
|
2018-10-16 15:32:58 +02:00
|
|
|
} else if (_char == '$' && quote == QLatin1Char('`')) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (_char == quote) {
|
2019-06-19 19:42:15 +02:00
|
|
|
_tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1);
|
|
|
|
|
_rawString = _tokenSpell;
|
2012-07-31 10:12:26 +02:00
|
|
|
scanChar();
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
if (quote == QLatin1Char('`'))
|
|
|
|
|
_bracesCount = _outerTemplateBraceCount.pop();
|
|
|
|
|
|
|
|
|
|
if (mode == TemplateHead)
|
|
|
|
|
return T_NO_SUBSTITUTION_TEMPLATE;
|
|
|
|
|
else if (mode == TemplateContinuation)
|
|
|
|
|
return T_TEMPLATE_TAIL;
|
|
|
|
|
else
|
|
|
|
|
return T_STRING_LITERAL;
|
2012-07-31 10:12:26 +02:00
|
|
|
}
|
2019-06-19 19:42:15 +02:00
|
|
|
// don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result
|
|
|
|
|
_char = *_codePtr++;
|
|
|
|
|
++_currentColumnNumber;
|
2012-07-31 10:12:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2019-06-19 19:42:15 +02:00
|
|
|
// rewind by one char, so things gets scanned correctly
|
|
|
|
|
--_codePtr;
|
2020-04-29 15:06:32 +02:00
|
|
|
--_currentColumnNumber;
|
2019-06-19 19:42:15 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
_validTokenText = true;
|
2019-06-19 19:42:15 +02:00
|
|
|
_tokenText = QString(startCode, _codePtr - startCode);
|
|
|
|
|
|
|
|
|
|
auto setRawString = [&](const QChar *end) {
|
|
|
|
|
QString raw(startCode, end - startCode - 1);
|
|
|
|
|
raw.replace(QLatin1String("\r\n"), QLatin1String("\n"));
|
|
|
|
|
raw.replace(QLatin1Char('\r'), QLatin1Char('\n'));
|
|
|
|
|
_rawString = _engine->newStringRef(raw);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
scanChar();
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
while (_codePtr <= _endPtr) {
|
2019-06-19 19:42:15 +02:00
|
|
|
if (_char == quote) {
|
2018-10-16 15:32:58 +02:00
|
|
|
scanChar();
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2019-06-19 19:42:15 +02:00
|
|
|
if (_engine) {
|
2018-10-16 15:32:58 +02:00
|
|
|
_tokenSpell = _engine->newStringRef(_tokenText);
|
2019-06-19 19:42:15 +02:00
|
|
|
if (quote == QLatin1Char('`'))
|
|
|
|
|
setRawString(_codePtr - 1);
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (quote == QLatin1Char('`'))
|
|
|
|
|
_bracesCount = _outerTemplateBraceCount.pop();
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (mode == TemplateContinuation)
|
|
|
|
|
return T_TEMPLATE_TAIL;
|
|
|
|
|
else if (mode == TemplateHead)
|
|
|
|
|
return T_NO_SUBSTITUTION_TEMPLATE;
|
|
|
|
|
|
|
|
|
|
return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
|
|
|
|
|
} else if (quote == QLatin1Char('`') && _char == QLatin1Char('$') && *_codePtr == '{') {
|
|
|
|
|
scanChar();
|
|
|
|
|
scanChar();
|
|
|
|
|
_bracesCount = 1;
|
2019-06-19 19:42:15 +02:00
|
|
|
if (_engine) {
|
2018-10-16 15:32:58 +02:00
|
|
|
_tokenSpell = _engine->newStringRef(_tokenText);
|
2019-06-19 19:42:15 +02:00
|
|
|
setRawString(_codePtr - 2);
|
|
|
|
|
}
|
2018-10-16 15:32:58 +02:00
|
|
|
|
|
|
|
|
return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
|
|
|
|
|
} else if (_char == QLatin1Char('\\')) {
|
|
|
|
|
scanChar();
|
|
|
|
|
if (_codePtr > _endPtr) {
|
|
|
|
|
_errorCode = IllegalEscapeSequence;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "End of file reached at escape sequence");
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QChar u;
|
|
|
|
|
|
|
|
|
|
switch (_char.unicode()) {
|
|
|
|
|
// unicode escape sequence
|
|
|
|
|
case 'u': {
|
|
|
|
|
bool ok = false;
|
|
|
|
|
uint codePoint = decodeUnicodeEscapeCharacter(&ok);
|
|
|
|
|
if (!ok)
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
if (QChar::requiresSurrogates(codePoint)) {
|
|
|
|
|
// need to use a surrogate pair
|
|
|
|
|
_tokenText += QChar(QChar::highSurrogate(codePoint));
|
|
|
|
|
u = QChar::lowSurrogate(codePoint);
|
|
|
|
|
} else {
|
|
|
|
|
u = codePoint;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
// hex escape sequence
|
|
|
|
|
case 'x': {
|
|
|
|
|
bool ok = false;
|
|
|
|
|
u = decodeHexEscapeCharacter(&ok);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
_errorCode = IllegalHexadecimalEscapeSequence;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Illegal hexadecimal escape sequence");
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
|
|
// single character escape sequence
|
|
|
|
|
case '\\': u = QLatin1Char('\\'); scanChar(); break;
|
|
|
|
|
case '\'': u = QLatin1Char('\''); scanChar(); break;
|
|
|
|
|
case '\"': u = QLatin1Char('\"'); scanChar(); break;
|
|
|
|
|
case 'b': u = QLatin1Char('\b'); scanChar(); break;
|
|
|
|
|
case 'f': u = QLatin1Char('\f'); scanChar(); break;
|
|
|
|
|
case 'n': u = QLatin1Char('\n'); scanChar(); break;
|
|
|
|
|
case 'r': u = QLatin1Char('\r'); scanChar(); break;
|
|
|
|
|
case 't': u = QLatin1Char('\t'); scanChar(); break;
|
|
|
|
|
case 'v': u = QLatin1Char('\v'); scanChar(); break;
|
|
|
|
|
|
|
|
|
|
case '0':
|
|
|
|
|
if (! _codePtr->isDigit()) {
|
|
|
|
|
scanChar();
|
|
|
|
|
u = QLatin1Char('\0');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Q_FALLTHROUGH();
|
|
|
|
|
case '1':
|
|
|
|
|
case '2':
|
|
|
|
|
case '3':
|
|
|
|
|
case '4':
|
|
|
|
|
case '5':
|
|
|
|
|
case '6':
|
|
|
|
|
case '7':
|
|
|
|
|
case '8':
|
|
|
|
|
case '9':
|
|
|
|
|
_errorCode = IllegalEscapeSequence;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Octal escape sequences are not allowed");
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
|
|
|
|
|
case '\r':
|
|
|
|
|
case '\n':
|
|
|
|
|
case 0x2028u:
|
|
|
|
|
case 0x2029u:
|
|
|
|
|
scanChar();
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// non escape character
|
|
|
|
|
u = _char;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenText += u;
|
|
|
|
|
} else {
|
|
|
|
|
_tokenText += _char;
|
2012-07-31 10:12:26 +02:00
|
|
|
scanChar();
|
|
|
|
|
}
|
2018-10-16 15:32:58 +02:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
_errorCode = UnclosedStringLiteral;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unclosed string at end of line");
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Lexer::scanNumber(QChar ch)
|
|
|
|
|
{
|
|
|
|
|
if (ch == QLatin1Char('0')) {
|
|
|
|
|
if (_char == QLatin1Char('x') || _char == QLatin1Char('X')) {
|
|
|
|
|
ch = _char; // 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("QmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double d = 0.;
|
|
|
|
|
while (1) {
|
|
|
|
|
int digit = ::hexDigit(_char);
|
|
|
|
|
if (digit < 0)
|
|
|
|
|
break;
|
|
|
|
|
d *= 16;
|
|
|
|
|
d += digit;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenValue = d;
|
|
|
|
|
return T_NUMERIC_LITERAL;
|
|
|
|
|
} else if (_char == QLatin1Char('o') || _char == QLatin1Char('O')) {
|
|
|
|
|
ch = _char; // 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("QmlParser", "At least one octal digit is required after '0%1'").arg(ch);
|
|
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double d = 0.;
|
|
|
|
|
while (1) {
|
|
|
|
|
int digit = ::octalDigit(_char);
|
|
|
|
|
if (digit < 0)
|
|
|
|
|
break;
|
|
|
|
|
d *= 8;
|
|
|
|
|
d += digit;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenValue = d;
|
|
|
|
|
return T_NUMERIC_LITERAL;
|
|
|
|
|
} else if (_char == QLatin1Char('b') || _char == QLatin1Char('B')) {
|
|
|
|
|
ch = _char; // remember the b or B to use it in the error message below.
|
|
|
|
|
|
|
|
|
|
// parse binary integer literal
|
|
|
|
|
scanChar(); // consume 'b'
|
|
|
|
|
|
|
|
|
|
if (_char.unicode() != '0' && _char.unicode() != '1') {
|
|
|
|
|
_errorCode = IllegalNumber;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "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() == '1')
|
|
|
|
|
digit = 1;
|
|
|
|
|
else if (_char.unicode() != '0')
|
|
|
|
|
break;
|
|
|
|
|
d *= 2;
|
|
|
|
|
d += digit;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenValue = d;
|
|
|
|
|
return T_NUMERIC_LITERAL;
|
|
|
|
|
} else if (_char.isDigit() && !qmlMode()) {
|
|
|
|
|
_errorCode = IllegalCharacter;
|
|
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Decimal numbers can't start with '0'");
|
2013-11-06 14:17:23 +01:00
|
|
|
return T_ERROR;
|
|
|
|
|
}
|
2012-07-31 10:12:26 +02:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
// decimal integer literal
|
2018-10-16 15:32:58 +02:00
|
|
|
QVarLengthArray<char,32> chars;
|
|
|
|
|
chars.append(ch.unicode());
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (ch != QLatin1Char('.')) {
|
2012-07-31 10:12:26 +02:00
|
|
|
while (_char.isDigit()) {
|
|
|
|
|
chars.append(_char.unicode());
|
2018-10-16 15:32:58 +02:00
|
|
|
scanChar(); // consume the digit
|
2012-07-31 10:12:26 +02:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_char == QLatin1Char('.')) {
|
|
|
|
|
chars.append(_char.unicode());
|
|
|
|
|
scanChar(); // consume `.'
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-31 10:12:26 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
while (_char.isDigit()) {
|
|
|
|
|
chars.append(_char.unicode());
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
|
2012-07-31 10:12:26 +02:00
|
|
|
if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
|
|
|
|
|
_codePtr[1].isDigit())) {
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
chars.append(_char.unicode());
|
|
|
|
|
scanChar(); // consume `e'
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
|
|
|
|
|
chars.append(_char.unicode());
|
|
|
|
|
scanChar(); // consume the sign
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
while (_char.isDigit()) {
|
|
|
|
|
chars.append(_char.unicode());
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
chars.append('\0');
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
const char *begin = chars.constData();
|
2018-10-16 15:32:58 +02:00
|
|
|
const char *end = nullptr;
|
2012-07-31 10:12:26 +02:00
|
|
|
bool ok = false;
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
_tokenValue = qstrtod(begin, &end, &ok);
|
2011-09-13 08:42:52 +02:00
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
if (end - begin != chars.size() - 1) {
|
|
|
|
|
_errorCode = IllegalExponentIndicator;
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number");
|
2012-07-31 10:12:26 +02:00
|
|
|
return T_ERROR;
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
|
2012-07-31 10:12:26 +02:00
|
|
|
return T_NUMERIC_LITERAL;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-04 16:11:02 +02:00
|
|
|
int Lexer::scanVersionNumber(QChar ch)
|
|
|
|
|
{
|
|
|
|
|
if (ch == QLatin1Char('0')) {
|
|
|
|
|
_tokenValue = 0;
|
|
|
|
|
return T_VERSION_NUMBER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int acc = 0;
|
|
|
|
|
acc += ch.digitValue();
|
|
|
|
|
|
|
|
|
|
while (_char.isDigit()) {
|
|
|
|
|
acc *= 10;
|
|
|
|
|
acc += _char.digitValue();
|
|
|
|
|
scanChar(); // consume the digit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_tokenValue = acc;
|
|
|
|
|
return T_VERSION_NUMBER;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-18 13:13:34 +01:00
|
|
|
bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
|
|
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText.resize(0);
|
|
|
|
|
_validTokenText = true;
|
|
|
|
|
_patternFlags = 0;
|
2010-01-18 13:13:34 +01:00
|
|
|
|
|
|
|
|
if (prefix == EqualPrefix)
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += QLatin1Char('=');
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2010-10-19 16:08:44 +02:00
|
|
|
while (true) {
|
2011-09-13 08:42:52 +02:00
|
|
|
switch (_char.unicode()) {
|
2010-10-19 16:08:44 +02:00
|
|
|
case '/':
|
2011-09-13 08:42:52 +02:00
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
|
|
|
|
|
// scan the flags
|
2011-09-13 08:42:52 +02:00
|
|
|
_patternFlags = 0;
|
|
|
|
|
while (isIdentLetter(_char)) {
|
2011-09-19 14:09:26 +02:00
|
|
|
int flag = regExpFlagFromChar(_char);
|
2013-11-06 14:17:23 +01:00
|
|
|
if (flag == 0 || _patternFlags & flag) {
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Invalid regular expression flag '%0'")
|
2011-09-13 08:42:52 +02:00
|
|
|
.arg(QChar(_char));
|
2010-10-19 16:08:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
_patternFlags |= flag;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
|
|
|
|
|
_tokenLength = _codePtr - _tokenStartPtr - 1;
|
2010-10-19 16:08:44 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
|
// regular expression backslash sequence
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
if (_codePtr > _endPtr || isLineTerminator()) {
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence");
|
2010-10-19 16:08:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-01-18 13:13:34 +01:00
|
|
|
break;
|
|
|
|
|
|
2010-10-19 16:08:44 +02:00
|
|
|
case '[':
|
|
|
|
|
// regular expression class
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
while (_codePtr <= _endPtr && ! isLineTerminator()) {
|
2011-09-13 08:42:52 +02:00
|
|
|
if (_char == QLatin1Char(']'))
|
2010-10-19 16:08:44 +02:00
|
|
|
break;
|
2013-11-06 14:17:23 +01:00
|
|
|
else if (_char == QLatin1Char('\\')) {
|
2010-10-19 16:08:44 +02:00
|
|
|
// regular expression backslash sequence
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
|
2013-11-06 14:17:23 +01:00
|
|
|
if (_codePtr > _endPtr || isLineTerminator()) {
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence");
|
2010-10-19 16:08:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
} else {
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
2010-10-19 16:08:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
if (_char != QLatin1Char(']')) {
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression class");
|
2010-10-19 16:08:44 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar(); // skip ]
|
2010-10-19 16:08:44 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2013-11-06 14:17:23 +01:00
|
|
|
if (_codePtr > _endPtr || isLineTerminator()) {
|
2018-04-05 11:13:53 +03:00
|
|
|
_errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression literal");
|
2013-01-22 11:15:23 +01:00
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
_tokenText += _char;
|
|
|
|
|
scanChar();
|
|
|
|
|
}
|
2010-10-19 16:08:44 +02:00
|
|
|
} // switch
|
|
|
|
|
} // while
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2010-10-19 16:08:44 +02:00
|
|
|
return false;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
bool Lexer::isLineTerminator() const
|
|
|
|
|
{
|
2013-01-22 11:15:23 +01:00
|
|
|
const ushort unicode = _char.unicode();
|
|
|
|
|
return unicode == 0x000Au
|
|
|
|
|
|| unicode == 0x000Du
|
|
|
|
|
|| unicode == 0x2028u
|
|
|
|
|
|| unicode == 0x2029u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned Lexer::isLineTerminatorSequence() const
|
|
|
|
|
{
|
|
|
|
|
switch (_char.unicode()) {
|
|
|
|
|
case 0x000Au:
|
|
|
|
|
case 0x2028u:
|
|
|
|
|
case 0x2029u:
|
|
|
|
|
return 1;
|
|
|
|
|
case 0x000Du:
|
|
|
|
|
if (_codePtr->unicode() == 0x000Au)
|
|
|
|
|
return 2;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-09-13 08:42:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::isIdentLetter(QChar ch)
|
|
|
|
|
{
|
|
|
|
|
// ASCII-biased, since all reserved words are ASCII, aand hence the
|
|
|
|
|
// bulk of content to be parsed.
|
|
|
|
|
if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z'))
|
|
|
|
|
|| (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))
|
|
|
|
|
|| ch == QLatin1Char('$')
|
|
|
|
|
|| ch == QLatin1Char('_'))
|
|
|
|
|
return true;
|
|
|
|
|
if (ch.unicode() < 128)
|
|
|
|
|
return false;
|
|
|
|
|
return ch.isLetterOrNumber();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::isDecimalDigit(ushort c)
|
|
|
|
|
{
|
|
|
|
|
return (c >= '0' && c <= '9');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::isHexDigit(QChar c)
|
|
|
|
|
{
|
|
|
|
|
return ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
|
|
|
|
|
|| (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
|
|
|
|
|
|| (c >= QLatin1Char('A') && c <= QLatin1Char('F')));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::isOctalDigit(ushort c)
|
|
|
|
|
{
|
|
|
|
|
return (c >= '0' && c <= '7');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Lexer::tokenText() const
|
|
|
|
|
{
|
|
|
|
|
if (_validTokenText)
|
|
|
|
|
return _tokenText;
|
|
|
|
|
|
|
|
|
|
if (_tokenKind == T_STRING_LITERAL)
|
|
|
|
|
return QString(_tokenStartPtr + 1, _tokenLength - 2);
|
|
|
|
|
|
|
|
|
|
return QString(_tokenStartPtr, _tokenLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Lexer::Error Lexer::errorCode() const
|
|
|
|
|
{
|
|
|
|
|
return _errorCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Lexer::errorMessage() const
|
|
|
|
|
{
|
|
|
|
|
return _errorMessage;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-18 13:13:34 +01:00
|
|
|
void Lexer::syncProhibitAutomaticSemicolon()
|
|
|
|
|
{
|
2011-09-13 08:42:52 +02:00
|
|
|
if (_parenthesesState == BalancedParentheses) {
|
2010-01-18 13:13:34 +01:00
|
|
|
// 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)
|
2011-09-13 08:42:52 +02:00
|
|
|
_prohibitAutomaticSemicolon = true;
|
|
|
|
|
_parenthesesState = IgnoreParentheses;
|
2010-01-18 13:13:34 +01:00
|
|
|
} else {
|
2011-09-13 08:42:52 +02:00
|
|
|
_prohibitAutomaticSemicolon = false;
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-13 08:42:52 +02:00
|
|
|
bool Lexer::prevTerminator() const
|
|
|
|
|
{
|
|
|
|
|
return _terminator;
|
|
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-19 14:09:26 +02:00
|
|
|
bool Lexer::followsClosingBrace() const
|
|
|
|
|
{
|
|
|
|
|
return _followsClosingBrace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::canInsertAutomaticSemicolon(int token) const
|
|
|
|
|
{
|
|
|
|
|
return token == T_RBRACE
|
|
|
|
|
|| token == EOF_SYMBOL
|
|
|
|
|
|| _terminator
|
|
|
|
|
|| _followsClosingBrace;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
static const int uriTokens[] = {
|
|
|
|
|
QmlJSGrammar::T_IDENTIFIER,
|
|
|
|
|
QmlJSGrammar::T_PROPERTY,
|
|
|
|
|
QmlJSGrammar::T_SIGNAL,
|
|
|
|
|
QmlJSGrammar::T_READONLY,
|
|
|
|
|
QmlJSGrammar::T_ON,
|
|
|
|
|
QmlJSGrammar::T_BREAK,
|
|
|
|
|
QmlJSGrammar::T_CASE,
|
|
|
|
|
QmlJSGrammar::T_CATCH,
|
|
|
|
|
QmlJSGrammar::T_CONTINUE,
|
|
|
|
|
QmlJSGrammar::T_DEFAULT,
|
|
|
|
|
QmlJSGrammar::T_DELETE,
|
|
|
|
|
QmlJSGrammar::T_DO,
|
|
|
|
|
QmlJSGrammar::T_ELSE,
|
|
|
|
|
QmlJSGrammar::T_FALSE,
|
|
|
|
|
QmlJSGrammar::T_FINALLY,
|
|
|
|
|
QmlJSGrammar::T_FOR,
|
|
|
|
|
QmlJSGrammar::T_FUNCTION,
|
2019-10-04 16:11:02 +02:00
|
|
|
QmlJSGrammar::T_FUNCTION_STAR,
|
2016-04-29 11:00:30 +02:00
|
|
|
QmlJSGrammar::T_IF,
|
|
|
|
|
QmlJSGrammar::T_IN,
|
2018-10-16 15:32:58 +02:00
|
|
|
QmlJSGrammar::T_OF,
|
2016-04-29 11:00:30 +02:00
|
|
|
QmlJSGrammar::T_INSTANCEOF,
|
|
|
|
|
QmlJSGrammar::T_NEW,
|
|
|
|
|
QmlJSGrammar::T_NULL,
|
|
|
|
|
QmlJSGrammar::T_RETURN,
|
|
|
|
|
QmlJSGrammar::T_SWITCH,
|
|
|
|
|
QmlJSGrammar::T_THIS,
|
|
|
|
|
QmlJSGrammar::T_THROW,
|
|
|
|
|
QmlJSGrammar::T_TRUE,
|
|
|
|
|
QmlJSGrammar::T_TRY,
|
|
|
|
|
QmlJSGrammar::T_TYPEOF,
|
|
|
|
|
QmlJSGrammar::T_VAR,
|
|
|
|
|
QmlJSGrammar::T_VOID,
|
|
|
|
|
QmlJSGrammar::T_WHILE,
|
|
|
|
|
QmlJSGrammar::T_CONST,
|
|
|
|
|
QmlJSGrammar::T_DEBUGGER,
|
|
|
|
|
QmlJSGrammar::T_RESERVED_WORD,
|
|
|
|
|
QmlJSGrammar::T_WITH,
|
|
|
|
|
|
|
|
|
|
QmlJSGrammar::EOF_SYMBOL
|
|
|
|
|
};
|
|
|
|
|
static inline bool isUriToken(int token)
|
2011-09-19 14:09:26 +02:00
|
|
|
{
|
2016-04-29 11:00:30 +02:00
|
|
|
const int *current = uriTokens;
|
|
|
|
|
while (*current != QmlJSGrammar::EOF_SYMBOL) {
|
|
|
|
|
if (*current == token)
|
|
|
|
|
return true;
|
|
|
|
|
++current;
|
2011-09-19 14:09:26 +02:00
|
|
|
}
|
2016-04-29 11:00:30 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
|
|
|
|
|
{
|
2019-10-04 16:11:02 +02:00
|
|
|
auto setError = [error, this](QString message) {
|
|
|
|
|
error->message = std::move(message);
|
|
|
|
|
error->loc.startLine = tokenStartLine();
|
|
|
|
|
error->loc.startColumn = tokenStartColumn();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QScopedValueRollback<bool> directivesGuard(_handlingDirectives, true);
|
2016-04-29 11:00:30 +02:00
|
|
|
Q_ASSERT(!_qmlMode);
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
lex(); // fetch the first token
|
|
|
|
|
|
|
|
|
|
if (_tokenKind != T_DOT)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
const int lineNumber = tokenStartLine();
|
2016-04-29 11:00:30 +02:00
|
|
|
const int column = tokenStartColumn();
|
|
|
|
|
|
|
|
|
|
lex(); // skip T_DOT
|
2011-09-19 14:09:26 +02:00
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_IMPORT))
|
2016-04-29 11:00:30 +02:00
|
|
|
return true; // expected a valid QML/JS directive
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
const QString directiveName = tokenText();
|
|
|
|
|
|
|
|
|
|
if (! (directiveName == QLatin1String("pragma") ||
|
2016-04-29 11:00:30 +02:00
|
|
|
directiveName == QLatin1String("import"))) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "Syntax error"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // not a valid directive name
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
// it must be a pragma or an import directive.
|
|
|
|
|
if (directiveName == QLatin1String("pragma")) {
|
|
|
|
|
// .pragma library
|
2016-04-29 11:00:30 +02:00
|
|
|
if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "Syntax error"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // expected `library
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
// we found a .pragma library directive
|
2018-10-16 15:32:58 +02:00
|
|
|
directives->pragmaLibrary();
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
Q_ASSERT(directiveName == QLatin1String("import"));
|
|
|
|
|
lex(); // skip .import
|
|
|
|
|
|
|
|
|
|
QString pathOrUri;
|
|
|
|
|
QString version;
|
|
|
|
|
bool fileImport = false; // file or uri import
|
|
|
|
|
|
|
|
|
|
if (_tokenKind == T_STRING_LITERAL) {
|
|
|
|
|
// .import T_STRING_LITERAL as T_IDENTIFIER
|
|
|
|
|
|
|
|
|
|
fileImport = true;
|
|
|
|
|
pathOrUri = tokenText();
|
|
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
if (!pathOrUri.endsWith(QLatin1String("js"))) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser","Imported file must be a script"));
|
2016-04-29 11:00:30 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-19 14:09:26 +02:00
|
|
|
} else if (_tokenKind == T_IDENTIFIER) {
|
2019-10-04 16:11:02 +02:00
|
|
|
// .import T_IDENTIFIER (. T_IDENTIFIER)* T_VERSION_NUMBER . T_VERSION_NUMBER as T_IDENTIFIER
|
2016-04-29 11:00:30 +02:00
|
|
|
while (true) {
|
|
|
|
|
if (!isUriToken(_tokenKind)) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser","Invalid module URI"));
|
2016-04-29 11:00:30 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
pathOrUri.append(tokenText());
|
|
|
|
|
|
|
|
|
|
lex();
|
|
|
|
|
if (tokenStartLine() != lineNumber) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser","Invalid module URI"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false;
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
|
|
|
|
if (_tokenKind != QmlJSGrammar::T_DOT)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
pathOrUri.append(QLatin1Char('.'));
|
2011-09-19 14:09:26 +02:00
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
lex();
|
|
|
|
|
if (tokenStartLine() != lineNumber) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser","Invalid module URI"));
|
2016-04-29 11:00:30 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-04 16:11:02 +02:00
|
|
|
if (_tokenKind != T_VERSION_NUMBER) {
|
|
|
|
|
setError(QCoreApplication::translate("QmlParser","Module import requires a version"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // expected the module version number
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
version = tokenText();
|
2019-10-04 16:11:02 +02:00
|
|
|
lex();
|
|
|
|
|
if (_tokenKind != T_DOT) {
|
|
|
|
|
setError(QCoreApplication::translate( "QmlParser", "Module import requires a minor version (missing dot)"));
|
|
|
|
|
return false; // expected the module version number
|
|
|
|
|
}
|
|
|
|
|
version += QLatin1Char('.');
|
|
|
|
|
|
|
|
|
|
lex();
|
|
|
|
|
if (_tokenKind != T_VERSION_NUMBER) {
|
|
|
|
|
setError(QCoreApplication::translate( "QmlParser", "Module import requires a minor version (missing number)"));
|
|
|
|
|
return false; // expected the module version number
|
|
|
|
|
}
|
|
|
|
|
version += tokenText();
|
2011-09-19 14:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// recognize the mandatory `as' followed by the module name
|
|
|
|
|
//
|
2018-10-16 15:32:58 +02:00
|
|
|
if (! (lex() == T_AS && tokenStartLine() == lineNumber)) {
|
2016-04-29 11:00:30 +02:00
|
|
|
if (fileImport)
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "File import requires a qualifier"));
|
2016-04-29 11:00:30 +02:00
|
|
|
else
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "Module import requires a qualifier"));
|
2016-04-29 11:00:30 +02:00
|
|
|
if (tokenStartLine() != lineNumber) {
|
|
|
|
|
error->loc.startLine = lineNumber;
|
|
|
|
|
error->loc.startColumn = column;
|
|
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // expected `as'
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
if (lex() != T_IDENTIFIER || tokenStartLine() != lineNumber) {
|
|
|
|
|
if (fileImport)
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "File import requires a qualifier"));
|
2016-04-29 11:00:30 +02:00
|
|
|
else
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "Module import requires a qualifier"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // expected module name
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
const QString module = tokenText();
|
2016-04-29 11:00:30 +02:00
|
|
|
if (!module.at(0).isUpper()) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser","Invalid import qualifier"));
|
2016-04-29 11:00:30 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
if (fileImport)
|
2016-04-29 11:00:30 +02:00
|
|
|
directives->importFile(pathOrUri, module, lineNumber, column);
|
2011-09-19 14:09:26 +02:00
|
|
|
else
|
2016-04-29 11:00:30 +02:00
|
|
|
directives->importModule(pathOrUri, version, module, lineNumber, column);
|
2011-09-19 14:09:26 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-29 11:00:30 +02:00
|
|
|
if (tokenStartLine() != lineNumber) {
|
2019-10-04 16:11:02 +02:00
|
|
|
setError(QCoreApplication::translate("QmlParser", "Syntax error"));
|
2011-09-19 14:09:26 +02:00
|
|
|
return false; // the directives cannot span over multiple lines
|
2016-04-29 11:00:30 +02:00
|
|
|
}
|
2011-09-19 14:09:26 +02:00
|
|
|
|
|
|
|
|
// fetch the first token after the .pragma/.import directive
|
|
|
|
|
lex();
|
|
|
|
|
} while (_tokenKind == T_DOT);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-03-04 06:45:26 +01:00
|
|
|
|
|
|
|
|
QT_QML_END_NAMESPACE
|