/****************************************************************************
**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
**
**
**
+**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qdeclarativejslexer_p.h"
#include "qdeclarativejsengine_p.h"
-#include "qdeclarativejsnodepool_p.h"
-#include <private/qdeclarativeutils_p.h>
+#include "qdeclarativejsmemorypool_p.h"
+
#include <QtCore/QCoreApplication>
#include <QtCore/QVarLengthArray>
#include <QtCore/QDebug>
using namespace QDeclarativeJS;
-enum RegExpFlag {
- Global = 0x01,
- IgnoreCase = 0x02,
- Multiline = 0x04
-};
-
-static int flagFromChar(const QChar &ch)
+static int regExpFlagFromChar(const QChar &ch)
{
switch (ch.unicode()) {
- case 'g': return Global;
- case 'i': return IgnoreCase;
- case 'm': return Multiline;
+ case 'g': return Lexer::RegExp_Global;
+ case 'i': return Lexer::RegExp_IgnoreCase;
+ case 'm': return Lexer::RegExp_Multiline;
}
return 0;
}
return (c - 'A' + 10);
}
-static unsigned char convertHex(QChar c1, QChar c2)
+static QChar convertHex(QChar c1, QChar c2)
{
- return ((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
+ return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
}
static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4)
, _parenthesesCount(0)
, _stackToken(-1)
, _patternFlags(0)
+ , _tokenKind(0)
, _tokenLength(0)
, _tokenLine(0)
, _validTokenText(false)
, _prohibitAutomaticSemicolon(false)
, _restrictedKeyword(false)
, _terminator(false)
- , _delimited(false)
+ , _followsClosingBrace(false)
+ , _delimited(true)
+ , _qmlMode(true)
{
if (engine)
engine->setLexer(this);
}
+bool Lexer::qmlMode() const
+{
+ return _qmlMode;
+}
+
QString Lexer::code() const
{
return _code;
}
-void Lexer::setCode(const QString &code, int lineno)
+void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
{
if (_engine)
_engine->setCode(code);
+ _qmlMode = qmlMode;
_code = code;
_tokenText.clear();
_tokenText.reserve(1024);
_prohibitAutomaticSemicolon = false;
_restrictedKeyword = false;
_terminator = false;
- _delimited = false;
+ _followsClosingBrace = false;
+ _delimited = true;
}
void Lexer::scanChar()
int Lexer::lex()
{
+ const int previousTokenKind = _tokenKind;
+
_tokenSpell = QStringRef();
- int token = scanToken();
+ _tokenKind = scanToken();
_tokenLength = _codePtr - _tokenStartPtr - 1;
_delimited = false;
_restrictedKeyword = false;
+ _followsClosingBrace = (previousTokenKind == T_RBRACE);
// update the flags
- switch (token) {
+ switch (_tokenKind) {
case T_LBRACE:
case T_SEMICOLON:
+ case T_COLON:
_delimited = true;
break;
break;
case CountParentheses:
- if (token == T_RPAREN) {
+ if (_tokenKind == T_RPAREN) {
--_parenthesesCount;
if (_parenthesesCount == 0)
_parenthesesState = BalancedParentheses;
- } else if (token == T_LPAREN) {
+ } else if (_tokenKind == T_LPAREN) {
++_parenthesesCount;
}
break;
break;
} // switch
- return token;
+ return _tokenKind;
}
bool Lexer::isUnicodeEscapeSequence(const QChar *chars)
_validTokenText = false;
_tokenLinePtr = _lastLinePtr;
- while (QDeclarativeUtils::isSpace(_char)) {
+ while (_char.isSpace()) {
if (_char == QLatin1Char('\n')) {
_tokenLinePtr = _codePtr;
scanChar();
if (_char == QLatin1Char('/')) {
scanChar();
+
+ if (_engine) {
+ _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
+ tokenStartLine(), tokenStartColumn() + 2);
+ }
+
goto again;
}
} else {
while (!_char.isNull() && _char != QLatin1Char('\n')) {
scanChar();
}
+ if (_engine) {
+ _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
+ tokenStartLine(), tokenStartColumn() + 2);
+ }
goto again;
} if (_char == QLatin1Char('=')) {
scanChar();
return T_DIVIDE_;
case '.':
- if (QDeclarativeUtils::isDigit(_char)) {
+ if (_char.isDigit()) {
QVarLengthArray<char,32> chars;
chars.append(ch.unicode()); // append the `.'
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar();
}
if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (QDeclarativeUtils::isDigit(_codePtr[0]) || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- QDeclarativeUtils::isDigit(_codePtr[1]))) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
chars.append(_char.unicode());
scanChar(); // consume `e'
scanChar(); // consume the sign
}
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar();
}
scanChar();
if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
- _stackToken = T_PLUS_PLUS;
+ _stackToken = T_MINUS_MINUS;
return T_SEMICOLON;
}
case '\'':
case '"': {
const QChar quote = ch;
- _tokenText.resize(0);
- _validTokenText = true;
-
bool multilineStringLiteral = false;
+ const QChar *startCode = _codePtr;
+
+ if (_engine) {
+ while (!_char.isNull()) {
+ if (_char == QLatin1Char('\n') || _char == QLatin1Char('\\')) {
+ break;
+ } else if (_char == quote) {
+ _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
+ scanChar();
+
+ return T_STRING_LITERAL;
+ }
+ scanChar();
+ }
+ }
+
+ _validTokenText = true;
+ _tokenText.resize(0);
+ startCode--;
+ while (startCode != _codePtr - 1)
+ _tokenText += *startCode++;
+
while (! _char.isNull()) {
if (_char == QLatin1Char('\n')) {
multilineStringLiteral = true;
break;
// single character escape sequence
- case '\\': u = QLatin1Char('\''); scanChar(); break;
+ case '\\': u = QLatin1Char('\\'); scanChar(); break;
case '\'': u = QLatin1Char('\''); scanChar(); break;
case '\"': u = QLatin1Char('\"'); scanChar(); break;
case 'b': u = QLatin1Char('\b'); scanChar(); break;
while (_char == QLatin1Char('\r'))
scanChar();
- if (_char == '\n') {
+ if (_char == QLatin1Char('\n')) {
u = _char;
scanChar();
} else {
}
default:
- if (QDeclarativeUtils::isLetter(ch) || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) {
+ if (ch.isLetter() || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) {
bool identifierWithEscapeChars = false;
if (ch == QLatin1Char('\\')) {
identifierWithEscapeChars = true;
}
}
while (true) {
- if (QDeclarativeUtils::isLetterOrNumber(_char) || _char == QLatin1Char('$') || _char == QLatin1Char('_')) {
+ if (_char.isLetterOrNumber() || _char == QLatin1Char('$') || _char == QLatin1Char('_')) {
if (identifierWithEscapeChars)
_tokenText += _char;
int kind = T_IDENTIFIER;
if (! identifierWithEscapeChars)
- kind = classify(_tokenStartPtr, _tokenLength);
+ kind = classify(_tokenStartPtr, _tokenLength, _qmlMode);
if (_engine) {
if (kind == T_IDENTIFIER && identifierWithEscapeChars)
return kind;
}
}
- } else if (QDeclarativeUtils::isDigit(ch)) {
+ } else if (ch.isDigit()) {
+ if (ch != QLatin1Char('0')) {
+ double integer = ch.unicode() - '0';
+
+ QChar n = _char;
+ const QChar *code = _codePtr;
+ while (n.isDigit()) {
+ integer = integer * 10 + (n.unicode() - '0');
+ n = *code++;
+ }
+
+ if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) {
+ if (code != _codePtr) {
+ _codePtr = code - 1;
+ scanChar();
+ }
+ _tokenValue = integer;
+ return T_NUMERIC_LITERAL;
+ }
+ }
+
QVarLengthArray<char,32> chars;
chars.append(ch.unicode());
- if (ch == QLatin1Char('0') && (_char == 'x' || _char == 'X')) {
+ if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) {
// parse hex integer literal
chars.append(_char.unicode());
}
// decimal integer literal
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar(); // consume the digit
}
chars.append(_char.unicode());
scanChar(); // consume `.'
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar();
}
if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (QDeclarativeUtils::isDigit(_codePtr[0]) || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- QDeclarativeUtils::isDigit(_codePtr[1]))) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
chars.append(_char.unicode());
scanChar(); // consume `e'
scanChar(); // consume the sign
}
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar();
}
}
}
} else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (QDeclarativeUtils::isDigit(_codePtr[0]) || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- QDeclarativeUtils::isDigit(_codePtr[1]))) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
chars.append(_char.unicode());
scanChar(); // consume `e'
scanChar(); // consume the sign
}
- while (QDeclarativeUtils::isDigit(_char)) {
+ while (_char.isDigit()) {
chars.append(_char.unicode());
scanChar();
}
// scan the flags
_patternFlags = 0;
while (isIdentLetter(_char)) {
- int flag = flagFromChar(_char);
+ int flag = regExpFlagFromChar(_char);
if (flag == 0) {
_errorMessage = QCoreApplication::translate("QDeclarativeParser", "Invalid regular expression flag '%0'")
.arg(QChar(_char));
_patternFlags |= flag;
scanChar();
}
+
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
return true;
case '\\':
return (c >= '0' && c <= '7');
}
+int Lexer::tokenKind() const
+{
+ return _tokenKind;
+}
+
int Lexer::tokenOffset() const
{
return _tokenStartPtr - _code.unicode();
if (_validTokenText)
return _tokenText;
+ if (_tokenKind == T_STRING_LITERAL)
+ return QString(_tokenStartPtr + 1, _tokenLength - 2);
+
return QString(_tokenStartPtr, _tokenLength);
}
return _terminator;
}
+bool Lexer::followsClosingBrace() const
+{
+ return _followsClosingBrace;
+}
+
+bool Lexer::canInsertAutomaticSemicolon(int token) const
+{
+ return token == T_RBRACE
+ || token == EOF_SYMBOL
+ || _terminator
+ || _followsClosingBrace;
+}
+
+bool Lexer::scanDirectives(Directives *directives)
+{
+ if (_qmlMode) {
+ // the directives are a Javascript-only extension.
+ return false;
+ }
+
+ lex(); // fetch the first token
+
+ if (_tokenKind != T_DOT)
+ return true;
+
+ do {
+ lex(); // skip T_DOT
+
+ const int lineNumber = tokenStartLine();
+
+ if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
+ return false; // expected a valid QML/JS directive
+
+ const QString directiveName = tokenText();
+
+ if (! (directiveName == QLatin1String("pragma") ||
+ directiveName == QLatin1String("import")))
+ 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")))
+ return false; // expected `library
+
+ // we found a .pragma library directive
+ directives->pragmaLibrary();
+
+ } 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();
+
+ } else if (_tokenKind == T_IDENTIFIER) {
+ // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER
+
+ pathOrUri = tokenText();
+
+ lex(); // skip the first T_IDENTIFIER
+ for (; _tokenKind == T_DOT; lex()) {
+ if (lex() != T_IDENTIFIER)
+ return false;
+
+ pathOrUri += QLatin1Char('.');
+ pathOrUri += tokenText();
+ }
+
+ if (_tokenKind != T_NUMERIC_LITERAL)
+ return false; // expected the module version number
+
+ version = tokenText();
+ }
+
+ //
+ // recognize the mandatory `as' followed by the module name
+ //
+ if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as")))
+ return false; // expected `as'
+
+ if (lex() != T_IDENTIFIER)
+ return false; // expected module name
+
+ const QString module = tokenText();
+
+ if (fileImport)
+ directives->importFile(pathOrUri, module);
+ else
+ directives->importModule(pathOrUri, version, module);
+ }
+
+ if (tokenStartLine() != lineNumber)
+ return false; // the directives cannot span over multiple lines
+
+ // fetch the first token after the .pragma/.import directive
+ lex();
+ } while (_tokenKind == T_DOT);
+
+ return true;
+}
+
#include "qdeclarativejskeywords_p.h"